From bb203f405ce87fd8d0e32979baab9bf2f752afac Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Sat, 26 Sep 2026 21:30:54 -0400 Subject: [PATCH] fix(auth): login Google retombait sur le callback import (userId null) -> route par purpose vers completeGoogleLogin --- server/index.mjs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/server/index.mjs b/server/index.mjs index f46a55b..9012c70 100644 --- a/server/index.mjs +++ b/server/index.mjs @@ -516,6 +516,11 @@ r.get('/oauth/:provider/callback', oauthLimiter, async (req, res) => { if (!code || !state) return fail('oauth_missing_code_or_state'); const entry = consumeOAuthState(String(state)); if (!entry || entry.provider !== provider) return fail('oauth_invalid_state'); + // Le login Google revient ici : buildAuthUrl utilise toujours la redirect URI + // /api/oauth/google/callback (une seule URI à enregistrer côté Google). + if (provider === 'google' && entry.purpose === 'login') { + return completeGoogleLogin(req, res, frontBaseForOAuth(req), entry, String(code)); + } const tokens = await exchangeCode(provider, String(code), req); if (!tokens?.accessToken) return fail('oauth_token_failed'); let profile = { id: '', displayName: provider, avatarUrl: '' }; @@ -2101,15 +2106,15 @@ r.get('/auth/google/url', loginLimiter, (req, res) => { } }); -r.get('/auth/google/callback', loginLimiter, async (req, res) => { - const frontBase = frontBaseForOAuth(req); +/** + * Finalise un login Google à partir d'un state `login` déjà consommé : + * échange code → profil → find-or-create → session + cookies → front. + * Partagé par /api/auth/google/callback ET /api/oauth/google/callback + * (buildAuthUrl n'enregistre qu'une seule redirect URI côté Google). + */ +async function completeGoogleLogin(req, res, frontBase, entry, code) { const fail = (code) => res.redirect(302, `${frontBase}/auth/login?error=${encodeURIComponent(code)}`); try { - if (req.query?.error) return fail(String(req.query.error_description || req.query.error)); - const { code, state } = req.query || {}; - if (!code || !state) return fail('google_missing_code_or_state'); - const entry = consumeOAuthState(String(state)); - if (!entry || entry.provider !== 'google' || entry.purpose !== 'login') return fail('google_invalid_state'); const tokens = await exchangeCode('google', String(code), req); if (!tokens?.accessToken) return fail('google_token_failed'); const profile = await fetchGoogleProfile(tokens.accessToken); @@ -2156,6 +2161,21 @@ r.get('/auth/google/callback', loginLimiter, async (req, res) => { } catch { return fail('google_login_failed'); } +} + +r.get('/auth/google/callback', loginLimiter, async (req, res) => { + const frontBase = frontBaseForOAuth(req); + const fail = (code) => res.redirect(302, `${frontBase}/auth/login?error=${encodeURIComponent(code)}`); + try { + if (req.query?.error) return fail(String(req.query.error_description || req.query.error)); + const { code, state } = req.query || {}; + if (!code || !state) return fail('google_missing_code_or_state'); + const entry = consumeOAuthState(String(state)); + if (!entry || entry.provider !== 'google' || entry.purpose !== 'login') return fail('google_invalid_state'); + return await completeGoogleLogin(req, res, frontBase, entry, String(code)); + } catch { + return fail('google_login_failed'); + } }); r.post('/auth/refresh', async (req, res) => {