Test OIDC Workspace Access with Keycloak
Use a local Keycloak container to test OIDC group-to-workspace mappings without creating an account with a hosted identity provider.
License requirement
End-to-end OIDC login on self-hosted Dagu requires an active self-host license with SSO enabled. Workspace mapping does not require a separate license beyond the OIDC/SSO feature.
This guide creates:
- a Keycloak realm and confidential OIDC client
- a
groupsclaim in the ID token - one user mapped to a Dagu workspace
- one unmapped user for testing the
nonefallback and grant revocation
This setup is for local testing only. Keycloak development mode and the example credentials are not suitable for production.
Prerequisites
- Docker
- a Dagu binary or source checkout
- port
8080available for Dagu - an active self-host license for OIDC/SSO
1. Start Keycloak
Run Keycloak on port 8081 so it does not conflict with Dagu:
docker run --name dagu-keycloak -d \
-p 127.0.0.1:8081:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
-v dagu-keycloak-data:/opt/keycloak/data \
quay.io/keycloak/keycloak:latest \
start-devOpen http://localhost:8081/admin and sign in with admin / admin.
The named volume preserves the test realm when the container stops. Use docker stop dagu-keycloak and docker start dagu-keycloak for subsequent sessions.
2. Create a Realm
- Open the realm selector in the upper-left corner.
- Choose Create realm.
- Enter
daguas the realm name. - Click Create.
Confirm that the realm selector shows dagu before continuing. Clients, groups, and users created in the master realm are not available in the dagu realm.
3. Create the Dagu Client
- Open Clients.
- Click Create client.
- Select OpenID Connect as the client type.
- Enter
dagu-clientas the client ID. - Click Next.
- Enable Client authentication.
- Keep Standard flow enabled.
- Click Next.
- Set Valid redirect URIs to
http://localhost:8080/oidc-callback. - Set Web origins to
http://localhost:8080. - Click Save.
Copy the Client Secret
- Open Clients and select
dagu-client. - Open the Credentials tab.
- Copy the value under Client secret.
If the Credentials tab is missing, return to Settings, enable Client authentication, and save the client.
Regenerating the secret immediately invalidates the previous value. Update Dagu whenever the secret is regenerated.
4. Add Groups to the ID Token
Add a regular realm-group mapper to the client's dedicated scope:
- Open Clients and select
dagu-client. - Open the Client scopes tab in the client details.
- Find the row described as Dedicated scope and mappers for this client.
- Click the linked scope name in that row. Do not depend on it having a particular generated name.
- Open the Mappers tab.
- Click Configure a new mapper, or Add mapper > By configuration.
- Select Group Membership. Do not select Organization Group Membership.
- Configure the mapper:
| Setting | Value |
|---|---|
| Name | groups |
| Token Claim Name | groups |
| Full group path | Enabled |
| Add to ID token | Enabled |
| Add to access token | Enabled, optional |
| Add to userinfo | Enabled, optional |
- Click Save.
With Full group path enabled, a top-level group named payments-team appears in the token as /payments-team. Dagu's mapping key must include the same leading slash.
5. Create Groups and Test Users
Create the mapped group
- Open Groups.
- Click Create group.
- Enter
payments-team. - Click Create.
Create a mapped user
- Open Users.
- Click Add user.
- Enter
payments-useras the username. - Enter an email address and enable Email verified.
- Click Create.
- Open Credentials and set a password.
- Disable Temporary before saving the password.
- Open the user's Groups tab.
- Click Join group and select
payments-team.
Create an unmapped user
Repeat the user steps with the username unmapped-user, but do not add this user to a group. This user verifies the behavior of default_workspace_access: none.
6. Configure Dagu
Add the following to the Dagu configuration, replacing the client secret:
auth:
mode: builtin
builtin:
token:
secret: "local-test-token-secret"
initial_admin:
username: admin
password: local-admin-password
oidc:
client_id: dagu-client
client_secret: "copy-from-keycloak-credentials"
client_url: "http://localhost:8080"
issuer: "http://localhost:8081/realms/dagu"
scopes:
- openid
- profile
- email
role_mapping:
groups_claim: groups
workspace_mappings:
/payments-team:
- workspace: payments
role: developer
default_workspace_access: none
role_attribute_strict: false
skip_org_role_sync: falseThe example creates the builtin administrator at startup. If initial_admin is omitted, open /setup and create the administrator before using OIDC.
Start Dagu:
dagu start-allWhen testing a source checkout, build the current branch and run its binary instead:
make build
.local/bin/dagu start-all7. Create the Test Workspace
Sign in with an existing Dagu administrator or the configured initial administrator.
- Open the workspace selector.
- Choose New workspace.
- Enter
payments. - Press Enter.
A mapping may reference a workspace that does not exist yet, but the grant remains dormant until the workspace is created.
8. Verify the Mapping
Use a private browser window for each test account to avoid reusing an existing Keycloak or Dagu session.
Mapped user
- Open
http://localhost:8080. - Choose the OIDC/SSO login option.
- Sign in as
payments-user. - Confirm that the workspace selector includes
payments. - Confirm that the account has
developerpermissions insidepaymentsand does not receive grants for other named workspaces.
Workspace-scoped users retain the global viewer role. Resources without a workspace label remain governed by that global role, so use workspace-labelled workflows when checking isolation.
Unmapped user
- Sign out from both Dagu and Keycloak, or use another private browser session.
- Sign in as
unmapped-user. - Confirm that login succeeds.
- Confirm that no named workspace grant is assigned.
As an administrator, open the Dagu users page and edit unmapped-user. The account should show Managed by SSO. Rename, disable, and re-enable remain available, but password reset does not. Use the builtin administrator for recovery.
Grant revocation and restoration
- In Keycloak, remove
payments-userfrompayments-team. - Sign out and sign in to Dagu again as
payments-user. - Confirm that the
paymentsgrant is removed. - Add the user back to
payments-teamin Keycloak. - Sign in to Dagu again and confirm that the grant returns.
With skip_org_role_sync: false, Dagu applies membership changes at the next OIDC login. If it cannot save the new policy, the login is rejected. See OIDC workspace access for session behavior and failure handling.
Verify the Token Claim
The generated ID token must contain the exact group path used in the Dagu configuration:
{
"groups": ["/payments-team"]
}In the Keycloak client-scope view, use the token evaluation view when available, select payments-user, and inspect the generated ID token. A real login token is authoritative if the evaluation view differs from the configured login flow.
Troubleshooting
The Credentials tab is missing
Enable Client authentication under Clients > dagu-client > Settings, then save the client.
The dedicated scope name is not visible
Open Clients > dagu-client > Client scopes and find the row described as Dedicated scope and mappers for this client. Click the link in that row; the generated name can vary.
The groups claim is missing
Check all of the following:
- the mapper type is Group Membership
- Add to ID token is enabled
- the mapper is attached to
dagu-client - the user joined the group in the
dagurealm - the login used a new session after the mapper or membership changed
Login succeeds but the workspace grant is missing
- Compare the ID-token group value with the
workspace_mappingskey exactly. Group names are case-sensitive. - Include the leading slash when Full group path is enabled.
- Confirm that a
role_attribute_pathorgroup_mappingsentry did not grant a global role first. Global mappings take precedence over workspace mappings. - Confirm that the named workspace exists if the grant appears configured but remains dormant.
A membership change is not reflected
- Sign out from Dagu and Keycloak, then start a new OIDC login. Ordinary Dagu API requests do not query Keycloak groups.
- Confirm the new ID token contains the updated
groupsclaim; an existing Keycloak session may reuse older authentication state. - If
skip_org_role_syncistrue, authorization is intentionally kept from the first login.
An existing user is denied after enabling strict mode
role_attribute_strict: true also applies to users already stored in Dagu. Decode the user's current ID token and confirm that at least one global or workspace mapping matches exactly. Dagu does not retain the previous authorization when strict mapping has no match.
The callback is rejected
Confirm that the Keycloak client allows exactly:
http://localhost:8080/oidc-callbackNext Steps
- OIDC Workspace Access explains evaluation order, strict mode, fallbacks, and synchronization.
- Keycloak OIDC Setup covers general Keycloak deployment configuration.
- Workspaces explains workspace creation and workflow labels.
- Keycloak container guide documents development and production container modes.
- Keycloak protocol mapper reference documents the Group Membership mapper fields.

