The oauth2 package implements the Authorization Code flow with PKCE and OIDC support. Four provider presets are built in: Google, GitHub, Facebook, and Zalo. Any other provider is configured as a plain ClientRegistration.

Import

Quick setup

The callback handler stores the principal in the session; subsequent requests are authenticated by the securityhttp.SessionAuthentication middleware. The callback route (/login/oauth2/code/*) is intentionally CSRF-exempt: the single-use, session-bound state parameter is the CSRF defense for the authorization-code flow. If you mount web/csrf, exclude that path — securityhttp does not add this exclusion automatically. The nonce check is only enforced when the authorization request set one (non-empty), and state is validated before the code exchange, closing an auth-code-injection window:

Provider presets

oauth2.Preset(t oauth2.ProviderType) returns the fixed endpoints, default scopes, and subject attribute for a known provider; oauth2.NewRegistration uses it internally to build a validated ClientRegistration. NewRegistration only works for these four known providers — Preset has no entry for ProviderGeneric, so a custom provider is built as a plain ClientRegistration (see below).

Custom provider

Build the ClientRegistration directly. Scopes containing "openid" make it an OIDC registration (IsOIDC() returns true), which Validate() then requires a JWKSetURI and Issuer for:
Drop "openid" (and JWKSetURI/Issuer) for a plain OAuth2 provider — the callback then calls UserInfoURI instead of validating an ID token. AllowInsecure: true permits http:// endpoints for localhost dev fixtures. Leave it false (the default) in production; every endpoint is otherwise required to be https://.

PKCE

PKCE (Proof Key for Code Exchange) is always enabled. A code_verifier is generated per authorization request and stored in the session. The code_challenge is sent in the authorization redirect as S256. No configuration is needed — PKCE cannot be disabled.

OIDC

A registration is OIDC when its Scopes include "openid" (ClientRegistration.IsOIDC()). For an OIDC registration the callback validates the ID token instead of calling the userinfo endpoint. ID token validation:
  • Signature verified against the provider’s JWKS
  • aud must match ClientID
  • exp must be present and in the future (required, not just checked if set)
  • iss must match the registration’s Issuer
  • nonce must match the value generated for that authorization request (ErrNonceMismatch otherwise)

State parameter

A single-use, session-bound state parameter is generated per authorization request. It is verified on callback and immediately invalidated, preventing CSRF attacks on the OAuth2 flow.

Errors

Failures are sentinel errors, matchable with errors.Is: LoginConfig.OnFailure receives these (default: write a 401). The provider’s error_description is never reflected back to the client.

Granted authorities

By default every successful login is granted OAUTH2_USER plus one SCOPE_<x> authority per granted OAuth2 scope (oauth2.DefaultAuthoritiesMapper()). Supply LoginConfig.AuthoritiesMap to map provider attributes to your own roles:

Session principal

After a successful callback, the OAuth2 subject (e.g. "12345678" for Google) is stored as the session principal. On subsequent requests, SessionAuthentication calls UserService.LoadUser(subject). Implement UserService to synthesize a user from the subject if it doesn’t exist in your database, or create a row on first login:

AuthorizedClient

After a successful login, the access/refresh token pair is stored server-side, keyed by session and registration id, in an AuthorizedClientRepository — it is never placed on the principal, a cookie, or a log. Load it with the session belonging to the current request:
client.AccessExpired(time.Now()) reports whether the access token has expired (a zero expiry, i.e. unknown lifetime, is treated as not expired).