Skip to content

feat: Google OAuth (XOAUTH2) sign-in alongside password login#4

Open
Aravinda-HWK wants to merge 1 commit into
mainfrom
feat/google-oauth-xoauth2
Open

feat: Google OAuth (XOAUTH2) sign-in alongside password login#4
Aravinda-HWK wants to merge 1 commit into
mainfrom
feat/google-oauth-xoauth2

Conversation

@Aravinda-HWK

Copy link
Copy Markdown
Collaborator

Summary

Adds "Sign in with Google" next to the existing app-password login, implementing the proposal's "gateway runs the OAuth flow and hands the token to IMAP over SASL XOAUTH2" path. Non-Google providers and app-password login are unaffected. Targets main and contains only the OAuth changes (independent of the client-cache PR).

Flow

Browser "Sign in with Google" → GET /api/v1/auth/google/start (302 → Google)
  → consent → GET /api/v1/auth/google/callback?code=…
    → backend exchanges code, resolves email, opens IMAP via XOAUTH2,
      issues Quicksilver JWT
    → 302 → SPA /auth/callback#token=… → session persisted → inbox

Backend

  • internal/oauth — Google provider: consent URL, code exchange, userinfo email lookup, token auto-refresh. Disabled (endpoints 404) when no client ID is configured.
  • internal/imap/xoauth2.go — XOAUTH2 SASL mechanism (go-sasl ships none); IMAP client authenticates via XOAUTH2 for OAuth sessions, LOGIN otherwise.
  • internal/smtpSMTPAuthXOAUTH2 for OAuth sessions.
  • mail.Credentials gains AuthType / AccessToken / RefreshToken / TokenExpiry.
  • session.Store refreshes the access token (and re-seals the session) on every IMAP reconnect and SMTP send, so long-lived sessions survive token expiry.
  • handlers/oauth.go/api/v1/auth/google/{start,callback} with an anti-CSRF state cookie; passes the JWT to the SPA via the URL fragment.
  • config + .env.exampleGOOGLE_OAUTH_CLIENT_ID/SECRET/REDIRECT_URL, FRONTEND_OAUTH_CALLBACK.

Frontend

  • OAuthCallbackPage + /auth/callback route — reads the JWT from the fragment, persists the session, scrubs the URL.
  • AuthContext.loginWithOAuth and a "Sign in with Google" button on the login page.

New dep: golang.org/x/oauth2.

Operator setup (required to enable)

OAuth is off until configured. Create a Google Cloud OAuth client (Web application) with:

  • Authorized redirect URI: http://localhost:3000/api/v1/auth/google/callback
  • Scope https://mail.google.com/ (restricted; testing mode + test users for personal use)

then set the QUICKSILVER_GOOGLE_OAUTH_* env vars.

Verification

  • go build ./..., go vet ./..., go test ./... all pass.
  • npm run build passes; typecheck clean for changed files.
  • Full end-to-end Google sign-in requires live OAuth credentials, so it was not exercised in CI.

Implements the proposal's "gateway runs the OAuth flow and hands the token
to IMAP over SASL XOAUTH2/OAUTHBEARER" path. Adds "Sign in with Google"
next to the existing app-password login; non-Google providers are
unaffected.

Backend:
- internal/oauth: Google provider (consent URL, code exchange, userinfo
  email lookup, token auto-refresh). Disabled (endpoints 404) when no
  client ID is configured.
- internal/imap/xoauth2.go: XOAUTH2 SASL mechanism (go-sasl ships none);
  client authenticates via XOAUTH2 for OAuth sessions, LOGIN otherwise.
- internal/smtp: SMTPAuthXOAUTH2 for OAuth sessions.
- mail.Credentials gains AuthType/AccessToken/RefreshToken/TokenExpiry.
- session.Store refreshes the access token (and re-seals the session) on
  every IMAP reconnect and SMTP send, keeping long-lived sessions alive.
- handlers/oauth.go: /api/v1/auth/google/{start,callback} with a CSRF
  state cookie; hands the JWT to the SPA via the URL fragment.
- config + .env.example: GOOGLE_OAUTH_* and FRONTEND_OAUTH_CALLBACK.

Frontend:
- OAuthCallbackPage + /auth/callback route: reads the JWT from the
  fragment, persists the session, scrubs the URL.
- AuthContext.loginWithOAuth and a "Sign in with Google" button.

Adds dep: golang.org/x/oauth2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant