Single Sign-On Setup¶
JIM requires an OIDC-compliant identity provider for authentication. This page is the high-level guide to configuring Single Sign-On (SSO): what JIM needs from your identity provider, how the client model works, and how to test and troubleshoot the result. The provider-specific, step-by-step setup lives on its own page per provider (see Choose your identity provider below).
Before you begin
Ensure you have:
- JIM deployed and accessible (see Deployment Guide)
- Administrative access to your identity provider
- The URL where JIM is hosted (e.g.
https://jim.example.com)
JIM serves both the web interface and API from a single application. The API is available at /api/ on the same host.
Choose your identity provider¶
JIM works with any OIDC-compliant identity provider. Pick the guide that matches yours for exact, click-by-click setup instructions:
-
App registration, client secret, exposed API scope, and PowerShell public-client platform, all on a single registration.
-
Application Group with a web application, native (PowerShell) application, and Web API, plus the claim rules to emit standard OIDC claims.
-
Realm, confidential web client, API client scope, and a separate public client for the PowerShell module.
Using a different provider? The concepts on this page (a confidential client for the web application, an optional public client for interactive tooling, an API scope for token validation, and the JIM_SSO_* environment variables) apply to any OIDC provider. Follow the closest guide above as a template and substitute your provider's terminology.
Whichever provider you use, JIM is configured through a small set of JIM_SSO_* environment variables; the per-provider guides show the exact values to set. For the full list and defaults, see the Configuration Reference.
Confidential vs Public Clients¶
JIM uses two kinds of OAuth 2.0 clients, and depending on your identity provider they may be the same registration or two separate ones. Understanding which is which makes the per-provider steps easier to follow.
| Client | Used by | Authentication | Configured via |
|---|---|---|---|
| Confidential | The JIM web application (Blazor UI) | A stored client secret sent directly from the backend to the IdP's token endpoint. Never exposed to the browser. | JIM_SSO_CLIENT_ID and JIM_SSO_SECRET |
| Public | Interactive clients that run on the user's machine (PowerShell module, future CLI tools) | PKCE with a loopback redirect URI (http://localhost:8400/callback/). No secret; the IdP relies on PKCE to bind the authorisation response to the original client. |
JIM_SSO_PUBLIC_CLIENT_ID (falls back to JIM_SSO_CLIENT_ID when unset) |
When are these the same registration?
- Microsoft Entra ID: One app registration can have both a Web platform (for the confidential flow) and a Mobile/Desktop platform (for the public flow). In this case, leave
JIM_SSO_PUBLIC_CLIENT_IDunset; the PowerShell module uses the same Application (client) ID as the web application.
When must they be different?
- AD FS: A Server application (confidential) and a Native application (public) are distinct applications with their own Client Identifiers, even inside a single Application Group. Create both and set
JIM_SSO_PUBLIC_CLIENT_IDto the Native application's identifier. - Keycloak: A single Keycloak client cannot be both confidential and public. You must create two clients (e.g.
jimandjim-powershell) and setJIM_SSO_PUBLIC_CLIENT_IDto the public one. - Any IdP where your security policy forbids adding loopback redirects to a confidential client: create a dedicated public client and point
JIM_SSO_PUBLIC_CLIENT_IDat it.
What does JIM do with these values?
The JIM server exposes /api/v1/auth/config, an unauthenticated discovery endpoint that interactive clients call to learn how to authenticate. It returns:
authority: the OIDC authority URL (JIM_SSO_PUBLIC_AUTHORITYif set, elseJIM_SSO_AUTHORITY)clientId: the client ID for public/PKCE flows (JIM_SSO_PUBLIC_CLIENT_IDif set, elseJIM_SSO_CLIENT_ID)scopes: the OAuth scopes to request:openid,profile,offline_access, and (when set)JIM_SSO_API_SCOPE
Backend token validation (the JWT bearer middleware that protects /api/** endpoints) always uses JIM_SSO_AUTHORITY for issuer and JWKS, and JIM_SSO_API_SCOPE for the audience, regardless of which public client issued the token. As long as the public and confidential clients belong to the same realm/tenant and both request the same API scope, tokens from either are valid.
Why offline_access?
JIM requests the offline_access scope so the identity provider issues a refresh token. The PowerShell module uses this for two things: silent in-session token renewal (so long-running sessions don't expire mid-task), and optional cross-session token persistence (so opening a new terminal doesn't require re-authenticating in the browser). The refresh token is the only credential persisted, and it is stored in the operating system's credential store (Credential Manager on Windows, login Keychain on macOS, libsecret on Linux); never in plain text. Your public client must be permitted to receive offline_access. Most identity providers grant it to public clients by default; the per-provider guides note where it must be enabled explicitly.
Testing Your Configuration¶
Once you have completed the setup for your provider, use these steps to verify sign-in, claims, sign-out, and API access.
1. Verify OIDC Discovery¶
Test that JIM can reach your identity provider's discovery document:
You should see a JSON response with endpoints for authorization_endpoint, token_endpoint, etc.
2. Start JIM and Test Login¶
- Start JIM with your configuration
- Navigate to the JIM web interface
- Click Login
- You should be redirected to your identity provider
- After authentication, you should be redirected back to JIM
3. Verify Claims¶
- After logging in, navigate to
/claims - Verify you see the expected claims:
sub-- Your unique identifiername-- Your display nameemail-- Your email address
4. Test Sign-Out¶
- With an active JIM session, open the user menu in the navigation drawer (bottom-left)
- Click Sign out and confirm the dialog
- You should be redirected briefly to your identity provider's end-session endpoint, then returned to JIM
- You should end up signed out of JIM; reopening a protected page should redirect you to your identity provider to sign in again
Scope of sign-out
Sign-out clears the JIM session cookie and notifies the identity provider via RP-initiated logout (OIDC end_session_endpoint). Whether the user is also signed out of other applications that share the same identity provider session depends entirely on the identity provider's single sign-out configuration, not JIM.
Hiding the sign-out button
For deployments where users cannot realistically sign out of their enterprise-managed SSO session (for example, on domain-joined devices with seamless SSO), administrators can hide the sign-out button entirely by setting the SSO.EnableLogOut service setting to false. See Service Settings in the configuration reference.
5. Test the API¶
In production, the recommended way to test API access with SSO is via the JIM PowerShell module (see step 6 below), which supports interactive browser-based authentication.
If you are configuring SSO against a non-production JIM instance and configured a Scalar single-page-application redirect URI on your identity provider (see Microsoft Entra ID Step 5a for an example), you can also test the API interactively via the Scalar API reference:
- Navigate to
https://your-jim-dev-url/api/reference - Select an endpoint and choose the OAuth2 security scheme
- Log in with your identity provider
- Try an API endpoint (e.g.
GET /api/v1/health)
6. Test the PowerShell Module¶
If you configured PowerShell module authentication:
# Import the module
Import-Module JIM
# Connect interactively -- opens browser for SSO
Connect-JIM -Url "https://your-jim-url"
# Verify the connection
Test-JIMConnection
# You should see:
# Connected : True
# Url : https://your-jim-url
# AuthMethod : OAuth
# Status : Healthy
# Message : Connection successful
# TokenExpiresAt : <date/time>
Sign-Out Troubleshooting¶
Sign-out uses OIDC RP-initiated logout: JIM clears its local session cookie, then redirects the browser to the identity provider's end_session_endpoint with an id_token_hint and a post_logout_redirect_uri. The identity provider ends its session and redirects the browser back to JIM.
The most common failure modes and how to fix them:
- "AADSTS50011: The reply URL specified in the request does not match..." (Entra ID)
- The post-logout URI (
https://your-jim-url/signout-callback-oidc) is not registered against the app. Add it to Authentication > Platform configurations > Web > Redirect URIs and save. See Entra ID Step 1. - "Invalid post_logout_redirect_uri" or similar (AD FS, Keycloak)
- Same root cause as above: the URI is not in the application's registered redirect URI list. For AD FS, add
https://your-jim-url/signout-callback-oidcto the Server application's Redirect URIs (see AD FS Step 2). For Keycloak, add it to Valid post logout redirect URIs on the client (see Keycloak Step 2). - "Missing parameters: id_token_hint" (Keycloak and other strict providers)
- Keycloak and other strict OIDC providers require
id_token_hinton the end-session request per the OIDC specification. JIM persists the ID token during sign-in automatically so the middleware can include it on sign-out. If you see this error, verify your identity provider is actually issuing an ID token (theopenidscope must be requested, which JIM does by default) and that your client configuration is not stripping it. - Sign-out appears to succeed, but refreshing the page keeps you signed in
- This usually means the identity provider still has an active single sign-on session and is silently re-authenticating JIM via the
prompt=noneflow (or cached browser credentials). This is working as designed for SSO. To test a full sign-out flow, use a private/incognito browser window, or sign out of the identity provider session directly. - The sign-out button is missing from the user menu
- The sign-out button is gated by the
SSO.EnableLogOutservice setting, which is enabled by default. See Service Settings in the configuration reference.
Security Considerations¶
Token Lifetimes¶
Configure appropriate token lifetimes in your identity provider:
- Access tokens
5–60 minutes (shorter is more secure) - Refresh tokens
Hours to days, depending on your security requirements - ID tokens
Same as access tokens
Client Secrets¶
- Rotate client secrets regularly
Every 6–12 months is recommended. - Use separate secrets for development and production
Never share credentials across environments. - Never commit secrets to source control
Keep all credentials out of repositories. - Consider using a secrets management solution
E.g. HashiCorp Vault for secret storage.
Redirect URIs¶
- Only register exact redirect URIs
No wildcards allowed. - Use HTTPS in production
Secure connections are required for production deployments. - Remove any development/test URIs
Before going to production, clean up test configurations.
Scopes and Permissions¶
- Request only the minimum scopes needed
Follow the principle of least privilege. - Regularly audit which permissions are granted
Review permissions periodically for security compliance. - Use admin consent for organisation-wide deployments
For widespread deployments, use organization-level consent mechanisms.
Brute-Force Protection and MFA¶
JIM delegates interactive authentication entirely to your identity provider and never sees or stores user credentials, whichever kind your provider is configured to require, i.e. Kerberos, passwords, smart cards, other certificate-based authentication types, FIDO2 passkeys, etc. Defences against credential attacks therefore belong at the identity provider, not in JIM:
- Where password authentication is in use, we recommend you enable brute-force/lockout protection
Entra ID (Smart Lockout), AD FS (the built-in Extranet Lockout Policy), and Keycloak (Realm settings > Security defences > Brute force detection) all offer this. The bundled devcontainer Keycloak realm ships with brute-force detection enabled by default. - Enable multi-factor authentication where available
MFA at the identity provider protects every application behind it, including JIM, without any JIM-side configuration. - Prefer phishing-resistant methods where your provider supports them
Kerberos, certificate-based authentication, and FIDO2 passkeys remove the guessable secret altogether, protecting users against credential-stuffing and password-guessing.
JIM's own REST API rate limiting throttles request volume at the application layer (see Service Settings); it complements, but does not replace, identity-provider-level credential protections for the interactive sign-in flow.