Google sign-in
Sign in with a Google account and give selected accounts access to your
application. This walkthrough uses Google's account ID (sub) to grant
app/member; other Google users can reach the portal but not the protected app.
The configuration targets caddy-security v1.3.0, containing go-authcrunch v1.3.8. Local OIDC fixtures check the released driver's code exchange and access rules. Complete the final login checks with your own Google client; local fixtures do not verify Google's consent or organization policies.
Before you start
Complete Install and verify. You need a Google Cloud
project where you can manage OAuth clients, two Google accounts for access tests,
and a public HTTPS portal. This guide uses https://auth.example.com/auth/ and
protects /app; replace the hostname throughout.
For the provider/portal/policy relationship, see the OAuth and OIDC overview.
Configure Google Auth Platform
In the Google Cloud console, choose your project and open Google Auth Platform. Complete Branding with your app's name, support contact, and required website information. Configure Audience for the accounts you intend to support:
| Audience | Use it for |
|---|---|
| Internal | Accounts in the project's Google Workspace or Cloud Identity organization, when available |
| External | Google accounts outside that organization, including personal accounts |
Review Data Access for the basic identity scopes: openid, email, and
profile. The Caddyfile requests openid email profile. It does not need Gmail,
Drive, or Cloud Identity group permissions for the account-ID example. See
Google's consent setup.
For an External app, review Audience → Publishing status and Test users.
Google makes an exception to its usual testing restrictions for basic
name/email/profile sign-in. Do not treat the test-user list as the app's access
policy, or assume a seven-day testing expiry applies to these scopes. Extra
scopes change the requirements; follow
Google's audience rules.
The AuthCrunch policy below is what restricts /app.
Screenshots: project and consent setup in the earlier Google console
These retained screenshots show the earlier APIs & Services interface. In the current console, use Google Auth Platform and the Branding, Audience, and Data Access pages described above. Example names and verification status belong to the original capture.




Register a web client
Open Clients → Create client and choose Web application. Register:

| Setting | Value |
|---|---|
| Authorized redirect URI | https://auth.example.com/auth/oauth2/google/authorization-code-callback |
| Client ID | Copy the complete value, including .apps.googleusercontent.com |
| Client secret | Save the secret for the AuthCrunch server |
The callback includes the portal's /auth/ mount and realm google. Match its
scheme, host, path, and trailing slash exactly. The portal exchanges the code
on the server; this example does not use a browser JavaScript client or require
an Authorized JavaScript origin. See
Google's web-client instructions.
Screenshots: OAuth client creation and callback
These earlier screens show the same client type and callback concepts. Use the exact HTTPS callback from the table above, not the localhost address in the old capture. The client secret is blank in the retained confirmation image.


Configure AuthCrunch
Save the following as Caddyfile. Change auth.example.com in both the site
address and policy URL. The complete file is embedded from the
canonical Google example.
# Set the public portal hostname and GOOGLE_ALLOWED_SUB before use.
{
admin off
persist_config off
security {
oauth identity provider google {
driver google
realm google
client_id {env.GOOGLE_CLIENT_ID}
client_secret {env.GOOGLE_CLIENT_SECRET}
scopes openid email profile
login icon text "Google"
}
authentication portal myportal {
enable identity provider google
crypto default token lifetime 900
crypto key sign-verify {env.JWT_SHARED_KEY}
cookie path /
ui {
links {
"Example app" /app icon "las la-external-link-alt"
"My identity" /auth/whoami icon "las la-user"
}
}
transform user {
match realm google
action add role authp/user
}
transform user {
match realm google
match sub {$GOOGLE_ALLOWED_SUB}
action add role app/member
}
}
authorization policy apppolicy {
set auth url https://auth.example.com/auth/
crypto key verify {env.JWT_SHARED_KEY}
allow roles app/member
}
}
}
auth.example.com {
route {
redir /auth /auth/ 308
authenticate /auth/* with myportal
@app path /app /app/*
route @app {
authorize with apppolicy
respond "You reached the Google-protected app." 200
}
respond "AuthCrunch is running. Open /app to begin." 200
}
}
Set the environment before starting the process:
export GOOGLE_CLIENT_ID='YOUR_COMPLETE_CLIENT_ID.apps.googleusercontent.com'
export GOOGLE_CLIENT_SECRET='YOUR_GOOGLE_CLIENT_SECRET'
export GOOGLE_ALLOWED_SUB='REPLACE_WITH_GOOGLE_SUB'
export JWT_SHARED_KEY="$(openssl rand -hex 32)"
Use your actual client credentials. Leave the subject placeholder for the first
login, then replace it as described below. GOOGLE_ALLOWED_SUB is an account ID,
not an email address or the OAuth client ID.
{$GOOGLE_ALLOWED_SUB} expands when the Caddyfile is parsed. The client ID,
client secret, and signing key use runtime {env.VARIABLE} placeholders. Supply
the whole Google client ID; do not append its suffix again in the Caddyfile.
For a managed service, use its protected environment and keep the signing key
stable across restarts.
./bin/authcrunch adapt --adapter caddyfile --config Caddyfile >/dev/null
./bin/authcrunch run --config Caddyfile
Adaptation checks parsing. Startup loads Google's discovery document and signing keys; the host needs outbound HTTPS connectivity. Public DNS and certificate issuance must work for your portal hostname. The example disables the admin endpoint, so stop with Ctrl+C and restart after configuration or environment changes.
Select the account and verify access
- Open
/auth/and choose Google. Sign in with the account that should have application access. - Open My identity (
/auth/whoami) and copy that account'ssubvalue. Confirm you are using the intended Google account. This login hasauthp/user; the unconfigured subject does not grantapp/member. - Set
GOOGLE_ALLOWED_SUBto that exact value, stop AuthCrunch, and start it again with the updated environment. - Sign out of the portal and sign in again to issue a token with the new role.
Select Example app or open
/app. - In a separate browser session, sign in as a second Google user and verify that the app denies access.
| Check | Selected account | Other account |
|---|---|---|
| Portal identity | Has authp/user and app/member | Has authp/user only |
/app, /app/, /app/nested | 200, protected response | 403, no protected response |
If your Google audience prevents the second account from signing in, that tests Google's boundary. Use another account eligible for the same audience to test the AuthCrunch denial rule.
Google documents sub as stable across email changes. An email suffix does
not establish Workspace membership. This release checks ID-token email
presence but does not enforce email_verified or expose hd to transforms;
the example therefore grants access by the selected sub. See
Google's identity claims
and the released claim parser.
The Google driver discovers its endpoints and sends state, nonce, and S256 PKCE. Keep these checks enabled. The example uses a 900-second portal token; changing the allowlist does not rewrite an already issued token. Portal logout clears the portal session and does not sign the browser out of Google. Use a separate browser profile when testing another account.
After the allow/deny checks pass, replace the protected respond with your
application's reverse_proxy, keeping authorize before it.
Workspace domains and groups
Organization access
For an organization-only application, use Google's Internal audience when
your project supports it. The hd authorization parameter is a login hint;
requesting it is not an access check. This release does not copy the ID token's
hd or email_verified into the portal claims, so an email-domain transform is
not a substitute for validating Workspace membership. Keep the account allowlist
or use an independently verified group-based policy.
Optional Cloud Identity groups
The released Google driver can query the Cloud Identity membership graph when its scopes include:
scopes openid email profile https://www.googleapis.com/auth/cloud-identity.groups.readonly
Replace the existing scopes line inside oauth identity provider google.
Enable the Cloud Identity API, authorize the additional scope, and verify the
signed-in user can read the relevant memberships. Google's
membership query guidance
explains permissions. The
graph endpoint
requires a supported Workspace Enterprise/Education edition or Cloud Identity
Premium; ordinary personal accounts are not sufficient.
The driver's group lookup
uses the user's email and access token. It reads response.groups[].displayName
from the returned operation and adds those display names to portal roles.
It does not use immutable group IDs, filter on your configured application
role, or poll an unfinished operation. A renamed group changes the match, and
identical display names cannot distinguish groups.
Only after confirming the returned roles at /auth/whoami, replace the
account-granting transform with one matching your group's exact display name:
transform user {
match realm google
match role "Example App Members"
action add role app/member
}
Keep the separate portal-access transform. Replacing the account rule makes
the group required; retaining both rules allows either to grant access. A group
lookup failure is logged and login continues, so test an API failure or missing
group as well as a member. Without the required role, /app must remain denied.
This optional integration needs validation against your organization's licenses,
permissions, naming controls, and actual API responses.
Troubleshoot
| Symptom | Check |
|---|---|
redirect_uri_mismatch | Compare the registered callback with the public host, /auth/ mount, and realm google. |
invalid_client | Use the complete client ID once and the corresponding current secret. |
| Google blocks consent | Check Internal/External audience, publishing status, requested scopes, and organization policy. |
| Selected user gets 403 | Compare sub with GOOGLE_ALLOWED_SUB, restart after changing the environment, then obtain a new portal token. |
| Another user reaches the app | Check for another transform granting app/member and ensure the policy does not allow authp/user. |
| Issuer validation fails | Compare the ID token's issuer with discovery; this release uses exact equality, including Google's HTTPS prefix. Do not disable key or nonce checks. |
| Workspace group is absent | Check the extra scope, API enablement, account edition, membership permissions, returned operation, and exact display name. |
| Sign-in resumes after logout | The Google session still exists; use a separate browser session for another account. |
For token validation and claim extraction details, continue to Generic OpenID Connect.