Skip to content

feat: Google sign-in and invite-only signup - #120

Merged
flexdinesh merged 1 commit into
feat/admin-identityfrom
feat/google-invites
Oct 26, 2026
Merged

flexdinesh merged 1 commit into
feat/admin-identityfrom
feat/google-invites

Conversation

@flexdinesh

@flexdinesh flexdinesh commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

Stack 2/3: Google sign-in and invite-only signup (API).

  • Behavior owner and resulting change: new internal/google wire adapter (authorization URL with PKCE S256, state, nonce; code exchange; ID token claim checks: issuer, audience, expiry, nonce). identityservice owns invite and Google sign-in rules; reviewstore/invites.go persists them. Admin creates, lists and revokes invites (/api/v2/admin/invites*): link <public URL>/invite#t=…, shown once, stored hashed, 48h, single use; re-inviting replaces the email's pending invite; an email already linked to an account is 409. Signup requires Google to report a verified email equal to the invite's; accepting uses the invite and creates the account in one transaction. Accounts are keyed by Google sub, so an email change keeps the account and updates the recorded email. Usernames derive from the email. Google accounts without an account or invite cannot sign up.
  • Contracts/invariants affected; local/remote implications: OpenAPI adds POST /api/v2/auth/google, GET /api/v2/auth/google/callback (always 303 to the app, failures as ?signin=), POST /api/v2/invites/lookup, POST /api/v2/invites/google, admin invite routes and Invite. The callback is a cross-site navigation authenticated by state plus an HttpOnly flow cookie scoped to the callback path, so it runs before the same-origin check. New env DIFFX_SERVER_GOOGLE_CLIENT_ID / DIFFX_SERVER_GOOGLE_CLIENT_SECRET (secret env only); Google needs DIFFX_SERVER_PUBLIC_URL. Without Google config the server runs admin-only: Google routes 404, invite creation 409.
  • Validation results; remaining risks: mise run check and pre-push pass. In-process tests against a fake Google token endpoint: invite signup, unverified or mismatched email refused, missing flow cookie and reused state refused, sign-in by sub after an email change, uninvited account refused, disabled account refused, replaced and revoked invites unusable, member and CSRF refused on admin routes, unconfigured server. Store tests include concurrent acceptance yielding one account. Real-binary script (35 checks): config validation, authorize URL parameters, cookie attributes, hashed secrets, callback failures, replace/revoke. Not tested against real Google with real credentials yet.
  • Intentional architecture/design changes and rationale: ID token signature is not verified; the token comes directly from Google's token endpoint over TLS with the client secret (OIDC Core 3.1.3.7), avoiding a JWKS dependency. Invite lookup is POST so the secret stays out of URLs and logs. Boundary rules register internal/google. Docs and compose.yaml describe the Google settings and redirect URI.
  • UI evidence: none (API only; UI in the next layer).

Google OIDC adapter (PKCE, state, nonce, browser-bound flow cookie). Accounts
link by Google subject, so email changes keep the account. Admins create,
list and revoke 48h single-use invites; signup needs a verified Google email
matching the invite. Without Google config the server runs admin-only.
@flexdinesh
flexdinesh added this pull request to stack #122 October 26, 2026 13:32
@flexdinesh
flexdinesh merged commit 6fcd2bb into main Oct 26, 2026
2 checks passed
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