Skip to main content

Okta

Configure Okta as the Single Sign-On provider for your Kargo instance.

There are two ways to connect Okta. Pick one:

MethodKargo modeOkta objectCredentialGroups from
Native OIDCOIDCOIDC app integration (SPA) + custom Authorization ServerPKCEID-token claim
Dex SAMLDex, saml connectorSAML 2.0 app integrationSigning certificate, inlineSAML assertion

Native OIDC is the simpler of the two at login time: Kargo talks to Okta directly, with no Dex hop and no client secret. It is also the only method documented here for CLI login. This guide builds it on a custom Authorization Server in Okta, which is what gives you the scope-gated groups claim, the regex filter and the access policy used below. Dex SAML routes through Kargo's built-in Dex instance and needs no Authorization Server.

Before you start, you need:

  • An Akuity Platform account with a Kargo instance, for example https://myinstance.kargo.akuity.cloud
  • Admin access to your Kargo instance settings
  • Admin access to an Okta organization, with permission to create app integrations and, for native OIDC, Authorization Servers

Native OIDC​

Connect Okta directly over OIDC with PKCE. Kargo's UI runs the authorization-code flow as a public client, so there is no client secret and no Dex hop.

Switching modes is non-destructive

Kargo stores the Dex configuration and the direct-OIDC fields side by side and uses a flag to select between them, so you can switch between Dex and OIDC without losing either configuration.

For CLI login (Step 7) you also need the kargo CLI installed.

Step 1: Switch to OIDC mode and note the callback URL​

  1. In your Kargo Instance, go to Settings.

  2. In the left panel, search for Access and go to OIDC Config.

    SSO Settings

  1. Toggle Enabled to on.

  2. Select OIDC, not Dex.

  3. The callback URL banner shows:

    https://<your-kargo-hostname>/login

The form changes completely compared with Dex mode: you get Client ID, CLI Client ID, Issuer URL and Additional Scopes instead of a YAML editor and a Secrets tab.

Copy that URL exactly as the banner shows it — it is the value Okta must have registered as a sign-in redirect URI. Leave this page open; you will return to it in Step 8.

Step 2: Create the OIDC app integration for the UI​

  1. In the Okta Admin Console, go to Applications → Applications → Create App Integration.

  2. In the Create a new app integration dialog:

    • Sign-in method: OIDC - OpenID Connect
    • Application type: Single-Page Application
  3. Click Next.

Selecting OIDC and Single-Page Application

Select Single-Page Application, not Web Application

Kargo's UI runs the authorization-code and PKCE exchange from the browser as a public client, with no client secret. Okta's Web Application type expects a client secret; Single-Page Application is the type that supports public-client PKCE with CORS.

Okta SPA integrations do not support client secrets at all. That is the intended shape here — Kargo needs none.

  1. On the New Single-Page App Integration page, fill in:

    FieldValue
    App integration nameA descriptive name, for example kargoakp
    Grant typeAuthorization Code only
    Sign-in redirect URIshttps://<your-kargo-hostname>/login — the callback URL from Step 1
    Sign-out redirect URIshttps://<your-kargo-hostname>
  2. Click Save.

General settings for the SPA integration

caution

The redirect URI must match what Kargo sends in its authorization request, character for character, or Okta rejects the callback with redirect_uri_mismatch. Copy it from the Kargo banner rather than typing it.

Leave Allow wildcard * in sign-in URI redirect unchecked in production.

On the app's General tab, note the Client ID — you will need it in Step 8.

Step 3: Create a custom Authorization Server​

Kargo needs its own Authorization Server so you can define the custom groups scope and claim used for role mapping.

  1. Go to Security → API → Authorization Servers, then click Add Authorization Server.

  2. Fill in:

    FieldValue
    Namekargo
    Audiencehttps://<your-kargo-hostname>
    DescriptionAnything descriptive, for example auth server
  3. Click Save.

Adding the custom Authorization Server

Why a custom Authorization Server rather than the Org one

Okta's built-in Org Authorization Server can emit a groups claim — you configure it on the app itself, under Sign On → OpenID Connect ID Token → Group claim type → Filter, rather than on an Authorization Server. If that covers your needs, it is a shorter path and you can skip Steps 3 to 5.

The Org server is the more limited of the two. Okta does not let you customize its audience, claims, policies, or scopes, and it can put a groups claim in an ID token only, never an access token. A custom Authorization Server gives you the scope-gated groups claim, the regex filter, the access policy and the token lifetimes configured over the next three steps, so that is what the rest of this walkthrough assumes.

The Audience value becomes the aud claim on every token this server issues.

Step 4: Add the groups scope and claim​

Skip this step only if you intend to map Kargo roles on email alone.

Add the groups scope​

  1. Inside the kargo Authorization Server, open the Scopes tab and click Add Scope.

  2. Fill in:

    FieldValue
    Namegroups
    Display phraseFor example grp membership
    DescriptionFor example Allows Kargo to see what group you belong to
    User consentImplicit
  3. Click Create.

Creating the groups scope

Kargo must request this scope at login for the claim to appear, so you will add groups to Additional Scopes in Step 8.

Add the groups claim​

  1. Still inside the kargo Authorization Server, open the Claims tab and click Add Claim.

  2. Fill in:

    FieldValue
    Namegroups
    Include in token typeID Token, Always
    Value typeGroups
    FilterMatches regex, kargo-.*
    Include inThe following scopes → groups
  3. Click Create.

Creating the groups claim

Keep the regex narrow

The kargo-.* filter emits only groups whose names start with kargo-. That keeps the token small and avoids leaking unrelated group memberships to Kargo.

Okta enforces a token size limit. A user who belongs to a large number of matching groups can produce an ID token too large to be accepted, which fails authentication. Adjust the regex to your naming convention, but keep it as specific as you can.

Step 5: Create an access policy​

Access policies control which clients may obtain tokens from this Authorization Server.

  1. Inside the kargo Authorization Server, open the Access Policies tab and click Add Policy.

  2. Fill in:

    FieldValue
    Namekargo
    DescriptionFor example Access to Kargo
    Assign toThe following clients → your UI app, kargoakp
  3. Click Create Policy.

Adding the access policy

caution

Without a policy assigned to your client, Okta refuses every token request with 401 unauthorized_client, even though the app integration exists.

  1. On the policy detail page, click Add Rule and fill in:

    FieldValue
    Rule NameAnything, for example default
    IF Grant type isAuthorization Code
    AND User isAny user assigned the app
    AND Scopes requestedAny scopes
    AND Access token lifetimeFor example 6 hours
    AND Refresh token lifetimeFor example 5 days
  2. Click Create Rule.

Adding a rule to the access policy

The access token lifetime here does not set how long a Kargo UI session lasts — that follows the ID token lifetime and Kargo's own session settings. Tune both to your organization's security policy.

Step 6: Create Okta groups and assign users​

Kargo maps users to roles through the groups claim, so create one Okta group per Kargo role.

  1. Go to Directory → Groups → Add group.

  2. Name the group so it matches the claim regex from Step 4, for example kargo-akpt1, and click Save.

Creating an Okta group

Settle on a naming convention first

Something like kargo-<project>-admin and kargo-<project>-viewer scales well. Renaming groups later means updating both Okta and every Kargo role mapping that referenced the old name.

A group whose name does not match the regex never reaches the token, so its members get no Kargo role even though the group membership is correct in Okta.

  1. Open the group, click Assign People, then click + next to each user you want to add.

Assigning people to the group

A status of "Pending user action" means the user has been assigned but has not yet activated their Okta account. They receive the group claim once the account is active.

  1. Open the group's Applications tab, click Assign applications, and select your UI app, kargoakp.

Assigning the application to the group

caution

Skipping this assignment is a common cause of User is not assigned to the client application. Group membership alone grants nothing — Okta blocks the login unless the user is also assigned to the app.

Step 7: Create a second app integration for the CLI​

kargo login --sso runs the same authorization-code and PKCE flow, but it starts a temporary local HTTP server and receives the callback on a loopback URL. Okta matches redirect URIs exactly, including the port, so the CLI needs its own app integration.

  1. Go to Applications → Applications → Create App Integration, select OIDC - OpenID Connect and Single-Page Application, then click Next.

  2. Fill in:

    FieldValue
    App integration namekargoakpcli
    Grant typeAuthorization Code only
    Sign-in redirect URIshttp://localhost:54321/auth/callback
    Sign-out redirect URIshttp://localhost:54321
  3. Click Save, then note the Client ID from the General tab.

Creating the CLI app integration

Include the port, and pass the same port to the CLI

Okta does not treat loopback redirect URIs specially: http://localhost/auth/callback and http://localhost:54321/auth/callback are different URIs. Register a fixed port here and pass it to the CLI with --port, otherwise the CLI picks a random port and the callback fails with redirect_uri_mismatch.

  1. On the kargoakpcli app's Assignments tab, click Assign → Assign to Groups and select the groups that need CLI access, for example kargo-akpt1.

Assigning the CLI app to the group

  1. Go back to Security → API → Authorization Servers → kargo → Access Policies, click Edit on the kargo policy, add kargoakpcli to Assign to alongside kargoakp, and click Update Policy.

Adding the CLI client to the access policy

Step 8: Complete the Kargo OIDC configuration​

First get the issuer URL: in Okta, go to Security → API → Authorization Servers and read the Issuer URI column for your kargo server. It looks like https://<your-okta-domain>/oauth2/<authServerId>.

The Issuer URI of the custom Authorization Server

Back on the Kargo OIDC Config page with OIDC mode selected, fill in:

FieldValue
Client IDThe Client ID of the UI app, kargoakp, from Step 2
CLI Client IDThe Client ID of the CLI app, kargoakpcli, from Step 7
Issuer URLhttps://<your-okta-domain>/oauth2/<authServerId>
Additional Scopesgroups

The completed Kargo OIDC configuration

Neither app has a client secret, and neither needs one — both use PKCE.

Set CLI Client ID explicitly

CLI Client ID is optional, and when it is blank Kargo falls back to the UI Client ID. With Okta that fallback fails, because the UI app has no http://localhost:54321/auth/callback redirect URI registered. Always set it to the CLI app's Client ID.

Step 9: Map users to Kargo roles​

Scroll to Kargo Roles and Permissions. For the Admin role:

  • Claim key email, claim value your email address, then click Add
  • Or claim key groups, claim value kargo-akpt1, then click Add

Available roles are Admin, Viewer, User, and Project Creator.

caution

Claim matching is exact string equality — no wildcards, no substring matching, no case folding. The value must be the Okta group name exactly as it appears in the token. If you later change your group naming convention, every existing mapping must be rewritten.

Click Save.

Step 10: Test the UI and CLI logins​

  1. Open a fresh incognito or private window and go to https://<your-kargo-hostname>.

  2. Click SSO Login, sign in with your Okta account, and you land in the Kargo UI with the role you mapped.

Then test the CLI, using the same port you registered in Step 7:

kargo login https://<your-kargo-hostname> --port 54321 --sso

Verify you are authenticated:

kargo get projects
Verify it really is native OIDC with PKCE

The redirect URL should contain:

<your-okta-domain>/oauth2/<authServerId>/v1/authorize?…&code_challenge=…&code_challenge_method=S256&redirect_uri=…%2Flogin

code_challenge_method=S256 confirms PKCE, and a redirect_uri ending in /login with no /dex/callback anywhere confirms Dex is bypassed.

Native OIDC troubleshooting​

redirect_uri_mismatch on the UI login

The sign-in redirect URI registered on kargoakp does not match what Kargo sent. Read the redirect_uri parameter out of the browser URL when you are redirected to Okta — that is the exact value Okta must have registered. Watch for a trailing slash or a stray character pasted in with the URL.

redirect_uri_mismatch on kargo login --sso

Either --port does not match the port registered on kargoakpcli, or CLI Client ID is blank in Kargo so the CLI is using the UI client. Register http://localhost:<port>/auth/callback on the CLI app, set CLI Client ID, and pass the same --port.

401 unauthorized_client

The client is not assigned to an access policy on the kargo Authorization Server. Add it under Access Policies — both kargoakp and kargoakpcli need to be listed.

Login succeeds but the user has no permissions

No role mapping matched. Check Kargo Roles and Permissions: the claim key and value must match the token exactly. Decode the ID token and compare the values character for character.

groups claim missing

  • Confirm groups is in Additional Scopes in Kargo — the claim is scoped to groups in Step 4, so it is only emitted when the scope is requested
  • Confirm the Issuer URL points at the custom Authorization Server you configured, not the Org one — the claim and scope in Step 4 live on the custom server
  • Confirm the group name matches the claim's regex filter
  • Confirm the user is assigned to the app, not only to the group

User is not assigned to the client application

Assign the group to the app under the group's Applications tab (Step 6), or assign the user directly on the app's Assignments tab.

Login fails for users in many groups

The ID token has outgrown Okta's size limit. Narrow the claim's regex filter so fewer groups are emitted.

Native OIDC summary​

ComponentValue
Okta UI appOIDC app integration, Single-Page Application
UI redirect URIhttps://<kargo-hostname>/login
Okta CLI appOIDC app integration, Single-Page Application
CLI redirect URIhttp://localhost:<port>/auth/callback — the port is required
Authorization ServerCustom, for example kargo
Kargo SSO modeOIDC, not Dex
Issuer URLhttps://<your-okta-domain>/oauth2/<authServerId>
Client secretPKCE
Additional scopesgroups
Groups claimName groups, ID token, value type Groups, regex kargo-.*
CLI commandkargo login https://<kargo-hostname> --port <port> --sso
Key gotchasPoint Issuer URL at the custom Authorization Server, since that is where the scope and claim live; register the CLI port; set CLI Client ID; assign the app to the group, not just the users

Dex SAML​

Connect Okta through Dex's saml connector, using a SAML 2.0 app integration and its signing certificate.

Step 1: Get your Kargo callback URL​

  1. In your Kargo Instance, go to Settings.

  2. In the left panel, search for Access and go to OIDC Config.

    SSO Settings

  1. Toggle Enabled to on.

  2. Select the Dex option, not OIDC.

  3. A banner displays your callback URL. Copy it — you will register it in Okta:

    https://<your-kargo-hostname>/dex/callback
note

The Kargo Dex callback path is /dex/callback. This single URL is used as both the SAML Single sign-on URL and the Audience URI (SP Entity ID).

When Dex is selected the page shows the /dex/callback URL; when OIDC is selected it shows a /login URL. Make sure you copy the /dex/callback one.

Leave this page open — you will come back to it in Step 5.

Step 2: Create a SAML app integration in Okta​

  1. In the Okta Admin Console, go to Applications → Applications → Create App Integration.

  2. Under Sign-in method, select SAML 2.0, then click Next.

Selecting SAML 2.0 as the sign-in method

Dex is the OIDC layer in this method, so Okta only needs to speak SAML. OIDC - OpenID Connect is for the native OIDC method above; SWA and API Services do not apply.

  1. On Step 1: General Settings, enter an app name — this guide uses kargoakpdex — and click Next.

General settings for the SAML integration

Including both the Kargo instance name and dex in the name keeps the app easy to identify later. Leave Do not display application icon to users unchecked unless you want to hide the tile from the Okta dashboard.

Step 3: Configure the SAML settings​

You are now on Step 2: Configure SAML.

FieldValueNotes
Single sign-on URLhttps://<your-kargo-hostname>/dex/callbackThe Dex callback endpoint from Step 1
Use this for Recipient URL and Destination URLCheckedOkta then uses the same URL for all three fields
Audience URI (SP Entity ID)https://<your-kargo-hostname>/dex/callbackThe same value as the SSO URL
Default RelayStateLeave blankDex does not use RelayState
Name ID formatUnspecifiedDex accepts any NameID format
Application usernameOkta usernameSent to Dex as the NameID
Update application username onCreate and update

The SAML URL settings

Step 4: Configure attribute and group statements​

Still on Configure SAML, scroll to Attribute Statements — under Show Advanced Settings if you do not see it — and add:

NameName formatValue
emailUnspecifieduser.email
nameUnspecifieduser.fullName

These map Okta profile fields to the SAML attributes Dex reads to build the user's identity, and they line up with emailAttr and usernameAttr in the connector config in Step 5.

Attribute statements

Then scroll to Group Attribute Statements and add:

NameName formatFilter
groupsUnspecifiedMatches regex, kargo-.*

Okta includes every group whose name starts with kargo- in the SAML assertion, and Dex passes them through to Kargo for role mapping. Adjust the regex if your naming convention differs.

Click Next and Finish.

Assign groups to the application​

  1. On the kargoakpdex application page, open the Assignments tab.

  2. Click Assign → Assign to Groups.

  3. Assign every group that should have access to Kargo, for example kargo-akpt1, then click Done.

The application Assignments tab

Get the IdP metadata​

  1. On the application page, open the Sign On tab.

  2. Click View SAML setup instructions.

  3. Copy the Identity Provider Single Sign-On URL — this is ssoURL in the connector config.

  4. Download the X.509 Certificate and base64-encode it — this is caData. The metadata URL, https://<your-okta-domain>/app/<app-id>/sso/saml/metadata, serves the same certificate as XML if you prefer to pull it that way.

Step 5: Configure the Dex connector in Kargo​

Back on the Kargo OIDC Config page with Dex selected, open the Configuration tab and enter the following in the Dex Config editor:

connectors:
- type: saml
id: okta
name: OKTA
config:
ssoURL: https://<your-okta-domain>/app/<app-name>/<app-id>/sso/saml
redirectURI: https://<your-kargo-hostname>/dex/callback
usernameAttr: name
emailAttr: email
groupsAttr: groups
caData: <base64-encoded certificate from Okta>

The Dex SAML connector in the Kargo OIDC configuration

Configuration fields explained​

FieldDescription
typeMust be saml for the Dex SAML connector
idUnique identifier for this connector
nameDisplay name shown on the Dex login screen
ssoURLThe Identity Provider Single Sign-On URL from Step 4
redirectURIMust exactly match the Single sign-on URL registered in Okta (/dex/callback)
usernameAttrThe SAML attribute holding the display name — name, from Step 4
emailAttrThe SAML attribute holding the email — email, from Step 4
groupsAttrThe SAML group attribute — groups, from Step 4
caDataThe base64-encoded X.509 signing certificate from Okta

Full connector reference: Dex SAML connector.

Step 6: Map users to Kargo roles and test​

Scroll to Kargo Roles and Permissions and map a claim to a role. For the Admin role, use claim key groups and claim value kargo-akpt1, then click Add. Mapping on email works the same way.

Click Save, then open an incognito or private window, go to https://<your-kargo-hostname>, click SSO Login, and sign in with your Okta account.

Dex SAML troubleshooting​

Okta shows User is not assigned to the application

The user's group is not assigned to the Okta app. Assign it on the Assignments tab (Step 4).

Dex returns Failed to authenticate

The Audience URI in Okta and the redirectURI in the Dex config do not match. Compare them exactly — a trailing slash on one side is enough to break it.

The user authenticates but has no Kargo permissions

The group names do not match the regex filter, or the role mapping value does not match the group name. Check the regex in Step 4 and compare the mapping value against the group name in Okta.

Groups do not appear in the Dex claims

The groups are not assigned to the app, or the regex is too narrow. Assign them (Step 4) and confirm kargo-.* covers your group names.

Redirect loop after the Okta login

The redirectURI in the Dex config does not match the Okta Single sign-on URL. Align Step 3 and Step 5.

Dex SAML summary​

ComponentValue
Okta app typeSAML 2.0 app integration
Single sign-on URLhttps://<kargo-hostname>/dex/callback
Audience URI (SP Entity ID)https://<kargo-hostname>/dex/callback
Name ID formatUnspecified, application username Okta username
Attribute statementsemail → user.email, name → user.fullName
Group attribute statementgroups, matches regex kargo-.*
Kargo SSO modeDex, type: saml connector
CertificateOkta X.509 signing certificate, base64-encoded into caData
Key gotchasAssign groups to the app or login is blocked; redirectURI, the SSO URL and the Audience URI must match exactly