Keycloak
Sign in with Keycloak and let an AuthCrunch policy protect your application. This walkthrough admits members of one Keycloak group. Other users can sign in to the portal but cannot access the app.
The integration was tested with Keycloak 26.7.4 and the published caddy-security v1.3.0 bundle, containing go-authcrunch v1.3.8. Real browser logins against a disposable local Keycloak instance verified PKCE, claim mapping, and application access. Public HTTPS and your deployment's network paths still need verification.
Before you start
Have an existing Keycloak server and permission to manage a realm and client. For a new server, follow Keycloak's getting-started guide and production configuration guidance. Complete Install and verify for the AuthCrunch binary.
This example uses two public HTTPS hosts. Replace them with your own:
| Name | Value in this guide |
|---|---|
| Keycloak server | https://id.example.com |
| Keycloak realm | example |
| Keycloak client ID | authcrunch |
| AuthCrunch portal | https://auth.example.com/auth/ |
| AuthCrunch provider name and realm | keycloak |
| Protected application | https://auth.example.com/app |
The Keycloak realm selects the tenant. The AuthCrunch realm identifies the login source inside the portal. They serve different purposes and need not share a name.
Realm
Create a realm named example, or select an existing application realm. Keep
master for Keycloak administration, as described in
Keycloak's realm setup.
Leave the realm's signing-key providers in place; this integration retrieves
public verification keys through discovery.
Check this URL from the AuthCrunch server:
https://id.example.com/realms/example/.well-known/openid-configuration
The response's issuer should be https://id.example.com/realms/example, and
its endpoints must be reachable by the browser or server that uses them. Current
Keycloak deployments use /realms/… by default. Include an additional /auth
prefix only if your deployment actually configures it. See
Keycloak's OIDC endpoints.
Earlier console reference: realm signing keys
These images document the old realm-key screens. Keep the current realm signing-key providers enabled as described above; the disabled switches and reduced key list in these historical captures are not steps to reproduce. The current integration discovers the provider’s public keys.



Client
In the example realm, create an OpenID Connect client with client ID
authcrunch. Configure these capabilities and URLs:
| Setting | Value |
|---|---|
| Client authentication | On; AuthCrunch is a confidential server-side client |
| Standard flow | On |
| Implicit flow, Direct access grants, Service accounts | Off for this integration |
| Home URL | https://auth.example.com/auth/ |
| Valid redirect URIs | https://auth.example.com/auth/oauth2/keycloak/authorization-code-callback |
| Require PKCE | On |
| PKCE Method | S256 |
Use the exact redirect URI. The code exchange happens on the server; the walkthrough does not require a wildcard redirect or browser CORS access to the token endpoint. Save the client and copy its secret from Credentials. Keep the client secret out of the browser and repository.
The generic driver sends the client ID and secret in the token request body. Use Keycloak's client ID/secret authentication for this setup, rather than a signed client assertion. The driver already sends S256 PKCE; configure the client to require that method. See the Keycloak OIDC client settings.
Screenshots: client registration in the earlier Keycloak console
The older console used Access Type = confidential; current Keycloak uses Client authentication = On. The screenshots use the master realm and old example URLs. Use the application realm, exact callback, flow settings, and S256 PKCE from this guide.





Earlier console reference: client keys and credentials
This sequence came from the old client-key walkthrough. The current configuration authenticates with a client secret from Credentials; it does not require generating a JKS archive or reusing a key-store password as that secret. The password fields and exported secret below are redacted.



Map groups into the ID token
Keep the client's profile and email scopes. Add a Group Membership
protocol mapper for this client, with these settings:
| Mapper setting | Value |
|---|---|
| Name | authcrunch-groups |
| Token Claim Name | groups |
| Full group path | On |
| Add to ID token | On |
| Add to access token | Off for this example |
| Add to userinfo | Off for this example |

Use the client's dedicated scope so this mapping applies to this client. In the client, open Client scopes, select its dedicated scope, and add a mapper By configuration → Group Membership. This is ordinary realm group membership, not an Organization Group Membership mapper.
With full paths enabled, a member of the top-level app-members group receives
this ID-token claim:
{
"groups": ["/app-members"]
}
The leading slash matters. AuthCrunch combines group claims into its roles,
so the Caddyfile matches the exact role /app-members. Putting the mapper only
on UserInfo does not supply this example's ID-token claim.
Screenshots: claim mappers in the earlier console
Current Keycloak keeps this configuration in the client’s dedicated scope. These older screenshots show the previous Mappers tab and help connect the email/groups claim names to their protocol mappers.


Groups
Create a top-level group named app-members. Assign only the intended
application users to it. In this walkthrough, Alice is a member and Bob is not.
| Stage | Access information |
|---|---|
| Keycloak membership | Alice belongs to /app-members |
| Keycloak ID token | Contains groups: ["/app-members"] |
| AuthCrunch transform | Matches realm keycloak and role /app-members |
| AuthCrunch application token | Receives app/member |
| Application policy | Allows app/member |
The transform also gives every signed-in Keycloak user authp/user for ordinary
portal access. That role does not meet the application's rule.
Screenshots: group creation and role mappings in the earlier console
The older example used Admins, Editors, and Viewers groups with portal roles. The current example needs one app-members group and a separate AuthCrunch transform that grants app/member. These screenshots illustrate the group editor and where role mappings were displayed; do not grant portal administration just to permit app access.









Users
Create or choose two users in the same realm:
| User | Group membership | Expected /app result |
|---|---|---|
| Alice | /app-members | 200, protected response |
| Bob | No /app-members membership | 403, no protected response |
Set each user's email and credentials. The generic driver requires an email
claim in the ID token by default, so retain the email client scope and its
ID-token mapping. For disposable test users, set a password in Credentials;
if it is temporary, complete Keycloak's required password change during login.
Assign Alice through the user's Groups tab. An email address or a similarly named Keycloak role does not automatically place a user in this group.
Screenshots: users, credentials, and membership
These earlier screens remain useful for locating user creation and credentials. Use ordinary test users in your application realm, set their email, and give only Alice membership in app-members.



Realm Roles
Existing deployments may prefer realm roles over a groups mapper. The bundled
generic driver recognizes realm_access.roles as well as top-level roles.
Configure the relevant realm-role mapper to include the intended role in the
ID token, then replace the group matcher with that role name. For example:
transform user {
match realm keycloak
match role example-app-member
action add role app/member
}
This is an alternative to the group-granting transform. Keeping both grants
would allow either one to add app/member. Nested client roles under
resource_access.CLIENT.roles are not extracted by this release's generic
parser; map the needed values into a supported claim. See the
generic claim reference.
There is no need to give application members authp/admin in Keycloak.
The example keeps portal permissions and application permissions separate.
Screenshots: realm-role alternative in the earlier console
These images show the earlier Add Role and role detail screens. For the alternative above, use a dedicated application role such as example-app-member. The old authp/admin, authp/user, and authp/guest names are historical examples and are not required Keycloak roles for this walkthrough.






A Keycloak group mapper supplies the membership this example checks. The mapper must put the intended group value in a supported ID-token claim. Realm roles are supported inputs, while resource_access client roles are not extracted by this released generic parser. A group’s full-path option changes the value the transform must match. The AuthCrunch realm label is distinct from the Keycloak realm.
Configure AuthCrunch
Save this as Caddyfile. Replace auth.example.com in both the site address and
policy login URL. Replace id.example.com and example in the issuer and
metadata URLs to match your Keycloak deployment. Keep the registered callback
in sync with the public portal hostname and /auth/ mount.
The example is embedded from the canonical Keycloak Caddyfile.
# Set the public portal hostname and the provider issuer/discovery URLs.
{
admin off
persist_config off
security {
oauth identity provider keycloak {
driver generic
realm keycloak
client_id {env.KEYCLOAK_CLIENT_ID}
client_secret {env.KEYCLOAK_CLIENT_SECRET}
scopes openid email profile
issuer https://id.example.com/realms/example
metadata_url https://id.example.com/realms/example/.well-known/openid-configuration
login icon text "Keycloak"
}
authentication portal myportal {
enable identity provider keycloak
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"
}
}
# Reserve portal roles and app/member for the transforms below.
transform user {
regex match role "^(authp/.*|app/member)$"
action drop matched role
}
transform user {
match realm keycloak
action add role authp/user
}
transform user {
match realm keycloak
match role /app-members
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 Keycloak-protected app." 200
}
respond "AuthCrunch is running. Open /app to begin." 200
}
}
The first transform removes provider-derived authp/* and app/member roles.
The following transforms grant portal access and translate /app-members
into application access. Keep that order so a Keycloak role named app/member
cannot bypass group membership or a role named authp/admin grant portal
administration. The required /app-members group remains available.
Supply these values to the AuthCrunch process:
export KEYCLOAK_CLIENT_ID='authcrunch'
export KEYCLOAK_CLIENT_SECRET='YOUR_KEYCLOAK_CLIENT_SECRET'
export JWT_SHARED_KEY="$(openssl rand -hex 32)"
Replace the secret placeholder with the client secret from Keycloak. For a managed service, store these values in its protected environment and reuse the signing key across restarts. The portal and policy must use the same key.
Start Keycloak before AuthCrunch, then check and run the Caddyfile:
./bin/authcrunch adapt --adapter caddyfile --config Caddyfile >/dev/null
./bin/authcrunch run --config Caddyfile
Adaptation checks parsing. Startup loads the discovery document and keys, and Caddy provisions HTTPS for the public portal host. DNS and ports 80/443 must be configured for your certificate setup. The example disables the admin endpoint; use Ctrl+C and restart the foreground process after edits.
The protected handler currently responds with a message. Replace that respond
with your application's reverse_proxy after verification. Keep authorize
before it and preserve the matcher covering /app and /app/*.
User Login
- Open
https://auth.example.com/app. The policy sends you to the portal. - Choose Keycloak and sign in as Alice.
- If you reach the portal's Applications page, select Example app.
You should see
You reached the Keycloak-protected app. - Select My identity or visit
/auth/whoami. Confirm thatrolesincludes/app-members,authp/user, andapp/member, andrealmiskeycloak. - In a separate browser session, sign in as Bob. He should reach the portal,
but
/app,/app/, and/app/nestedshould return 403.
Membership and transforms are evaluated at login. Removing Alice from the group does not rewrite an existing AuthCrunch token. This example uses a 900-second token lifetime; test changed membership with a fresh login.
Screenshots: sign-in, account applications, and portal identity
These captures show the older Keycloak account console and AuthCrunch identity view. They illustrate where a user signs in and inspects roles. The current expected roles are /app-members, authp/user, and app/member; the old account names, hostnames, and role set are illustrative only.



Check logout separately
The portal's Sign out action clears its browser session. In this configuration, it does not end the Keycloak SSO session. A subsequent Keycloak login can issue a new portal token without asking for a password again. Use separate browser sessions when testing Alice and Bob; portal logout alone does not switch their Keycloak account.
Troubleshoot
| Symptom | Check |
|---|---|
| Discovery fails at startup | Verify the realm's full discovery URL, configured context path, DNS, and TLS trust from the AuthCrunch server. |
invalid_redirect_uri | Match the registered callback to the portal hostname, /auth/ mount, and AuthCrunch realm keycloak. |
| Client authentication fails | Verify Client authentication is On and the process has the current client ID and secret. |
| Issuer mismatch | Compare the configured issuer with Keycloak's discovery document and ID token; proxy hostname settings and realm paths must agree. |
| PKCE failure | Require S256 at the client and leave PKCE enabled in AuthCrunch. Start a fresh login flow. |
| Email claim missing | Give the user an email and include it in the ID token through the client's email scope. |
| Alice signs in but gets 403 | Check membership, mapper placement, Add to ID token, and the leading slash in /app-members; inspect the resulting roles after a new login. |
| Bob reaches the app | Check for another transform or provider role that grants app/member, and ensure the policy requires that role rather than authp/user. |
| Login resumes immediately after logout | The Keycloak SSO session is still active; use a separate browser session for a different account. |
For discovery overrides, supported claim paths, and optional UserInfo behavior, continue to Generic OpenID Connect.
Agentic Prompts
Copy a prompt into your LLM to explore this topic. Each prompt prioritizes upstream repository guidance and code over this page, and asks for version-aware reasoning.
Map both realm namespaces
Help me understand Keycloak.
Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.
Relevant skills to locate: configuration-oauth-providers,
oauth-identity-provider.
Secondary reference:
https://docs.authcrunch.com/docs/authenticate/oauth/backend-oauth2-0011-keycloak
If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.
Explain Keycloak realm/issuer versus AuthCrunch realm/callback and named
provider selection. Trace confidential code flow, body client authentication,
and S256 PKCE. Ask for the installed Keycloak and AuthCrunch versions before
describing console screens.
Read the group mapper
Help me understand Keycloak.
Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.
Relevant skills to locate: configuration-oauth-providers,
oauth-identity-provider.
Secondary reference:
https://docs.authcrunch.com/docs/authenticate/oauth/backend-oauth2-0011-keycloak
If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.
Explain placing Group Membership in the ID token, full-path values such as
/app-members, and exact transform matching. Compare ID-token groups,
realm_access.roles, and client resource_access roles using the actual parser.
Show why requesting a scope alone may not publish the needed claim.
Diagnose a mapper mismatch
Help me understand Keycloak.
Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.
Relevant skills to locate: configuration-oauth-providers,
oauth-identity-provider.
Secondary reference:
https://docs.authcrunch.com/docs/authenticate/oauth/backend-oauth2-0011-keycloak
If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.
Help me investigate successful login with 403 using redacted token/group
shapes, mapper placement, full-path setting, email presence, issuer, and
transform order. Separate Keycloak assignment from the AuthCrunch permission
and avoid broadening authp/user to admit everyone.
Test member and reserved-role cases
Help me understand Keycloak.
Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.
Relevant skills to locate: configuration-oauth-providers,
oauth-identity-provider.
Secondary reference:
https://docs.authcrunch.com/docs/authenticate/oauth/backend-oauth2-0011-keycloak
If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.
Design cases for intended group, nonmember, similar group name, realm role
named app/member, reserved authp/admin, and membership removal with fresh
login. Explain the normalized and issued roles at each stage.
Review restart and logout expectations
Help me understand Keycloak.
Primary authorities (take precedence over website documentation):
https://github.com/greenpau/caddy-security/blob/main/AGENTS.md
https://github.com/greenpau/caddy-security/tree/main/.codex/skills
https://github.com/greenpau/go-authcrunch/blob/main/AGENTS.md
https://github.com/greenpau/go-authcrunch/tree/main/.codex/skills
Read each repository's root AGENTS.md first, then any scoped AGENTS.md that
applies to inspected paths. Read the relevant SKILL.md files and follow their
implementation and test references.
Relevant skills to locate: configuration-oauth-providers,
oauth-identity-provider.
Secondary reference:
https://docs.authcrunch.com/docs/authenticate/oauth/backend-oauth2-0011-keycloak
If a source is inaccessible, ask me to paste its relevant text. Identify the
versions your answer applies to; main may be newer than my release. Resolve
disagreements using code and tests, and flag unverified claims. Use synthetic
credentials and redacted examples; explain proposed checks before any changes.
Compare local portal logout with Keycloak SSO, token lifetime, and
provider/user changes. Help me verify browser/server reachability for
published metadata. Quiz me on why a fresh provider token is needed when
retesting a changed group mapper.
Source Code References
Start with the code search, then follow the parser, runtime, and tests relevant
to this topic. These links target main; use GitHub's branch/tag selector to
compare them with your installed release.
- Search this topic in both repositories — searches topic-specific symbols and paths across both codebases.
- caddy-security: caddyfile_identity_provider_oauth.go — adapts OAuth/OIDC provider settings, scopes, endpoints, and trust options.
- go-authcrunch: pkg/idp/oauth/provider.go — loads discovery metadata, provider readiness, and driver setup.
- go-authcrunch: pkg/idp/oauth/authenticate.go — handles authorization callbacks, token exchange, and identity completion.
- go-authcrunch: pkg/idp/oauth/jwt.go — checks upstream token signatures, issuer, audience, and transaction trust.
- go-authcrunch: pkg/idp/oauth/claim_parser.go — maps supported token claims into the normalized provider identity.
- go-authcrunch: pkg/idp/oauth/claim_parser_test.go — tests supported claim paths and rejected value shapes.