diff --git a/controllers/auth.controller.js b/controllers/auth.controller.js index e71738b..7c1a9b3 100644 --- a/controllers/auth.controller.js +++ b/controllers/auth.controller.js @@ -489,6 +489,17 @@ exports.googleCallback = async (req, res) => { await user.update({ email: `deleted_${user.user_id}@deleted.invalid` }); user = null; } + + // A system (manual) registration signing in with Google for the first time + // gets folded into that same account rather than blocked or duplicated. + // From this point on the account is Google-only — mirrors the existing + // rule that blocks Google accounts from manual login/password reset. + let justLinkedGoogle = false; + if (user && user.reg_type === 'system') { + justLinkedGoogle = true; + await user.update({ reg_type: 'google' }); + } + if (!user) { const t = await sequelize.transaction(); try { @@ -558,8 +569,13 @@ exports.googleCallback = async (req, res) => { logActivity(user.user_id, 'login', { entityType: 'session', entityId: Number(session.session_id) }); - // No cookie needed here — the refresh cookie set above is itself the - // signal. The frontend just calls restoreSession() and it succeeds. + // Normally no cookie is needed here — the refresh cookie set above is + // itself the signal, and the frontend just calls restoreSession(). The + // one exception is the just-linked flag, which restoreSession() has no + // way to surface on its own. + if (justLinkedGoogle) { + setGoogleResultCookie(res, { justLinkedGoogle: true }); + } return res.redirect(CALLBACK_PAGE); } @@ -569,7 +585,7 @@ exports.googleCallback = async (req, res) => { sendEmail({ to: user.email, type: 'LOGIN_OTP', data: { otp } }) .catch(err => console.error('[AUTH] googleCallback: Failed to send login OTP email:', err)); - setGoogleResultCookie(res, { otpRequired: true, email: user.email }); + setGoogleResultCookie(res, { otpRequired: true, email: user.email, justLinkedGoogle }); return res.redirect(CALLBACK_PAGE); } catch (err) { console.error('[AUTH] googleCallback OIDC error:', err);