Sourcebot logo

    JumpCloud SSO Authentication Failure (copy)

    Recent Chats

    Sign in to access your chat history.

    So I've just managed to get Sourcebot spun up this morning, and upon configuring JC SSO, the request appears to fail. The SB login screen gives a generic "An error occurred during authentication. Please try again" banner.

    Looking at the request flows, I can see the following response from the JC auth request: error=invalid_state&error_description=The+state+is+missing+or+does+not+have+enough+characters+and+is+therefore+considered+too+weak.+Request+parameter+%27state%27+must+be+at+least+be+8+characters+long+to+ensure+sufficient+entropy.&state=

    Answer

    JumpCloud SSO invalid_state Error

    The error you're seeing — invalid_state / The state is missing or does not have enough characters — is not a bug in Sourcebot's code, but almost certainly a configuration issue with the AUTH_SECRET environment variable.

    Root Cause

    Sourcebot uses Auth.js (NextAuth.js) for authentication. During an OIDC flow (like JumpCloud SSO), Auth.js generates a cryptographic state parameter that it sends to the identity provider as a CSRF protection measure. This state is derived from the AUTH_SECRET value.

    The default AUTH_SECRET in the docker-compose.yml is a placeholder value of all zeros docker-compose.yml:21-21:

    1AUTH_SECRET=${AUTH_SECRET:-000000000000000000000000000000000}

    If you haven't overridden AUTH_SECRET with a properly generated random secret, Auth.js will produce a weak or degenerate state value. JumpCloud is stricter than some other OIDC providers and enforces a minimum of 8 characters of entropy on the state parameter — hence the rejection.

    How to Fix

    1. Generate a proper AUTH_SECRET using the command suggested in the docker-compose comment:

      1openssl rand -base64 33
    2. Set AUTH_SECRET in your environment (either in a .env file or directly in your docker-compose.yml override) with the generated value.

    3. Also verify AUTH_URL is set correctly docker-compose.yml:20-20. It must match the externally accessible URL of your Sourcebot instance (e.g., https://sourcebot.coolcorp.com), not the default http://localhost:3000. Auth.js uses this to construct the callback URL and state validation. A mismatch here can also cause state issues.

    4. Restart Sourcebot after making changes.

    Verifying Your JumpCloud Configuration

    Make sure your JumpCloud SSO app is configured per the docs idp.mdx:527-568:

    • SSO type is set to "OIDC"
    • Redirect URI is <your_AUTH_URL>/api/auth/callback/jumpcloud
    • Login URL is <your_AUTH_URL>/login
    • The issuer is typically https://oauth.id.jumpcloud.com

    And your Sourcebot config file includes the JumpCloud identity provider idp.mdx:546-565:

    1{
    2 "identityProviders": [
    3 {
    4 "provider": "jumpcloud",
    5 "purpose": "sso",
    6 "clientId": { "env": "JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_ID" },
    7 "clientSecret": { "env": "JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_SECRET" },
    8 "issuer": { "env": "JUMPCLOUD_IDENTITY_PROVIDER_ISSUER" }
    9 }
    10 ]
    11}

    The JumpCloud provider is created as a generic OIDC provider in sso.ts:437-447, which relies on Auth.js's standard OIDC state handling — so the fix is entirely on the configuration side.

    Summary

    CheckWhat to verify
    AUTH_SECRETMust be a strong random string (≥33 chars), not the default zeros
    AUTH_URLMust match your external Sourcebot URL exactly
    JumpCloud redirect URIMust be <AUTH_URL>/api/auth/callback/jumpcloud
    JumpCloud issuer env varTypically https://oauth.id.jumpcloud.com
    docker-compose.yml
    idp.mdx
    sso.ts

    AUTH_SECRET is set to a suitable value.

    However the login attempts are still failing. It looks like the generated request URL for JumpCloud doesn't include the state param. Example:

    1https://oauth.id.jumpcloud.com/oauth2/auth?response_type=code&client_id=804deecf-d**redacted**&redirect_uri=https%3A%2F%2Fsourcebot.**redacted**%2Fapi%2Fauth%2Fcallback%2Fjumpcloud&code_challenge=**redacted**&code_challenge_method=S256&scope=openid+profile+email

    Answer

    Root Cause: Missing checks in createJumpCloudProvider

    This is a bug in Sourcebot's createJumpCloudProvider implementation. The state parameter is absent from the authorization URL because the JumpCloud provider is defined as a plain object without a checks field, which tells Auth.js which security mechanisms to use during the OIDC flow.

    The Problem

    The createJumpCloudProvider function sso.ts:437-447 returns a bare object with just the minimum fields:

    1{
    2 id: 'jumpcloud',
    3 name: "JumpCloud",
    4 type: "oidc",
    5 clientId: clientId,
    6 clientSecret: clientSecret,
    7 issuer: issuer,
    8 allowDangerousEmailAccountLinking: ...
    9}

    Auth.js uses the checks array on a provider config to decide what to include in the authorization URL. Without it explicitly set, Auth.js defaults to ["pkce"] only for OIDC providers — meaning it generates a code_challenge and code_challenge_method (which you can see in your URL), but omits the state parameter, since "state" is not in the checks list.

    JumpCloud's OIDC implementation requires the state parameter to be present and at least 8 characters long.

    Compare With a Working Provider

    Compare with createAuthentikProvider sso.ts:427-435 or createOktaProvider sso.ts:306-314 — these use the official next-auth provider wrappers (e.g. Authentik(...), Okta(...)) which include proper defaults like checks: ["pkce", "state"] internally. The JumpCloud provider uses a raw object and misses this.

    The Fix

    The createJumpCloudProvider function needs checks: ["pkce", "state"] added to the provider object sso.ts:437-447:

    1const createJumpCloudProvider = (clientId: string, clientSecret: string, issuer: string): Provider => {
    2 return {
    3 id: 'jumpcloud' satisfies IdentityProviderType,
    4 name: "JumpCloud",
    5 type: "oidc",
    6 clientId: clientId,
    7 clientSecret: clientSecret,
    8 issuer: issuer,
    9 checks: ["pkce", "state"], // ← ADD THIS
    10 allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true',
    11 } as Provider;
    12}

    This will cause Auth.js to include both the code_challenge (PKCE) and the state parameter in the redirect URL sent to JumpCloud, satisfying JumpCloud's entropy requirement.

    Workaround Until Fixed

    If you cannot patch the code directly, you could raise this as a bug with the Sourcebot team referencing the createJumpCloudProvider function sso.ts:437-447 — the fix is a one-line addition and straightforward.

    sso.ts

    So the changes suggested have been made, however it's still failing with:

    Answer

    It looks like your message got cut off! Could you share the error you're seeing after applying the fix? For example:

    • The error message from the Sourcebot login screen
    • Any relevant request/response from the JumpCloud auth flow (e.g. the authorization URL, error codes, or response body)
    • Any server-side logs from the Sourcebot container if available (e.g. docker logs <container>)
    No file references found
    1[web] | [auth][details]: {
    2[web] | "error": "invalid_client",
    3[web] | "error_description": "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Cli
    4ent supports client authentication method 'client_secret_post', but method 'client_secret_basic' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_
    5method' value to accept 'client_secret_basic'.",
    6[web] | "provider": "jumpcloud"
    7[web] | }

    Answer

    Root Cause: Missing client_secret_post Auth Method

    Good progress — the state fix is working (JumpCloud is now getting far enough to attempt token exchange), but now the token endpoint authentication method is mismatched.

    By default, Auth.js sends client credentials using client_secret_basic (i.e., base64-encoded in the Authorization HTTP header). JumpCloud expects client_secret_post (i.e., credentials sent in the POST body).

    Interestingly, the exact same fix already exists in the codebase for the Bitbucket Server provider via the client field sso.ts:381-381:

    1client: { token_endpoint_auth_method: "client_secret_post" },

    The Fix

    Add the same client field to createJumpCloudProvider sso.ts:437-448:

    1const createJumpCloudProvider = (clientId: string, clientSecret: string, issuer: string): Provider => {
    2 return {
    3 id: 'jumpcloud' satisfies IdentityProviderType,
    4 name: "JumpCloud",
    5 type: "oidc",
    6 clientId: clientId,
    7 clientSecret: clientSecret,
    8 issuer: issuer,
    9 checks: ["pkce", "state"],
    10 client: { token_endpoint_auth_method: "client_secret_post" }, // ← ADD THIS
    11 allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true',
    12 } as Provider;
    13}

    This instructs Auth.js's underlying openid-client library to send the client_id and client_secret as POST body parameters during the token exchange, which is what JumpCloud's token endpoint requires.

    Summary of All Fixes So Far

    FixField AddedResolves
    1checks: ["pkce", "state"]Missing state param in auth URL
    2client: { token_endpoint_auth_method: "client_secret_post" }invalid_client on token exchange

    Both fields together should get you through the full OIDC flow with JumpCloud.

    sso.ts

    It was possible to "fix" this by updating the JC app to use client_secret_basic :)

    Answer

    That's a completely valid alternative fix! Both approaches are equivalent — you're just making the two sides agree on the same auth method:

    ApproachWhere the change is made
    Add client: { token_endpoint_auth_method: "client_secret_post" } to SourcebotSourcebot adapts to JumpCloud's default
    Change JumpCloud app to use client_secret_basicJumpCloud adapts to Auth.js's default

    Changing it on the JumpCloud side is arguably the less invasive option since it doesn't require a code change or redeployment of Sourcebot.

    Is the login flow fully working now end-to-end, or are you still hitting issues further along in the flow?

    No file references found

    login flow is fully working :)

    Answer

    That's great news! 🎉

    To summarise what was needed to get JumpCloud SSO working with Sourcebot:

    1. AUTH_SECRET must be set to a strong random value (e.g. openssl rand -base64 33) — the default placeholder breaks state generation
    2. checks: ["pkce", "state"] needs to be added to createJumpCloudProvider sso.ts:437-447 — without it, Auth.js omits the state param from the authorization URL, which JumpCloud rejects
    3. Token endpoint auth method — either add client: { token_endpoint_auth_method: "client_secret_post" } to the Sourcebot provider config, or configure the JumpCloud app to use client_secret_basic (your chosen approach)

    It would be worth raising a bug/PR against Sourcebot for point #2 specifically — the missing checks field in createJumpCloudProvider sso.ts:437-447 is a genuine bug that will affect any user trying to set up JumpCloud SSO out of the box, regardless of how they handle the auth method question.

    sso.ts

    Answer

    Great — the fix from your debugging session has already landed in the codebase! Commit 9921abce sso.ts:437-448 (authored by you — fatmcgav) merged PR #1020, which added checks: ["pkce", "state"] to createJumpCloudProvider:

    1checks: ["pkce", "state"],

    So anyone pulling the latest version of Sourcebot will now have the state fix included out of the box sso.ts:445-445.

    The one thing that hasn't been upstreamed yet is the client_secret_post vs client_secret_basic discrepancy — but as you worked around that by configuring JumpCloud to use client_secret_basic, it's arguably not a Sourcebot bug, just a JumpCloud default that differs from Auth.js's default. Either way, future JumpCloud SSO users benefit from the state fix already being in place. Nice work getting it merged! 🎉

    sso.ts

    Answer

    That's great — PR #1022 has also landed! The JumpCloud docs section idp.mdx:523-568 now exists with full setup instructions covering:

    • JumpCloud SSO app configuration — OIDC type, redirect URI (<sourcebot_url>/api/auth/callback/jumpcloud), login URL idp.mdx:529-538
    • Environment variables to set for CLIENT_ID, CLIENT_SECRET, and ISSUER idp.mdx:539-541
    • Identity provider config snippet for the Sourcebot config file idp.mdx:546-565
    • A note that the issuer URL is typically https://oauth.id.jumpcloud.com idp.mdx:537-537

    So between PR #1020 (code fix) and PR #1022 (docs), future JumpCloud SSO users should have a much smoother experience than you did this morning! 🎉 Great contributions.

    idp.mdx

    This chat is read-only.