Browser-based OpenID Connect sign-in using the standard go-oidc library and golang.org/x/oauth2, without the AuthGate SDK. This is useful when you want to integrate AuthGate (or any OIDC provider) into an existing Go web app that already uses the upstream OAuth2 toolchain.
Authorization Code with:
- State cookie — CSRF protection on the redirect back to
/callback. - Nonce — CSRF / replay protection tied to the ID token. Generated on
/login, verified againstid_token.nonceon callback. - PKCE (S256) — required by most modern providers (AuthGate rejects public clients without it:
invalid_request: pkce required for public clients). Verifier generated on/login, challenge sent viaoauth2.S256ChallengeOption, verifier sent back on code exchange viaoauth2.VerifierOption. - ID token verification — signature checked against the provider's JWKS;
iss,aud,expvalidated automatically byverifier.Verify. at_hash— when present, access token is bound to the ID token viaidToken.VerifyAccessToken.
- Go 1.25+
- An OIDC provider (e.g., AuthGate) with a confidential client that allows
http://localhost:8088/callbackas a redirect URI
| Variable | Required | Description |
|---|---|---|
ISSUER_URL |
Yes | OIDC issuer URL (discovery: $ISSUER_URL/.well-known/openid-configuration) |
CLIENT_ID |
Yes | OAuth 2.0 client identifier |
CLIENT_SECRET |
No | OAuth 2.0 client secret. Omit for public clients (PKCE-only) |
REDIRECT_URL |
No | Defaults to http://localhost:8088/callback |
export ISSUER_URL=https://auth.example.com
export CLIENT_ID=your-client-id
export CLIENT_SECRET=your-client-secret
go run main.goOr create a .env file in the go-oidc/ directory:
ISSUER_URL=https://auth.example.com
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret
REDIRECT_URL=http://localhost:8088/callbackThen open http://localhost:8088/ in a browser and click Sign in.
| Route | Description |
|---|---|
/ |
Landing page with a sign-in link |
/login |
Generates state + nonce, sets cookies, redirects to the provider's authorize endpoint |
/callback |
Validates state, exchanges code, verifies ID token + nonce, fetches userinfo, renders JSON |
oidc.NewProvider(ctx, issuerURL)fetches the discovery document and extracts the authorize/token/userinfo/JWKS endpoints.provider.Verifier(&oidc.Config{ClientID: ...})builds an*oidc.IDTokenVerifierthat caches JWKS keys and validatesiss,aud,exp, and signature.oauth2.Configis wired toprovider.Endpoint()so the upstream OAuth2 helpers do the heavy lifting of the code exchange.- On
/login, fresh random state, nonce, and PKCE verifier are stored in short-lived HttpOnly cookies. The nonce is passed viaoidc.Nonce(nonce)so the provider echoes it into the ID token, and the PKCE challenge is added viaoauth2.S256ChallengeOption(verifier). - On
/callback, the handler:- Rejects the response if
statedoes not match the cookie. - Exchanges the code via
oauth2Config.Exchange(ctx, code, oauth2.VerifierOption(pkceVerifier)). - Extracts the raw ID token from
oauth2Token.Extra("id_token"). - Calls
verifier.Verifyto cryptographically validate it. - Rejects the response if
idToken.Noncedoes not match the cookie. - Calls
idToken.VerifyAccessTokenwhen anat_hashclaim is present. - Queries the UserInfo endpoint via
provider.UserInfoand returns both ID token claims and UserInfo claims as JSON.
- Rejects the response if
GET /callback (successful flow) returns something like:
{
"subject": "user-uuid-1234",
"issuer": "https://auth.example.com",
"audience": ["your-client-id"],
"expiry": "2026-04-23T14:23:45Z",
"id_token_claims": {
"sub": "user-uuid-1234",
"email": "alice@example.com",
"email_verified": true,
"nonce": "..."
},
"access_token": "eyJhbGci...",
"refresh_token": "abcdef12...",
"token_type": "Bearer",
"token_expiry": "2026-04-23T15:23:45Z",
"userinfo": {
"sub": "user-uuid-1234",
"email": "alice@example.com",
"name": "Alice"
},
"userinfo_fetch_ok": true
}- For production, store
id_token,access_token, andrefresh_tokenin a session (e.g., encrypted cookie, server-side store) rather than echoing them to the browser — this example prints them to make the flow transparent. - The
Securecookie flag is set automatically when the server is reached over TLS (r.TLS != nil). Behind a reverse proxy that terminates TLS, configure the proxy to pass the correct scheme or set the flag explicitly. go-oidchandles JWKS key rotation transparently — you do not need to refresh the verifier manually.