OIDC Integration
jitsudo uses OpenID Connect (OIDC) for all authentication. The CLI authenticates users via the Device Authorization Flow (RFC 8628) and the server validates JWTs issued by your IdP.
How Authentication Works
Section titled “How Authentication Works”- CLI → IdP:
jitsudo loginstarts the device flow, directing the user to authenticate in any browser. - IdP → CLI: After browser authentication, the IdP issues an ID token (JWT).
- CLI → Server: Every API request carries the ID token as a
Bearertoken in theAuthorizationheader. - Server → IdP: jitsudod validates the token by fetching the IdP’s JWKS from
{oidc_issuer}/.well-known/openid-configurationand verifying the JWT signature, issuer (iss), audience (aud), and expiry (exp).
Server Configuration
Section titled “Server Configuration”auth: # Must match the `iss` claim in tokens issued by your IdP. oidc_issuer: "https://your-idp.example.com"
# OIDC client ID registered with your IdP for the jitsudo server. client_id: "jitsudo-server"You need two OIDC clients registered with your IdP:
jitsudo-server— the server’s resource server / audiencejitsudo-cli— the CLI’s public client (uses device flow, hardcoded in the CLI)
Required Scopes
Section titled “Required Scopes”The CLI requests these scopes:
| Scope | Purpose |
|---|---|
openid | Required for OIDC |
email | Used as the user’s identity in requests and audit log |
profile | Display name |
groups | Group membership (used in eligibility/approval policies) |
offline_access | Refresh token (for future silent refresh support) |
Make sure your IdP includes the groups claim in the ID token.
Step 1: Create a Server Application
Section titled “Step 1: Create a Server Application”- In Okta Admin Console: Applications → Create App Integration → OIDC → Web Application.
- Set Grant type: Device Authorization.
- Note the Client ID (
jitsudo-server) — this is yourclient_id. - Set Issuer to your Okta org URL:
https://your-org.okta.com.
Step 2: Create a CLI Application
Section titled “Step 2: Create a CLI Application”- Applications → Create App Integration → OIDC → Native Application.
- Set Grant type: Device Authorization.
- Set Client ID to
jitsudo-cli(or note the generated one and configure it in the CLI source if needed).
Step 3: Add Groups Claim
Section titled “Step 3: Add Groups Claim”In the Sign-On tab of the server app, edit the token settings:
- Add a claim: Name
groups, Include inID Token, Value typeGroups, FilterMatches regex: .*.
Step 4: Configure jitsudod
Section titled “Step 4: Configure jitsudod”auth: oidc_issuer: "https://your-org.okta.com" client_id: "jitsudo-server"Step 5: Log In
Section titled “Step 5: Log In”jitsudo login --provider https://your-org.okta.comMicrosoft Entra ID (Azure AD)
Section titled “Microsoft Entra ID (Azure AD)”Step 1: Register the Server Application
Section titled “Step 1: Register the Server Application”az ad app create \ --display-name "jitsudo-server" \ --identifier-uris "api://jitsudo-server"
# Note the appId — this is your client_idaz ad app show --display-name "jitsudo-server" --query appId -o tsvEnable device flow on the registration:
# In Azure Portal: App registrations → your app → Authentication# Add platform: Mobile and desktop applications# Enable "Allow public client flows"Step 2: Add Groups Claim
Section titled “Step 2: Add Groups Claim”- App registrations → your app → Token configuration → Add groups claim.
- Select Security groups and include in ID Token.
Step 3: Register the CLI Application
Section titled “Step 3: Register the CLI Application”az ad app create --display-name "jitsudo-cli"# Enable device flow and "Allow public client flows"# The appId becomes the CLI's client ID (must match "jitsudo-cli" constant in source)Step 4: Configure jitsudod
Section titled “Step 4: Configure jitsudod”auth: oidc_issuer: "https://login.microsoftonline.com/<TENANT_ID>/v2.0" client_id: "<SERVER_APP_CLIENT_ID>"Step 5: Log In
Section titled “Step 5: Log In”jitsudo login --provider "https://login.microsoftonline.com/<TENANT_ID>/v2.0"Keycloak
Section titled “Keycloak”Step 1: Create a Realm and Clients
Section titled “Step 1: Create a Realm and Clients”- In Keycloak Admin Console, create a realm (e.g.
jitsudo). - Create a client: Clients → Create → Client ID:
jitsudo-server.- Protocol:
openid-connect - Access Type:
confidential(orpublicfor development) - Enable Device Authorization Grant.
- Protocol:
- Create a client: Client ID:
jitsudo-cli.- Protocol:
openid-connect - Access Type:
public - Enable Device Authorization Grant.
- Protocol:
Step 2: Add Groups Mapper
Section titled “Step 2: Add Groups Mapper”For each client, add a mapper:
- Mapper type: Group Membership
- Token Claim Name:
groups - Add to ID token: ON
Step 3: Configure jitsudod
Section titled “Step 3: Configure jitsudod”auth: oidc_issuer: "https://keycloak.example.com/realms/jitsudo" client_id: "jitsudo-server"Step 4: Log In
Section titled “Step 4: Log In”jitsudo login --provider https://keycloak.example.com/realms/jitsudoGoogle Workspace
Section titled “Google Workspace”Step 1: Create OAuth Clients
Section titled “Step 1: Create OAuth Clients”In Google Cloud Console:
- APIs & Services → Credentials → Create Credentials → OAuth client ID.
- Application type: Desktop app (supports device flow via workaround).
- Create one for the server (
jitsudo-server) and one for the CLI (jitsudo-cli).
Dex as a Bridge
Section titled “Dex as a Bridge”Dex federates to Google Workspace and provides standard RFC 8628 device flow:
connectors: - type: google id: google name: Google config: clientID: <GOOGLE_CLIENT_ID> clientSecret: <GOOGLE_CLIENT_SECRET> redirectURI: https://dex.example.com/callback hostedDomains: - your-org.comauth: oidc_issuer: "https://dex.example.com" client_id: "jitsudo-server"Security Considerations
Section titled “Security Considerations”Group claim trust
Section titled “Group claim trust”jitsudo trusts the groups claim in the OIDC ID token without independent verification. This means:
- Your IdP is the authoritative source of group membership
- If an attacker can modify group claims at your IdP (e.g., through a compromised admin account or IdP misconfiguration), they could satisfy approval policies they should not
- Audit your IdP group membership regularly
- Restrict who can modify group assignments in your IdP
Token Lifecycle and Replay
Section titled “Token Lifecycle and Replay”Validation on every API call. jitsudo validates the JWT signature, issuer (iss), audience (aud), and expiry (exp) on every API request by fetching and caching your IdP’s JWKS from {oidc_issuer}/.well-known/openid-configuration. There is no session state held server-side — every request is independently verified against the live JWKS. A token that was valid at login is re-validated on each subsequent call.
What happens when a token expires mid-session. When a JWT expires, the server returns a 401 Unauthorized response. The CLI surfaces this as a re-authentication prompt. There is no automatic silent refresh — offline_access is requested for future refresh token support, but is not currently used. If a token expires while you have a pending approval in flight, re-run jitsudo login and then check jitsudo status — the pending request is unaffected and preserved in the database.
Stolen token replay. A stolen but unexpired JWT can be used to submit elevation requests until it expires. jitsudo has no additional binding (e.g., mTLS client certificate binding) to tie a JWT to a specific device. Mitigations:
- Short JWT lifetimes: Configure 60–120 minute token lifetimes in your IdP. Do not exceed 4 hours for production use. All major IdPs (Okta, Entra ID, Keycloak) support configuring access and ID token lifetimes.
- IdP session revocation: Revoking an IdP session or account blocks new tokens from being issued. If a token is stolen, immediately revoke the IdP session to limit the window.
- Step-up authentication: Configure your IdP to require re-authentication before issuing tokens for sensitive operations. Some IdPs support step-up auth policies tied to specific client IDs or scopes.
Offboarding principals
Section titled “Offboarding principals”Revoking an IdP account or removing group memberships blocks new elevation requests immediately — the next request will fail eligibility evaluation.
However, active grants are not automatically revoked. Always run:
jitsudo status --user departing-engineer@example.com --state activejitsudo revoke <each-active-request-id>See Writing Policies — Principal Lifecycle for the full offboarding procedure.
Troubleshooting
Section titled “Troubleshooting”Token validation fails with iss mismatch
Section titled “Token validation fails with iss mismatch”The oidc_issuer in your config must exactly match the iss claim in the JWT. Fetch a token and inspect it:
# Decode the JWT (base64 decode the middle segment)jitsudo login --provider https://your-idp.example.comcat ~/.jitsudo/credentials | python3 -c "import json, base64, systoken = json.load(sys.stdin)['Token']payload = token.split('.')[1]# Add paddingpayload += '=' * (4 - len(payload) % 4)print(json.dumps(json.loads(base64.b64decode(payload)), indent=2))"Check the iss field and make sure it matches your config exactly.
groups claim missing
Section titled “groups claim missing”If eligibility policies use input.user.groups but the claim is empty, your IdP is not including groups in the ID token. Add the groups claim as described above for your IdP.
Device flow not supported
Section titled “Device flow not supported”Not all IdPs enable device flow by default. Check that:
- The client type is
NativeorPublic(notWeb). - Device authorization grant is explicitly enabled.
- Your IdP’s device authorization endpoint is reachable from the CLI machine.