Sign in with Google
Google’s OpenID Connect implementation. One of the closest-to-spec implementations available; minimal surprises.
Discovery Document
https://accounts.google.com/.well-known/openid-configurationFetch this at startup (and cache with sane TTL) rather than hardcoding endpoints.
Key Endpoints
| Purpose | URL |
|---|---|
| Authorization | https://accounts.google.com/o/oauth2/v2/auth |
| Token | https://oauth2.googleapis.com/token |
| UserInfo | https://openidconnect.googleapis.com/v1/userinfo |
| JWKS (public keys) | https://www.googleapis.com/oauth2/v3/certs |
| Token revocation | https://oauth2.googleapis.com/revoke |
ID Token Claims
Always present: iss, sub, aud, exp, iat.
Conditional on scope / account type:
| Claim | Condition |
|---|---|
email, email_verified | email scope granted |
name, given_name, family_name, picture, locale | profile scope granted |
hd | User is a Google Workspace member; value is the workspace domain (use to gate workspace-only access) |
nonce | Sent in your auth request |
at_hash | Hash of access token (for hybrid flows) |
Identifier Choice
Google’s documentation is explicit: never use
sub.
Emails can change (workspace rename, account migration). The sub claim is stable and unique per Google account.
Signing Algorithm
ID Tokens are signed with RS256. Pin this; do not accept other algorithms.
Refresh Tokens
Google issues a refresh token only when:
access_type=offlineis included in the auth request, ANDprompt=consentis included on the first request, OR the user has not previously granted consent.
If you ask for offline access on every login, you may not get a refresh token after the first because Google won’t re-prompt for consent. Plan for this.
Hosted Domain (hd) for Workspace Apps
For “internal app — only members of acme.com may sign in”:
if claims.get("hd") != "acme.com":
raise PermissionDenied("not an acme.com user")Don’t rely on email.endswith("@acme.com") — Workspace can have alias domains and the hd claim is the canonical signal.
Common Implementation Path
flowchart LR A[Discover via .well-known] --> B[Build authorize URL with state, nonce, PKCE] B --> C[Redirect user] C --> D[Receive code at callback] D --> E[Validate state] E --> F[Exchange code → tokens] F --> G[Validate id_token: sig, iss, aud, exp, nonce] G --> H[Find/create user by sub]
Pitfalls
- Wrong
audafter credential rotation — if you regenerated client credentials, old sessions reference the oldaud. Reject and re-auth. - Assuming
email_verifiedis true — for some legacy / educational accounts it isn’t. Check explicitly. - Hardcoding endpoints — Google has rotated them historically.
See Also
- OpenID Connect
- ID Token
- Sign in with Apple — contrast for a non-spec-conforming implementation
- Social Login Security