Key points
- Authentication identity is provider ID, issuer, and the ID-token subject. Email is nullable metadata and may be duplicated.
- Email carries a status: verified, unverified, unknown, or not-provided. Only an explicitly verified email may be used by opt-in email-linking policy.
- Malformed
email_verifiedclaims continue login as unknown email assurance and produce auth audit metadata. - Local login and the protected break-glass administrator are always available.
- Only one external provider can be active per installation.
- Local username and password login can be disabled only while external authentication is enabled with at least one enabled provider.
Field reference
AUTH_PROVIDER_LOCAL_ENABLEDapibooleanOptionaltrue
Whether local username and password login is offered.
AUTH_PROVIDER_LOCAL_ENABLED=false- Can only be disabled while external authentication is enabled with at least one enabled provider.
- The protected break-glass administrator remains available regardless.
config/config.go
JWT_EXPIRY_MINUTESapiintegerOptionalPlatform default
Lifetime of an issued access token.
JWT_EXPIRY_MINUTES=60config/config.go
REFRESH_TOKEN_TTL_MINUTESapiintegerOptionalPlatform default
Lifetime of a refresh token.
REFRESH_TOKEN_TTL_MINUTES=10080config/config.go
IDLE_TIMEOUT_MINUTESapiintegerOptionalPlatform default
Idle period after which a browser session is ended.
IDLE_TIMEOUT_MINUTES=30config/config.go
RATE_LIMIT_LOGIN_PER_MINUTEapiintegerOptionalPlatform default
Login attempts accepted per minute before requests are throttled.
RATE_LIMIT_LOGIN_PER_MINUTE=10Together with the lockout settings, this is the brute-force control for local login.
config/config.go
LOGIN_LOCKOUT_THRESHOLDapiintegerOptionalPlatform default
Failed attempts within the lockout window before an account is locked.
LOGIN_LOCKOUT_THRESHOLD=5config/config.go
LOGIN_LOCKOUT_WINDOW_MINUTESapiintegerOptionalPlatform default
Window over which failed login attempts are counted.
LOGIN_LOCKOUT_WINDOW_MINUTES=15config/config.go
JWT_ISSUERapistringOptionalPlatform default
Issuer claim written into user and API tokens.
JWT_ISSUER=https://nopsai.example.comconfig/config.go
JWT_AUDIENCEapistringOptionalPlatform default
Audience claim written into user and API tokens.
JWT_AUDIENCE=nopsai-apiconfig/config.go
JWT_RSA_KEY_PATHapipathOptionalNone
RSA private key path for asymmetric token signing instead of the shared HMAC key.
JWT_RSA_KEY_PATH=/run/secrets/jwt-rsa.pemconfig/config.go
SERVICE_JWT_ISSUERall servicesstringOptionalPlatform default
Issuer claim for internal service tokens.
SERVICE_JWT_ISSUER=nopsai-internalconfig/config.go
SERVICE_JWT_AUDIENCEall servicesstringOptionalPlatform default
Audience claim for internal service tokens.
SERVICE_JWT_AUDIENCE=nopsai-servicesconfig/config.go
Examples
# public: this is what the login screen renders from
curl -s "$NOPSAI_URL/v1/auth/providers" | jq
# resolve which provider should handle an identifier
curl -sX POST "$NOPSAI_URL/v1/auth/discover" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}' | jqHow it works
Production authority lives in setting/system/auth.yaml, credential references, AAA ownership, and runtime IdP-managed identity state. The runnable fixtures under examples/sso/keycloak and examples/sso/idp-test-pack are test fixtures for local scenario testing, not production configuration.
The identity-provider tab lists providers directly, while provider create and edit flows own provider details, OIDC connection, login defaults, mapping, and review panels.
Login is rate limited and lockout protected through RATE_LIMIT_LOGIN_PER_MINUTE, LOGIN_LOCKOUT_THRESHOLD, and LOGIN_LOCKOUT_WINDOW_MINUTES.
Implementation evidence
doc/jwt-authentication.mdLocal auth, SSO/OIDC, user and service tokens, refresh behavior.
doc/local-keycloak-sso.mdThe local Keycloak fixture with seeded users and teams.
examples/sso/README.mdRunnable SSO example fixtures. Test material only.

