feat(auth): A4 — Sign in with Google on the web and in the app - #292
Merged
Conversation
- web: Google Identity Services renders its own button, drawn only when the instance advertises a web client id. Same nonce goes to Google and to our server (Google echoes it raw). The config fetch now feeds both providers, and the divider appears only when something sits above it. - iOS: authorization code + PKCE via ASWebAuthenticationSession, with no SDK — the flow is ~100 lines of OAuth, and doing it here matches how the server verifies Apple/Google tokens with node:crypto rather than a library. The verifier never leaves the device, so intercepting the redirect gains nothing; state guards against a redirect we didn't start; the session is ephemeral so signing out doesn't leave the next person silently signed in. ASWebAuthenticationSession intercepts the callback itself, so no URL scheme has to be registered in project.yml. The button sits BELOW Sign in with Apple — Guideline 4.8 wants SIWA offered with equal prominence wherever a third-party login is. /api/auth/config now advertises both client ids; each client draws its button only if its own id is present, so an instance can run Google on one surface and not the other. Client ids are public by design — they identify the app, they don't authorize it. Plan: docs/PLAN_AUTH_OTP_GOOGLE_v1.0.md (A4). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
Reviewed the client-side Google flow closely, since it's hand-rolled OAuth. It's correct — ready to merge. iOS PKCE is textbook
Follow-ups (none block this PR)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #291. The client half of Google sign-in.
Web
Google Identity Services renders its own button, drawn only when the instance advertises a web client id. The same nonce goes to Google and to our server (Google echoes it raw, unlike Apple's hash). The
/api/auth/configfetch now feeds both providers, and the "or use email" divider appears only when something sits above it.iOS — authorization code + PKCE, no SDK
The flow is ~100 lines of OAuth, so we run it ourselves rather than take a dependency — the same call the server side makes (verify Apple/Google JWTs with
node:cryptoinstead of a library, and no RevenueCat for IAP).stateis checked on the way back, guarding against a redirect we didn't start.ASWebAuthenticationSessionintercepts the callback itself, so no URL scheme has to be registered inproject.yml— which also means the client id doesn't need to be known at build time.Config
/api/auth/confignow advertises both client ids, and each client draws its button only if its own id is present — so an instance can run Google on the app and not the web, or vice versa. Client ids are public by design: they identify the app, they don't authorize it.Verification
initializereceives the configured client id and a nonce, and clicking it posts the token plus the matching nonce to/api/auth/googleand lands signed in. Server log confirms the nonce round-trips identically. Screenshot in the thread.swiftc -typecheckagainst the iOS 17 simulator SDK, clean. Added an XCTest for the redirect-scheme derivation, and verified the same logic standalone underswift(correct reversal; nil for a non-Google id and for empty input, rather than building a redirect Google would reject).npm test→ 1173 green, typecheck clean.🤖 Generated with Claude Code