authentication
See OAuthConfig for details on provider URL, scopes, and field mapping from OIDC claims to ssoossh identity fields (username, groups).
| Key | Type | Default |
|---|---|---|
authentication.client_id |
string | empty |
authentication.client_secret |
string | empty |
authentication.provider_url |
string | empty |
authentication.scopes |
string | profile email |
authentication.disable_pkce |
bool | false |
authentication.userinfo |
bool | true |
authentication.fields.subject |
string | sub |
authentication.fields.username |
string | preferred_username |
authentication.fields.name |
string | name |
authentication.fields.groups |
string | groups |
authentication.fields.other_accounts |
string | empty |
authentication.fields.service_accounts |
string | empty |
authentication.fields.email |
string | email |
authentication.fields.extra |
map |
client_id
Section titled “client_id”string, default empty
The OAuth2/OIDC client ID registered with the identity provider. Required.
authentication: client_id: ""client_secret
Section titled “client_secret”string, default empty
The OAuth2 client secret.
authentication: client_secret: ""provider_url
Section titled “provider_url”string, default empty
The OIDC provider’s issuer URL, used for discovery. A trailing /.well-known/openid-configuration is stripped automatically. Required.
authentication: provider_url: ""scopes
Section titled “scopes”string, default profile email
A space-separated list of additional scopes to request alongside the always-included “openid” scope, e.g. “profile email”.
authentication: scopes: "profile email"disable_pkce
Section titled “disable_pkce”bool, default false
Drops the PKCE (RFC 7636) code challenge from the authorization request and the code verifier from the token exchange. Off by default: PKCE is what stops an authorization code stolen in transit — out of a proxy log, a browser history, a referrer header — from being redeemed by anyone but the browser that started the login.
It exists because a few providers reject an authorization request carrying code_challenge for a confidential client, and an operator who cannot change that at the provider would otherwise have no way to log in at all. Nothing else in the login changes: state and the ID token nonce are unaffected and still checked.
Turn it on only against a provider that demonstrably refuses the parameters, and treat it as temporary — every provider ssoossh supports otherwise handles PKCE, and the server warns at startup whenever this is set.
authentication: disable_pkce: falseuserinfo
Section titled “userinfo”bool, default true
Calls the provider’s userinfo endpoint at every login (and every claims echo), with the access token the code exchange returned, and fills in the claims the ID token lacks. Many providers keep the ID token lean and put profile, email or groups claims only in userinfo; without this, a fields mapping that names one of them resolves to nothing.
The ID token always wins: a claim it carries is never replaced, and userinfo only adds the keys it lacks. Its sub must equal the ID token’s exactly (OpenID Connect Core 5.3.2); a mismatch fails the login. A provider whose discovery document names no userinfo_endpoint is simply not called.
A failed call (network, an error status, a body that is not JSON) never fails a login by itself: it is logged, and the login carries on with the ID token’s claims. After 10 failed calls in a row, counted across every instance through the database, the server pauses the calls: logins use the ID token alone, and one login every 15 minutes (one across the fleet) makes the call as a probe. A probe that succeeds resumes the calls. Pausing and resuming are each audited (auth.userinfo_paused, auth.userinfo_resumed) and emailed to every admin; the admin diagnostics page shows the state.
Turning this on can add claims the ID token lacked, the configured groups claim included, so a role group can start counting at the next login.
authentication: userinfo: truefields
Section titled “fields”Maps ssoossh identity fields to claim names in the OIDC provider’s ID token (and, with userinfo on, the claims the provider’s userinfo endpoint adds). Username is the only required field; the rest are empty by default, meaning “not populated from OIDC”.
To see which claims a provider actually sends before choosing, run ssoosshd identity echo on the server host and open the one-time link it prints: it needs no admin role and no working mapping. See https://mnestor.github.io/ssoossh/operations/identity-provider/#seeing-what-your-provider-actually-sends
fields.subject
Section titled “fields.subject”string, default sub
Names the claim holding the unique, immutable account identifier — the value users.subject is keyed by, and the only thing that ties a person to their certificate history across logins.
It defaults to “sub” because that is what OIDC guarantees to be stable per issuer, but not every provider’s “sub” is the identifier an operator wants: Entra ID issues a per-application “sub” and puts the tenant-stable one in “oid”, and a provider fronting an upstream directory may carry the directory’s own UUID in a private claim. Username and email are deliberately not candidates — both change when a person is renamed or married, and keying on either silently forks their history into a second account.
Changing this on a running deployment re-keys every login: users whose row was written under the old claim will no longer match and will be created fresh. Choose it before the first login, not after.
authentication: fields: subject: "sub"fields.username
Section titled “fields.username”string, default preferred_username
Names the claim holding the local account username, e.g. “preferred_username”. Required.
This is the account name on target systems — the default certificate principal — not an identifier. It may change; Subject is what stays put.
authentication: fields: username: "preferred_username"fields.name
Section titled “fields.name”string, default name
Names the claim holding the person’s human-readable name, e.g. “Ada Lovelace”. Display only: it is shown beside the username in the web UI and offered to email templates, and it never becomes a certificate principal, a key ID, or an authorization input.
Empty disables the capture. A configured claim that does not arrive stores empty and is simply not shown; login never fails over one.
authentication: fields: name: "name"fields.groups
Section titled “fields.groups”string, default groups
Names a claim expected to hold a JSON array of group names. It feeds the certificate lifetime and require-group decision only; group membership is never placed in an issued certificate (see https://mnestor.github.io/ssoossh/internals/invariants/).
authentication: fields: groups: "groups"fields.other_accounts
Section titled “fields.other_accounts”string, default empty
Names a claim expected to hold a JSON array of alternate account identifiers this identity is known by on target systems (a different username, UPN, or sAMAccountName), added to a certificate’s principal list alongside the username claim.
authentication: fields: other_accounts: ""fields.service_accounts
Section titled “fields.service_accounts”string, default empty
Names a claim expected to hold a JSON array of service-account identifiers this identity is authorized to manage and enroll certificates for.
authentication: fields: service_accounts: ""fields.email
Section titled “fields.email”string, default email
Names the claim to read the user’s email from. Empty falls back to the standard “email” claim opportunistically (not an error if absent either way).
authentication: fields: email: "email"fields.extra
Section titled “fields.extra”map
Maps additional template field names to claim names. Each configured claim is captured at login (scalars as strings, string arrays as lists), stored on the users row, and made available to key ID templates as {{.Extra.<name>}} (or {{join .Extra.<name> “<sep>”}} for lists). A claim absent from the ID token stores empty and renders as MISSING; login never fails over one. Names with characters beyond letters/digits/underscores need the template’s index syntax: {{index .Extra “my-name”}}.
For example, capturing a department claim and an account list:
authentication: fields: extra: dept: "https://idp.example.com/department" accounts: altAccountsthen interpolated into a key ID:
cert_options: user: key_id_template: '{{.Username}}-{{.Extra.dept}}-{{join .Extra.accounts ";"}}'See https://mnestor.github.io/ssoossh/operations/key-id-templates/ for the full template field list and the MISSING rendering rules.