Skip to main content

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:

NameValue in this guide
Keycloak serverhttps://id.example.com
Keycloak realmexample
Keycloak client IDauthcrunch
AuthCrunch portalhttps://auth.example.com/auth/
AuthCrunch provider name and realmkeycloak
Protected applicationhttps://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.

Client​

In the example realm, create an OpenID Connect client with client ID authcrunch. Configure these capabilities and URLs:

SettingValue
Client authenticationOn; AuthCrunch is a confidential server-side client
Standard flowOn
Implicit flow, Direct access grants, Service accountsOff for this integration
Home URLhttps://auth.example.com/auth/
Valid redirect URIshttps://auth.example.com/auth/oauth2/keycloak/authorization-code-callback
Require PKCEOn
PKCE MethodS256

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.

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 settingValue
Nameauthcrunch-groups
Token Claim Namegroups
Full group pathOn
Add to ID tokenOn
Add to access tokenOff for this example
Add to userinfoOff for this example

Keycloak 26.7.4 Group Membership mapper with full group path and ID token enabled

Keycloak 26.7.4, captured in the disposable test realm: Full group path and Add to ID token are enabled. The temporary-admin banner belongs to that local test environment. Select the image to view it at full size.

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.

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.

StageAccess information
Keycloak membershipAlice belongs to /app-members
Keycloak ID tokenContains groups: ["/app-members"]
AuthCrunch transformMatches realm keycloak and role /app-members
AuthCrunch application tokenReceives app/member
Application policyAllows 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.

Users​

Create or choose two users in the same realm:

UserGroup membershipExpected /app result
Alice/app-members200, protected response
BobNo /app-members membership403, 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.

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.

A Keycloak group mapper supplies the membership this example checksA Keycloak group mapper supplies the membership this example checks

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.

Scroll horizontally when needed, or open the diagram for a larger view. Download Mermaid source.

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.

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​

  1. Open https://auth.example.com/app. The policy sends you to the portal.
  2. Choose Keycloak and sign in as Alice.
  3. If you reach the portal's Applications page, select Example app. You should see You reached the Keycloak-protected app.
  4. Select My identity or visit /auth/whoami. Confirm that roles includes /app-members, authp/user, and app/member, and realm is keycloak.
  5. In a separate browser session, sign in as Bob. He should reach the portal, but /app, /app/, and /app/nested should 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.

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​

SymptomCheck
Discovery fails at startupVerify the realm's full discovery URL, configured context path, DNS, and TLS trust from the AuthCrunch server.
invalid_redirect_uriMatch the registered callback to the portal hostname, /auth/ mount, and AuthCrunch realm keycloak.
Client authentication failsVerify Client authentication is On and the process has the current client ID and secret.
Issuer mismatchCompare the configured issuer with Keycloak's discovery document and ID token; proxy hostname settings and realm paths must agree.
PKCE failureRequire S256 at the client and leave PKCE enabled in AuthCrunch. Start a fresh login flow.
Email claim missingGive the user an email and include it in the ID token through the client's email scope.
Alice signs in but gets 403Check 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 appCheck 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 logoutThe 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.

  1. Search this topic in both repositories — searches topic-specific symbols and paths across both codebases.
  2. caddy-security: caddyfile_identity_provider_oauth.go — adapts OAuth/OIDC provider settings, scopes, endpoints, and trust options.
  3. go-authcrunch: pkg/idp/oauth/provider.go — loads discovery metadata, provider readiness, and driver setup.
  4. go-authcrunch: pkg/idp/oauth/authenticate.go — handles authorization callbacks, token exchange, and identity completion.
  5. go-authcrunch: pkg/idp/oauth/jwt.go — checks upstream token signatures, issuer, audience, and transaction trust.
  6. go-authcrunch: pkg/idp/oauth/claim_parser.go — maps supported token claims into the normalized provider identity.
  7. go-authcrunch: pkg/idp/oauth/claim_parser_test.go — tests supported claim paths and rejected value shapes.