fix(auth): persist provider connection state for Google OAuth#508
fix(auth): persist provider connection state for Google OAuth#508VIDYANKSHINI wants to merge 4 commits into
Conversation
|
@VIDYANKSHINI is attempting to deploy a commit to the Prashantkumar Khatri's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR adds persistence of Google OAuth access tokens during the Google callback flow, and introduces a Vitest to verify token persistence on successful login.
Changes:
- Persist encrypted Google
access_token(and scopes) tooAuthTokenvia Prisma upsert during/auth/google/callback. - Add a backend test covering successful login +
oAuthToken.upsertinvocation.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| apps/backend/src/routes/auth.ts | Encrypts and upserts Google OAuth access token + scopes, with non-blocking error handling. |
| apps/backend/src/tests/auth.test.ts | Adds Vitest coverage for Google callback and token persistence behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try { | ||
| const encryptedToken = encrypt(tokenData.access_token); | ||
| await app.prisma.oAuthToken.upsert({ | ||
| where: { userId_platform: { userId: user.id, platform: 'google' } }, | ||
| update: { accessToken: encryptedToken, scopes: tokenData.scope || 'openid email profile' }, | ||
| create: { userId: user.id, platform: 'google', accessToken: encryptedToken, scopes: tokenData.scope || 'openid email profile' }, | ||
| }); | ||
| } catch (err) { | ||
| app.log.error({ err, userId: user.id }, 'Failed to persist Google OAuth token — authentication proceeds'); | ||
| } |
| } catch (err) { | ||
| app.log.error({ err, userId: user.id }, 'Failed to persist Google OAuth token — authentication proceeds'); | ||
| } |
| process.env.NODE_ENV = 'test'; | ||
| process.env.PUBLIC_APP_URL = 'http://localhost:3000'; | ||
| process.env.BACKEND_URL = 'http://localhost:3001'; | ||
| process.env.MOBILE_REDIRECT_URI = 'devcard://auth'; | ||
| process.env.GOOGLE_CLIENT_ID = 'test-google-id'; | ||
| process.env.GOOGLE_CLIENT_SECRET = 'test-google-secret'; | ||
| process.env.ENCRYPTION_KEY = '12345678901234567890123456789012'; |
| }, | ||
| }; | ||
|
|
||
| global.fetch = vi.fn(); |
CI — All Checks PassedBackend — PASS
Mobile — SKIP
Web — SKIP
Last updated: |
|
Hi team / @Harxhit, I have addressed the recent PR review feedback and fixed the linting issues. Could you please let me know if any further changes are required from my end? If everything looks good, I would appreciate it if someone could review and merge this PR. |
Please fix the lint issues. |
| const app = await buildApp(); | ||
|
|
||
| const res = await app.inject({ | ||
| method: 'GET', | ||
| url: '/auth/google/callback?code=fake-code&state=fake-state', | ||
| cookies: { | ||
| oauth_state: 'fake-state', | ||
| }, | ||
| }); |
| process.env.GOOGLE_CLIENT_ID = 'test-google-id'; | ||
| process.env.GOOGLE_CLIENT_SECRET = 'test-google-secret'; | ||
| process.env.ENCRYPTION_KEY = '12345678901234567890123456789012'; | ||
|
|
| it('allows authentication to succeed even if token persistence fails', async () => { | ||
| const mockUser = { id: 'user-123', username: 'testuser' }; | ||
| mockPrisma.user.upsert.mockResolvedValue(mockUser); | ||
|
|
| // Should still succeed and redirect to dashboard | ||
| expect(res.statusCode).toBe(302); | ||
| expect(res.headers.location).toBe('http://localhost:3000/dashboard'); | ||
|
|
| const scopes = tokenData.scope || 'openid email profile'; | ||
| await app.prisma.oAuthToken.upsert({ | ||
| where: { userId_platform: { userId: user.id, platform: 'google' } }, | ||
| update: { accessToken: encryptedToken, scopes }, | ||
| create: { userId: user.id, platform: 'google', accessToken: encryptedToken, scopes }, | ||
| }); |
"I have successfully fixed all the linting issues and the CI is completely green now. Ready for review/merge!" |
Allow me some time please. |
Summary
This PR fixes an issue where the Google OAuth login flow authenticated the user but failed to persist the provider connection metadata (
oAuthToken). By ensuring that Google's access token is explicitly upserted into theoAuthTokentable upon successful login, downstream integrations that rely on provider connection states can now consistently identify Google-linked accounts, matching the behavior of the GitHub login flow.Closes #493
Type of Change
What Changed
apps/backend/src/routes/auth.ts:app.prisma.oAuthToken.upsertblock inside theGET /google/callbackhandler immediately after the user is created/updated.encrypt(tokenData.access_token)to securely persist the token.googleplatform name and extracted the exact scopes (defaulting toopenid email profile) so that the integration state matches what downstream integrations expect.apps/backend/src/__tests__/auth.test.ts:app.injectand a mocked globalfetchto simulate the Google authentication token/user endpoints.prisma.oAuthToken.upsertsuccessfully triggers with the right payload, preventing any future regressions on this flow.How to Test
cd apps/backend).pnpm exec vitest run src/__tests__/auth.test.ts./auth/meendpoint) to ensure your GoogleoAuthTokenis correctly linked to your user account.Checklist
pnpm -r run lintpasses).pnpm -r run typecheck).pnpm -r run test). (Note: The newly added auth tests pass perfectly. Any pre-existing failures in other modules liketeam.test.tsare from recent upstream refactoring)console.logor debug statements left in the code.Screenshots / Recordings
N/A (Backend logic only)
Additional Context
This ensures parity across all authentication providers, avoiding unpredictable behaviour where some providers silently left users without linked connection entries.