Nexus GSLB uses named user accounts with session-based authentication. Users log in with email and password, receive a session token, and carry that token (via cookie in the browser, or Authorization: Bearer header in the CLI and API clients). Sessions are stored in NATS KV and are cluster-wide — a session created on one node is valid on all nodes.
The legacy per-tenant API key system remains fully supported for programmatic and CI/CD access.
Environment variables
Two environment variables must be set before starting the daemon when auth is enabled:
Variable | Purpose |
|---|---|
| System-level admin key. Any Bearer request carrying this exact value is granted full access bypassing all RBAC checks. Also serves as a break-glass credential if the database is unavailable. If unset, the daemon runs in dev mode — all requests are treated as system admin with no login required. |
| 32-byte hex key used to encrypt TOTP secrets at rest (AES-256-GCM). Required at startup if any user has TOTP enabled. Safe to omit until you enroll the first TOTP device. |
Generate values:
openssl rand -hex 32 # for GSLB_API_KEYopenssl rand -hex 32 # for GSLB_SECRET_KEY
Store them in an environment file, not in the config file:
# /etc/gslb/env (chmod 600, owned by the gslb service user)GSLB_API_KEY=<32-byte hex>GSLB_SECRET_KEY=<32-byte hex>
Reference it from the systemd unit:
[Service]EnvironmentFile=/etc/gslb/env
Configuration
Auth behaviour is controlled by the auth: section in config.yaml. All fields have sensible defaults and can be omitted for a standard deployment.
auth: sessionTTL: "24h" # How long a session lives without activity sessionExtend: true # Reset the TTL on every authenticated request (sliding window) bcryptCost: 12 # bcrypt work factor — higher = slower but more resistant to brute force (10–14 is sensible) setupEndpoint: true # Allow POST /api/v1/auth/setup; set false after initial bootstrapping if desired
First-run setup
On a fresh installation with no users in the database, you have two ways to create the first admin account.
Option A — interactive CLI (on the server)
gslbd --config /etc/gslb/config.yaml --create-admin
The daemon prompts for email and password, creates a tenant_admin user in the default tenant, and exits. Start the daemon normally afterwards.
This command exits immediately if any users already exist.
Option B — setup endpoint (infrastructure-as-code, Docker, Ansible)
While the database is empty, POST /api/v1/auth/setup is available without authentication:
curl -s -X POST https://<host>/api/v1/auth/setup \ -H 'Content-Type: application/json' \ -d '{"email": "admin@example.com", "password": "your-password"}'
The endpoint responds with the created user object and is permanently disabled (returns 404) once the first user exists. It can also be disabled explicitly in config:
auth: setupEndpoint: false
Logging inWeb UI
Navigate to https://<host>/ui/login. Enter email and password. If TOTP is enrolled on the account, a second prompt appears after the password is accepted.
On successful login the browser is redirected to the dashboard. The session is stored in an HttpOnly cookie (gslb_session) — no token is visible or stored in localStorage.
API / CLI
curl -s -X POST https://<host>/api/v1/auth/login \ -H 'Content-Type: application/json' \ -d '{"email": "admin@example.com", "password": "your-password"}'
Response:
{ "token": "4272f488...", "userId": "cc543538...", "role": "tenant_admin", "expiresAt": 1778587389}
Use the token as a Bearer header for subsequent requests:
curl -H "Authorization: Bearer 4272f488..." https://<host>/api/v1/pools
The same token also works via cookie — the login endpoint sets gslb_session in the response regardless of the client.
With TOTP
curl -s -X POST https://<host>/api/v1/auth/login \ -H 'Content-Type: application/json' \ -d '{"email": "admin@example.com", "password": "your-password", "totpCode": "123456"}'
If the account has TOTP enabled and totpCode is omitted, the response is:
{"error": "TOTP code required", "mfaRequired": true}
System API key (programmatic / CI)
The GSLB_API_KEY value can be used directly as a Bearer token and bypasses the session system entirely. This is suitable for CI pipelines and automated tooling:
curl -H "Authorization: Bearer $GSLB_API_KEY" https://<host>/api/v1/pools
Roles and permissions
Each user has one of three built-in roles:
Role | What they can do |
|---|---|
| Full access within their tenant: manage pools, members, services, health checks, geo rules, and other users. Cannot manage tenants or cross-tenant resources. |
| Read and write pools, members, services, health checks, and geo rules. Can read users. Cannot create, modify, or delete users. |
| Read-only access to all resources within the tenant. No write operations. |
The system API key (GSLB_API_KEY) is supra-tenant and bypasses all RBAC checks.
Managing usersList users
curl -H "Authorization: Bearer <token>" https://<host>/api/v1/users
Create a user
Requires tenant_admin role:
curl -s -X POST https://<host>/api/v1/users \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <token>' \ -d '{ "email": "operator@example.com", "displayName": "Jane Doe", "password": "secure-password", "role": "operator" }'
Update a user (role or password)
curl -s -X PUT https://<host>/api/v1/users/<userId> \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <token>' \ -d '{ "displayName": "Jane Doe", "role": "tenant_admin", "password": "new-password" }'
Omit password to leave it unchanged.
Delete a user
curl -s -X DELETE -H "Authorization: Bearer <token>" \ https://<host>/api/v1/users/<userId>
Users cannot delete their own account.
Setting up TOTP (MFA)
TOTP requires GSLB_SECRET_KEY to be set (the secret is encrypted at rest).
Initiate enrollment — returns a secret and a QR code PNG:
curl -s -H "Authorization: Bearer <token>" \ -X POST https://<host>/api/v1/auth/totp/setup \ -o /tmp/qr.png
Open /tmp/qr.png in any image viewer and scan it with an authenticator app (Google Authenticator, Authy, 1Password, etc.). The response body also contains the secret as base32 text for manual entry.
Verify enrollment — submit a live code from the authenticator app to activate TOTP:
curl -s -X POST https://<host>/api/v1/auth/totp/verify \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <token>' \ -d '{"code": "123456"}'
Once verified, all future logins for this account require a valid TOTP code.
Disable TOTP — a
tenant_admincan clear it by updating the user withPUT /api/v1/users/<id>(this clears the stored secret). Currently requires direct DB access if the admin has also lost their TOTP device (see Troubleshooting).
Session managementLogout
curl -s -X POST -H "Authorization: Bearer <token>" \ https://<host>/api/v1/auth/logout
This immediately deletes the session from NATS KV — the token is invalid across all cluster nodes within milliseconds.
Session TTL
Sessions expire after auth.sessionTTL (default 24 hours). With auth.sessionExtend: true (default), each authenticated request resets the clock — the session only expires if the user is inactive for the full TTL period.
Revoking a session without the token
Delete the session directly from NATS KV. The KV key is the SHA-256 hex hash of the plaintext token:
# On any node with NATS CLI accessnats kv del NEXUS_SESSIONS <sha256-of-token>
If NATS is unavailable, sessions fall back to a per-node in-memory store and are lost on daemon restart.
Backwards compatibility
Deployments using the per-tenant API key (Authorization: Bearer <tenant-api-key>) continue to work without changes. The auth middleware checks:
Cookie session
Bearer — system API key
Bearer — user session token
Bearer — tenant API key (existing behaviour)
In that order. No config changes are required for existing programmatic clients.
SSO via OIDC
Nexus GSLB supports Single Sign-On through any OpenID Connect (OIDC) provider — Google, Okta, Keycloak, Azure AD, GitHub, and others. When enabled, a Sign in with SSO button appears on the login page. Accounts are provisioned automatically on first login (JIT provisioning).
Password-based login remains available alongside SSO; the two methods coexist.
Configuration
Add an oidc: block under auth: in config.yaml:
auth: oidc: enabled: true discoveryUrl: "https://accounts.google.com" # provider OIDC discovery endpoint clientId: "1234567890-abc.apps.googleusercontent.com" clientSecret: "GOCSPX-..." redirectUri: "https://nexus.example.com/api/v1/auth/oidc/callback" scopes: ["openid", "profile", "email"] # default; usually no need to change roleClaim: "groups" # which claim to inspect for role mapping roleMapping: "nexus-admins": "tenant_admin" "nexus-operators": "operator" defaultRole: "viewer" # role assigned if no mapping matches allowedDomains: ["example.com"] # optional — reject emails from other domains
Field | Type | Required | Description |
|---|---|---|---|
| bool | — | Set |
| string | Yes | OIDC issuer URL. The provider's |
| string | Yes | OAuth2 client ID from the provider. |
| string | Yes | OAuth2 client secret from the provider. |
| string | Yes | Full callback URL registered with the provider. Must end with |
| []string | — | OAuth2 scopes to request. Default: |
| string | — | Claim in the userinfo response whose value is inspected for role mapping (e.g., |
| map | — | Maps claim values to Nexus role names ( |
| string | — | Role assigned when no |
| []string | — | When non-empty, only emails whose domain matches one of these values are permitted. Others receive a 403. |
Provider setup
Register a new OAuth2 / OIDC application with your provider and copy the client ID and secret. Set the redirect URI to exactly:
https://<your-nexus-host>/api/v1/auth/oidc/callback
Most providers call this the "Authorized redirect URI" or "Callback URL".
How it works
User clicks Sign in with SSO on the login page.
Browser is redirected to
GET /api/v1/auth/oidc/authorize, which generates a single-use CSRF state nonce (10-minute expiry) and redirects to the provider's authorization endpoint. When NATS is configured the nonce is stored in NATS KV, so in a cluster the authorize and callback requests may be served by different nodes (multi-replica Kubernetes Services and round-robin admin hostnames work). Without NATS the nonce is process-local: both requests must reach the same node, and a daemon restart drops in-flight logins.User authenticates with the provider and approves the application.
Provider redirects back to
/api/v1/auth/oidc/callback?code=…&state=….The daemon validates the state nonce (CSRF protection), exchanges the code for tokens, and fetches the userinfo endpoint.
The email claim is extracted and the domain allowlist is checked (if configured).
The role is resolved:
roleClaimvalue →roleMappinglookup →defaultRolefallback.The user account is looked up by email. If it doesn't exist, it is created automatically (
auth_provider: oidc) with the resolved role.A session cookie (
gslb_session) is set and the browser is redirected to/.
OIDC-provisioned users have no password set and cannot log in via the password form. To grant or change a role for an OIDC user, update the user record via PUT /api/v1/users/{id}.
Common provider configurations
Google Workspace
auth: oidc: enabled: true discoveryUrl: "https://accounts.google.com" clientId: "<google-client-id>" clientSecret: "<google-client-secret>" redirectUri: "https://nexus.example.com/api/v1/auth/oidc/callback" allowedDomains: ["example.com"] defaultRole: "viewer"
Okta
auth: oidc: enabled: true discoveryUrl: "https://<your-okta-domain>/oauth2/default" clientId: "<okta-client-id>" clientSecret: "<okta-client-secret>" redirectUri: "https://nexus.example.com/api/v1/auth/oidc/callback" roleClaim: "groups" roleMapping: "Nexus Admins": "tenant_admin" "Nexus Operators": "operator" defaultRole: "viewer"
Keycloak
auth: oidc: enabled: true discoveryUrl: "https://keycloak.example.com/realms/<realm>" clientId: "nexus-gslb" clientSecret: "<keycloak-secret>" redirectUri: "https://nexus.example.com/api/v1/auth/oidc/callback" # Keycloak returns realm roles nested under realm_access.roles in the # userinfo response; the dotted path reads them directly — no custom # protocol mapper needed. Create matching realm roles and assign them # to users/groups in Keycloak. roleClaim: "realm_access.roles" roleMapping: "nexus-admin": "tenant_admin" "nexus-operator": "operator" defaultRole: "viewer"
Authentication endpoint reference
Method | Path | Auth required | Description |
|---|---|---|---|
|
| No | Create first admin user. Disabled after first user exists. |
|
| No | Authenticate with email + password. Returns token + sets cookie. |
|
| Session | Revoke current session. |
|
| Session | Return current user details and permissions. |
|
| Session | Begin TOTP enrollment; returns QR PNG + secret. |
|
| Session | Confirm TOTP code to activate MFA. |
|
| No | Returns |
|
| No | Initiates OIDC flow; redirects to provider. |
|
| No | Handles provider redirect; sets session cookie. |
|
|
| List users in tenant. |
|
|
| Create user. |
|
|
| Get user details. |
|
|
| Update display name, role, or password. |
|
|
| Delete user. |