Single sign-on
Each tenant signs in through its own identity provider. Configuration lives in the JSON file named by SSO_CONFIG_FILE. The sign-in page shows one button whichever protocol the tenant uses.
OpenID Connect
Section titled “OpenID Connect”Register an app at the identity provider with the redirect URI https://<host>/api/auth/oidc/callback and the openid, email and profile scopes. Then add the tenant:
{ "tenants": [ { "tenantId": "ten_example", "issuer": "https://login.microsoftonline.com/<directory-id>/v2.0", "clientId": "<application-id>", "clientSecret": "<from the secret manager>", "redirectUri": "https://spend.example.gov/api/auth/oidc/callback", "emailClaim": "preferred_username", "allowedDomains": ["example.gov"], "requireMfa": true } ]}Spend uses the authorization code flow with PKCE. ID tokens must be signed with an asymmetric key from the provider’s published key set (HMAC and none are refused), match the issuer, audience, nonce and expiry, and carry an email in an allowed domain.
Multi-factor
Section titled “Multi-factor”With "requireMfa": true, a sign-in counts only when the token’s amr claim includes mfa or mca, or factors of two kinds, or its acr is a multi-factor class. Providers with their own classes can list them in mfaAcrValues, which are also sent as acr_values. In Entra ID, a Conditional Access policy requiring MFA for the app makes every sign-in carry mfa. Refused sign-ins are recorded with the reason no_mfa.
SAML 2.0
Section titled “SAML 2.0”For ADFS, or Entra ID, Okta and Ping set up for SAML, list the tenant under saml:
{ "saml": [ { "tenantId": "ten_example", "entryPoint": "https://adfs.example.gov/adfs/ls/", "idpIssuer": "http://adfs.example.gov/adfs/services/trust", "idpCert": "<token-signing certificate, PEM>", "spEntityId": "https://spend.example.gov/saml/ten_example", "acsUrl": "https://spend.example.gov/api/auth/saml/acs/ten_example", "allowedDomains": ["example.gov"], "requireMfa": true } ]}Give the provider’s administrator the metadata at https://<host>/api/auth/saml/metadata/<tenantId>. Assertions must be signed and answer a sign-in Spend started; unsolicited responses are refused. The email comes from the emailaddress or mail claim (or emailAttribute), falling back to an email-shaped NameID. During a certificate rollover, idpCert can be a list of two.
First administrator
Section titled “First administrator”On first start each tenant gets one administrator, the email in BOOTSTRAP_ADMIN_EMAIL. That person signs in through single sign-on and sets up everyone else, or connects SCIM.