Okta
Sign in through Okta and allow an app-members group to reach a protected
application. The example separates Okta app assignment, authorization-server
policy, and AuthCrunch's application policy so each can be checked independently.
This guide targets caddy-security v1.3.0 with go-authcrunch v1.3.8. Local OIDC fixtures verify the released configuration, token checks, and allow/deny behavior. Live Okta assignment, policies, and consent must still be verified in your organization.
Before you start
Complete Install and verify. Have an Okta administrator,
a group named app-members, and two test accounts: one member and one nonmember.
Use a public AuthCrunch hostname with ports 80 and 443 available for HTTPS.
Replace auth.example.com in the example and app registration.
The named okta driver builds URLs for a custom authorization server and
requires a server_id, such as default. Okta's built-in org authorization server
has a different issuer and endpoint layout. Custom authorization servers require
API Access Management in production; test organizations may include them. The
default server can exist without an access policy, so check the policy rather
than assuming the name makes the server ready. See
Okta's authorization-server types.
Register a web application
In the Admin Console, open Applications and Resources → Applications → Create App Integration. Choose OIDC – OpenID Connect and Web Application. Older consoles may label the first section simply Applications. Follow Okta's web app registration guidance with these values:
| Setting | Value |
|---|---|
| Sign-in redirect URI | https://auth.example.com/auth/oauth2/okta/authorization-code-callback |
| Grant type | Authorization Code |
| Client authentication | Client secret; token endpoint method client_secret_post |
| Assignments | The users or groups intended to sign in through this portal |
AuthCrunch sends the client ID and secret in the token request's form body.
Configure token_endpoint_auth_method as client_secret_post; an API-created
client can default to client_secret_basic. If the console does not expose
this setting, have your administrator verify it through Okta's client API. See
Okta client authentication methods.
Save the Client ID and Client secret for the server environment. The released driver sends S256 PKCE with the authorization-code flow. This example does not request an Okta refresh token or use implicit/client-credentials grants.

Screenshots: earlier Okta web app settings
These screens use old localhost callbacks, example groups, and the earlier console layout. Apply the callback, assignments, and token authentication method documented above.



App assignment controls who may attempt Okta sign-in. To test AuthCrunch's
nonmember denial separately, assign both test users to the app while keeping
only one in app-members. If you assign only that group, Okta rejects the
nonmember before AuthCrunch gets a token.
Configure the authorization server
In Security → API → Authorization Servers, select your server. This guide
uses default; another server ID is valid when its scopes, claims, and policies
are configured consistently.
Add the groups scope and ID-token claim
Create a scope named groups if it is absent. The example explicitly requests
it, so it need not be a default scope. Then create this claim:
| Claim setting | Value |
|---|---|
| Name | groups |
| Include in token type | ID Token, Always |
| Value type | Groups |
| Filter | Matches regex: ^app-members$ |
| Include in | The groups scope |
| Disable claim | Off |
Always matters: the named driver does not fetch UserInfo to fill in missing ID-token groups. A claim available only through UserInfo does not grant the application role in this example. See Okta's claim inclusion settings and groups claim guidance.
Ensure the returned ID token also contains email, as required by the driver's
default email-presence check. If your authorization-server configuration only
releases email through UserInfo, configure its email claim for ID-token
inclusion. The app's access rule remains group based.

Screenshots: authorization server and groups scope
Current navigation is Security → API → Authorization Servers. These earlier console images show the corresponding server and scope settings.


Add an access policy and rule
Under the server's Access Policies, select or create a policy for this client.
Add a rule allowing Authorization Code, the intended assigned users, and
the requested openid email profile groups scopes. Review rule ordering and
the existing policies in your organization. See
Okta's access-policy setup.
These controls serve separate purposes:
| Control | Decision |
|---|---|
| Okta app assignment and sign-on policy | May this user sign in to this client? |
| Authorization-server access policy | May this client/user obtain tokens for these scopes? |
| ID-token claim filter | Which of the user's group names are included? |
| AuthCrunch transform and policy | Does the resulting identity receive app/member and reach /app? |
Okta assignment, token issuance, and app membership are separate gates. App assignment admits a user to the Okta client, and the custom authorization-server policy permits token issuance. The example requires the exact app-members value in the ID-token groups claim. A claim released only through UserInfo does not satisfy this named-driver walkthrough. Test an assigned nonmember to isolate AuthCrunch denial.
Configure AuthCrunch
Save this as Caddyfile. The source is the
canonical Okta example.
# Set the public portal hostname, OKTA_DOMAIN, and OKTA_SERVER_ID before use.
{
admin off
persist_config off
security {
oauth identity provider okta {
driver okta
realm okta
client_id {env.OKTA_CLIENT_ID}
client_secret {env.OKTA_CLIENT_SECRET}
domain_name {$OKTA_DOMAIN}
server_id {$OKTA_SERVER_ID}
scopes openid email profile groups
login icon text "Okta"
}
authentication portal myportal {
enable identity provider okta
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 okta
action add role authp/user
}
transform user {
match realm okta
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 Okta-protected app." 200
}
respond "AuthCrunch is running. Open /app to begin." 200
}
}
The portal maps the token's groups into roles before applying transforms.
The first transform removes provider-derived authp/* and app/member roles;
the following transforms grant portal access and translate the exact
app-members group into app access. Keep that order so a group named
app/member cannot bypass membership or authp/admin grant portal
administration. A signed-in user without app-members can enter the portal
but receives 403 from /app and its children.
Set these variables for the process that runs AuthCrunch:
export OKTA_DOMAIN='YOUR_ORG.okta.com'
export OKTA_SERVER_ID='default'
export OKTA_CLIENT_ID='YOUR_CLIENT_ID'
export OKTA_CLIENT_SECRET='YOUR_CLIENT_SECRET'
export JWT_SHARED_KEY="$(openssl rand -hex 32)"
Use your actual Okta hostname without https:// or a trailing slash; your
organization may use a different Okta domain or a configured custom domain.
OKTA_SERVER_ID is the authorization-server ID, not the app's client ID.
{$OKTA_DOMAIN} and {$OKTA_SERVER_ID} expand during Caddyfile parsing.
Credentials and the signing key use runtime {env.VARIABLE} placeholders.
Protect the environment file and reuse the signing key across restarts.
The driver's discovery URL has this shape:
https://YOUR_ORG.okta.com/oauth2/default/.well-known/openid-configuration?client_id=YOUR_CLIENT_ID
Verify it returns metadata for the intended issuer and reachable endpoints. With the installed binary, check and run:
./bin/authcrunch adapt --adapter caddyfile --config Caddyfile >/dev/null
./bin/authcrunch run --config Caddyfile
Adaptation checks configuration syntax; startup and a real login check the
provider. The example disables the admin endpoint, so use Ctrl+C and restart
after changes. Replace the protected respond with your application's
reverse_proxy when the allow/deny checks pass.
Verify sign-in and access
- Open
https://auth.example.com/app, choose Okta, and sign in as the member. - Select Example app from the portal if you are not sent there automatically.
Expect
You reached the Okta-protected app. - Open My identity at
/auth/whoami. Confirmrealm: okta,app-members, andapp/memberin the roles. - In a fresh browser session, use the assigned nonmember. Login should complete,
but
/appmust return 403. A group with a similar name is not sufficient. - Remove the member from the group and repeat with a fresh portal session to confirm the policy no longer admits that user.
Existing portal tokens retain their recorded roles; this example uses a
900-second lifetime. /auth/logout ends the portal session, not the Okta SSO
session. Listing that URL as an Okta logout redirect does not make AuthCrunch
call Okta's end-session endpoint.
Using the org authorization server
For an org authorization server, use the
generic OIDC driver with issuer
https://YOUR_ORG.okta.com and its root
/.well-known/openid-configuration metadata. Do not invent a server_id to
make the named driver generate those endpoints. Configure the app's ID-token
group claim using Okta's org-server instructions, then verify the returned
email and groups with your actual authorization-code flow.
Troubleshoot
| Symptom | Check |
|---|---|
invalid_client during token exchange | Client ID, secret, and client_secret_post authentication method |
| Scope or policy error in Okta | Selected custom server, groups scope, app assignment, and a matching access-policy rule |
| Email missing during callback | Include email in the ID token; this driver does not fill it from UserInfo |
Portal login works but /app is 403 | ID Token → Always, exact groups claim name and group value, and a fresh login |
| Issuer validation fails | Keep org/custom server and custom-domain issuer settings consistent with discovery |
| Logout seems to sign the user straight back in | The browser's Okta SSO session is separate from its AuthCrunch portal session |
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.
Separate Okta policy layers
Help me understand Okta.
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-0001-okta
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 app assignment, authorization-server access policy, claim inclusion,
and AuthCrunch app ACL as separate decisions. Use an assigned member and
assigned nonmember so a provider rejection is not mistaken for a portal 403.
Compare authorization servers
Help me understand Okta.
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-0001-okta
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 the named driver’s custom authorization-server server_id with
org-server generic OIDC discovery. Explain how issuer and endpoint layout
differ. Ask for my tenant’s actual metadata and consult current official Okta
requirements before discussing product entitlement or console steps.
Read group claim inclusion
Help me understand Okta.
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-0001-okta
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.
Trace requested groups scope, an always-included ID-token group claim,
normalized roles, and a narrow app/member transform. Explain why a group
returned only through UserInfo may not satisfy this named-driver example.
Check the email-presence requirement separately.
Diagnose token exchange
Help me understand Okta.
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-0001-okta
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 invalid_client or a missing group using redacted
client-auth method, issuer/server_id, scope, access policy, and ID-token claim
shape. Explain form-body secret authentication versus Basic. Preserve PKCE,
signature, issuer, audience, and nonce checks.
Design Okta access checks
Help me understand Okta.
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-0001-okta
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.
Build tests for assigned member, assigned nonmember, provider-unassigned
account, lookalike group, reserved-role injection, and changed membership with
fresh login. Explain local logout versus Okta SSO and what local fixtures
cannot establish about live tenant policy.
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/config.go — validates generic and named-driver defaults and endpoint settings.
- go-authcrunch: pkg/idp/oauth/provider.go — loads discovery metadata, provider readiness, and driver setup.
- 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/config_test.go — tests generic/named provider configuration and validation.