Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

API Reference

Complete reference for all HTTP endpoints exposed by the Authentication Verifier.

All endpoints are served over HTTPS only. The base URL is https://{host}:{port}.


Table of Contents


Public Endpoints

No authentication required.

GET /public/version

Returns the server version string.

Response

HTTP/1.1 200 OK
Content-Type: text/plain

"0.1.2"

GET /.well-known/jwks.json

Returns the JSON Web Key Set containing the server's public key(s) for JWT signature verification.

Note: Only available in test builds. Production deployments expose JWKS through the Identity Provider's own JWKS endpoint.

Response

{
  "keys": [
    {
      "kty": "EC",
      "crv": "P-256",
      "x": "...",
      "y": "...",
      "use": "sig"
    }
  ]
}

Authentication — Login and Session Claims

POST /login?realm={realm_id}

Authenticate a client and issue a session cookie. The authentication method is determined by the Authorization header or the request body.

Authentication methods:

MethodHow to supply credentials
Username/PasswordAuthorization: Basic <base64(username:password)>
JWT BearerAuthorization: Bearer <jwt_token>
Client CertificateClient certificate in TLS handshake

Request body (optional — for public-key FIDO2 challenge or TOTP code)

{
  "public_key_pem": null,
  "totp_code": "482913"
}
FieldTypeDescription
public_key_pemString | nullClient public key (FIDO2 / digital credentials flows)
totp_codeString | nullTOTP verification code for 2FA step

Response — 200 OK

{
  "next_step": "Authenticated",
  "session_id": "550e8400-e29b-41d4-a716-446655440000"
}

Set-Cookie header: _ea_=<cookie_string>; HttpOnly; Secure; SameSite=Strict

FieldTypeDescription
next_step"Authenticated" | "TotpRequired" | "ChangePassword"What the client should do next
session_idString | nullUUID of the new session (only present when Authenticated)

Response — 401 Unauthorized — credentials invalid or missing.

Response — 200 OK with "next_step": "TotpRequired" — primary credentials valid but TOTP code is required. Re-submit the request with totp_code.


GET /whoami?realm={realm_id}

Returns the JWT claims for the currently authenticated session.

Authentication: Session cookie (_ea_ cookie).

Response — 200 OK

{
  "iss": "auth-auth",
  "sub": "alice",
  "aud": ["my-service"],
  "exp": 1745000000,
  "iat": 1744996400,
  "as_as": "up",
  "as_rid": "my-service"
}

See Data Types — ClientClaims for the full field reference.

Response — 401 Unauthorized — no valid session cookie.


Session Management

GET /sessions/{session_id}

Retrieve session data by session ID. Returns null (not an error) when the session does not exist or has expired.

Response — 200 OK (session found)

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "realm_id": "my-service",
  "username": "alice",
  "auth_scheme": "up",
  "cookie_string": "eyJ…",
  "max_age_seconds": 3600,
  "max_stale_age_seconds": 1800,
  "created_at": 1744996400
}

Response — 200 OK (session not found)

null

POST /sessions/{session_id}

Retrieve session data and optionally apply a bulk logout action in the same request.

Request body

{
  "authenticated_clients": [
    { "username": "alice", "auth_scheme": "UsernamePassword" }
  ],
  "sessions_action": "LogoutOtherSessions"
}
FieldTypeDescription
authenticated_clientsAuthenticatedClientScheme[]Clients whose sessions may be affected
sessions_action"LogoutOtherSessions" | "LogoutAllSessions" | nullAction to perform alongside the lookup

sessions_action values:

ValueEffect
"LogoutOtherSessions"Delete all sessions for the given clients except the queried one
"LogoutAllSessions"Delete all sessions for the given clients (including the queried one); session data is returned before deletion

Response — 200 OK — same shape as GET /sessions/{id}.


POST /sessions/realms/{realm_id}/clients

Return all session IDs for a set of authenticated clients in a realm.

Request body

[
  { "username": "alice", "auth_scheme": "UsernamePassword" },
  { "username": "alice", "auth_scheme": "Jwt" }
]

Response — 200 OK

{
  "session_ids": [
    "550e8400-e29b-41d4-a716-446655440000",
    "660f9511-f3ac-52e5-c827-557766551111"
  ]
}

DELETE /sessions

Delete sessions by ID.

Request body

{
  "session_ids": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}

Response — 204 No Content — empty body.


DELETE /sessions/expired

Delete all expired sessions from the session store.

Response — 204 No Content — empty body.


DELETE /sessions/realms/{realm_id}

Delete all sessions for a given realm (administrative bulk logout).

Response — 204 No Content — empty body.


Machine Authentication (AppRole / Kubernetes / Token)

Endpoints for machine-to-machine authentication. These sit under the /auth/ prefix. Login endpoints are unauthenticated (the credential is the request body). Role management endpoints require an active admin session cookie. Token self-service endpoints require a valid X-Vault-Token header.

For complete request/response examples and field descriptions see app_auth_api.md.

POST /auth/approle/login

Exchange a role_id + secret_id pair for an opaque app token. Also accepts PUT (SPIRE compatibility).

Request body

{
  "role_id": "a5d7e2f1-0c3b-4a8d-9e6f-1234567890ab",
  "secret_id": "b7c4e9d2-1a2b-4c3d-8e5f-fedcba987654"
}

secret_id is optional when the role has bind_secret_id: false.

Response — 200 OK

{
  "auth": {
    "client_token": "hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob",
    "renewable": true,
    "lease_duration": 3600,
    "policies": ["default"],
    "metadata": { "role_name": "spire-server" }
  }
}

Error responses: 400 Bad Request (invalid role_id or secret_id), 403 Forbidden (secret_id exhausted).


POST /auth/approle/role/{name}

Create or update an AppRole role. Requires admin session cookie.

Path parameter

name — role name (unique identifier for this role)

Request body

{
  "token_ttl": 3600,
  "secret_id_ttl": 86400,
  "token_policies": ["default"],
  "bind_secret_id": true
}
FieldTypeDefaultDescription
token_ttlinteger3600Token lifetime in seconds
secret_id_ttlinteger0Secret ID lifetime in seconds; 0 = no expiry
token_policiesarray[]Policies attached to every issued token
bind_secret_idbooleantrueIf false, login succeeds with role_id alone

Response — 204 No Content


GET /auth/approle/role/{name}/role-id

Read the stable role_id for a role. Requires admin session cookie.

Response — 200 OK

{ "data": { "role_id": "a5d7e2f1-0c3b-4a8d-9e6f-1234567890ab" } }

POST /auth/approle/role/{name}/secret-id

Generate a new secret_id for a role. Requires admin session cookie.

Request body

{ "ttl": 0, "num_uses": 1 }
FieldTypeDefaultDescription
ttlinteger0Per-secret-ID TTL override in seconds; 0 = use role default
num_usesinteger0Max login uses; 0 = unlimited

Response — 200 OK

{
  "data": {
    "secret_id": "b7c4e9d2-1a2b-4c3d-8e5f-fedcba987654",
    "secret_id_accessor": "9f8e7d6c-5b4a-3c2d-1e0f-abcdef012345"
  }
}

Save the secret_id — it cannot be retrieved again. Use the secret_id_accessor to destroy it without knowing the value.


POST /auth/approle/role/{name}/secret-id/destroy

Invalidate a secret_id by its accessor. Requires admin session cookie.

Request body

{ "secret_id_accessor": "9f8e7d6c-5b4a-3c2d-1e0f-abcdef012345" }

Response — 204 No Content


DELETE /auth/approle/role/{name}

Delete a role and all its secret IDs (cascade). Requires admin session cookie.

Response — 204 No Content


GET /auth/approle/role?list=true

List all AppRole role names. Requires admin session cookie.

Response — 200 OK

{ "data": { "keys": ["spire-server", "mistral-agents"] } }

POST /auth/kubernetes/login

Exchange a Kubernetes service-account JWT for an app token.

Request body

{
  "role": "my-k8s-role",
  "jwt": "<service-account JWT from /var/run/secrets/kubernetes.io/serviceaccount/token>"
}

Response — 200 OK

{
  "auth": {
    "client_token": "hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob",
    "renewable": true,
    "lease_duration": 3600,
    "policies": [],
    "metadata": {}
  }
}

Error responses: 400 Bad Request (invalid JWT or role), 403 Forbidden (SA name / namespace not in allow-list).


POST /auth/kubernetes/role/{name}

Create or update a Kubernetes auth role. Requires admin session cookie.

Request body

{
  "jwks_url": "https://kubernetes.default.svc/.well-known/jwks.json",
  "bound_service_account_names": ["my-app"],
  "bound_service_account_namespaces": ["production"],
  "token_ttl": 3600
}
FieldTypeDescription
jwks_urlstringURL of the Kubernetes JWKS endpoint
bound_service_account_namesarrayAllowed SA names; ["*"] = any
bound_service_account_namespacesarrayAllowed namespaces; ["*"] = any
token_ttlintegerToken lifetime in seconds
expected_issuerstring (optional)If set, validates JWT iss claim
bound_audiencesarray (optional)If set, validates JWT aud claim

Response — 204 No Content


DELETE /auth/kubernetes/role/{name}

Delete a Kubernetes role. Requires admin session cookie.

Response — 204 No Content


GET /auth/token/lookup-self

Validate an app token and return its metadata.

Request header

X-Vault-Token: hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob

Response — 200 OK

{
  "data": {
    "id": "hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob",
    "entity_id": "spire-server",
    "policies": ["default"],
    "renewable": true,
    "ttl": 3541,
    "creation_time": 1753670400
  }
}

Error response: 403 Forbidden — token missing, invalid, expired, or revoked.


POST /auth/token/renew-self

Extend a renewable token's TTL back to its configured lease_duration.

Request header

X-Vault-Token: hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob

Response — 200 OK

{
  "auth": {
    "client_token": "hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob",
    "renewable": true,
    "lease_duration": 3600,
    "policies": ["default"],
    "metadata": {}
  }
}

Error response: 403 Forbidden — token not found, expired, or not renewable.


POST /auth/token/revoke-self

Immediately invalidate the current token.

Request header

X-Vault-Token: hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob

Response — 204 No Content


Realm Administration

Realm CRUD endpoints live under /admins/realms. Creating, updating, and deleting realms requires super admin privileges. Listing and getting a single realm is accessible to any authenticated admin (filtered to their administered realms).

POST /admins/realms

Create a new realm. Super admin only.

Request body

{
  "id": "my-service",
  "auth_params": {
    "username_password_params": {
      "allow_expired_passwords": false
    }
  },
  "session_max_age_seconds": 3600,
  "session_max_stale_age_seconds": 1800
}

Response — 201 Created — created Realm object.

Response — 409 Conflict — realm ID already exists.


GET /admins/realms/{realm_id}

Retrieve a realm by ID. Realm admins may only retrieve realms they administer.

Response — 200 OK

{
  "id": "my-service",
  "auth_params": { "username_password_params": { "allow_expired_passwords": false } },
  "session_max_age_seconds": 3600,
  "session_max_stale_age_seconds": 1800
}

Response — 404 Not Found — realm does not exist.


PUT /admins/realms/{realm_id}

Replace a realm's configuration. Super admin only. Returns the updated realm.

Request body — same shape as POST /admins/realms

Response — 200 OK — updated Realm object.


DELETE /admins/realms/{realm_id}

Delete a realm and all associated data. Super admin only.

Response — 204 No Content — empty body.


GET /admins/realms

List all realms. Super admins see all realms; realm admins see only their administered realms.

Response — 200 OK

[
  {
    "id": "_",
    "auth_params": {},
    "session_max_age_seconds": 3600,
    "session_max_stale_age_seconds": 3600
  },
  {
    "id": "my-service",
    ...
  }
]

GET /admins/userpass

List all username/password credentials across every realm. Super admin only.

Response — 200 OK — array of UserPass objects. The password field is always returned as an empty byte array.


Admin Administration

POST /admins

Create a new Admin record. Super admins may create any admin. Realm admins may create an admin only if every realm in the new admin's realms list is one they administer.

Request body

{
  "id": "bob",
  "realms": ["my-service"],
  "userpass": "bob",
  "jwt": null,
  "fido2": null,
  "digital_credentials": null,
  "client_certificate": null,
  "totp_enabled": false,
  "totp_secret": null,
  "totp_auth_url": null
}
FieldTypeDescription
idStringUnique user identifier
realmsString[]Realms this user administers. Use ["_"] for super admin.
userpassString | nullUsername key referencing the userpass credential table
jwtString | nullJWT subject identifier
fido2String | nullFIDO2 identifier (future)
digital_credentialsObject | nullKey-value map of digital credential identifiers (future)
client_certificateString | nullClient certificate identifier
totp_enabledbool | nullWhether TOTP is active for this user
totp_secretString | nullBase32-encoded TOTP secret (read-only via API)
totp_auth_urlString | nullotpauth:// URL for QR code enrollment (read-only)

Response — 201 Created — created Admin object.


GET /admins/{admin_id}

Retrieve an admin by ID.

Response — 200 OKAdmin object.

Response — 404 Not Found — admin does not exist.


PUT /admins/{admin_id}

Replace an admin record. Returns the updated admin.

Request body — same shape as POST /admins

Response — 200 OK — updated Admin object.


DELETE /admins/{admin_id}

Delete an admin record.

Response — 204 No Content — empty body.


GET /admins

List all admins. Super admin only.

Response — 200 OK — array of Admin objects.


PUT /admins/{admin_id}/realms/{realm_id}

Add a realm to an admin's realms list.

Response — 200 OK — updated Admin object.


DELETE /admins/{admin_id}/realms/{realm_id}

Remove a realm from an admin's realms list.

Response — 200 OK — updated Admin object.


Credential Management

All /realms/{realm} endpoints require a valid session from a user who administers the given realm (see authorization_and_administration.md).

POST /realms/{realm_id}/userpass

Create a username/password credential in a realm.

Request body

{
  "realm": "my-service",
  "username": "alice",
  "password": [115, 101, 99, 114, 101, 116],
  "change_password": false
}
FieldTypeDescription
realmStringRealm ID (must match path parameter)
usernameStringCredential username
passwordu8[]UTF-8 bytes of the plaintext password. The server hashes with Argon2id.
change_passwordboolWhen true, the next login returns "ChangePassword" next step

Response — 201 Created — created UserPass object.


GET /realms/{realm_id}/userpass

List all credentials in a realm.

Response — 200 OK — array of UserPass objects. The password field is always empty.


GET /realms/{realm_id}/userpass/{username}

Retrieve a single credential.

Response — 200 OKUserPass object. The password field is always empty.

Response — 404 Not Found — credential does not exist.


PUT /realms/{realm_id}/userpass/{username}

Update a credential (typically to change the password or set change_password).

Request body — same shape as POST /realms/{realm}/userpass

Response — 200 OK — updated UserPass object.


DELETE /realms/{realm_id}/userpass/{username}

Delete a credential.

Response — 204 No Content — empty body.


TOTP Management

See two_factor_authentication.md for the full enrollment flow.

POST /realms/{realm_id}/totp/generate

Generate a TOTP secret for a user. The secret is not stored at this point — it is only persisted after a successful verification call.

Query parameters: realm={realm_id}

Request body

{
  "username": "alice",
  "issuer": "My Application"
}

Response — 200 OK

{
  "secret_base32": "JBSWY3DPEHPK3PXP",
  "otpauth_url": "otpauth://totp/My%20Application:alice?secret=JBSWY3DPEHPK3PXP&issuer=My%20Application&algorithm=SHA1&digits=6&period=30"
}

POST /realms/{realm_id}/totp/verify

Verify a TOTP code and enable TOTP for the user. Call this after generate to confirm the user has enrolled their authenticator app.

Query parameters: realm={realm_id}

Request body

{
  "username": "alice",
  "token": "482913",
  "secret": "JBSWY3DPEHPK3PXP",
  "issuer": "My Application"
}

Response — 200 OK — empty body. TOTP is now active for the user.

Response — 400 Bad Request — TOTP token is invalid.


DELETE /realms/{realm_id}/totp/{username}

Disable TOTP for a user.

Query parameters: realm={realm_id}

Response — 200 OK — empty body.


Common Response Codes

CodeMeaning
200 OKSuccess
400 Bad RequestMalformed request body or invalid parameter
401 UnauthorizedMissing or invalid credentials
403 ForbiddenAuthenticated but not authorised (e.g., realm admin trying to modify another realm)
404 Not FoundResource does not exist
409 ConflictResource already exists (duplicate ID)
500 Internal Server ErrorUnexpected server error

Authentication for Admin Endpoints

Admin endpoints (/admins, /admins/realms, /realms) require a valid session cookie obtained by logging into the _ realm (super admin) or the target realm (realm admin).

# Log in as super admin
curl --cacert ca.cert.pem \
     -X POST "https://localhost:8443/login?realm=_" \
     -u "admin:password" \
     -c cookies.txt

# Use the session cookie for admin calls
curl --cacert ca.cert.pem \
     -b cookies.txt \
     https://localhost:8443/admins/realms

See authorization_and_administration.md for the full authorization matrix.


Data Types

Realm

{
  "id": "my-service",
  "auth_params": {
    "username_password_params": { "allow_expired_passwords": false },
    "jwt_params": {
      "idp_params": [
        {
          "jwt_issuer_uri": "https://accounts.google.com",
          "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
          "jwt_audience": "my-client-id.apps.googleusercontent.com"
        }
      ],
      "smallest_refresh_interval_seconds": 300
    },
    "totp_params": { "algorithm": "SHA1", "step": 30 }
  },
  "session_max_age_seconds": 3600,
  "session_max_stale_age_seconds": 1800
}

Admin

{
  "id": "alice",
  "realms": ["my-service"],
  "userpass": "alice",
  "jwt": null,
  "fido2": null,
  "digital_credentials": null,
  "client_certificate": null,
  "totp_enabled": false,
  "totp_secret": null,
  "totp_auth_url": null
}

UserPass

{
  "realm": "my-service",
  "username": "alice",
  "password": [],
  "change_password": false
}

The password field is always returned as an empty byte array from GET endpoints. Send the plaintext UTF-8 bytes only on create/update.

SessionData

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "realm_id": "my-service",
  "username": "alice",
  "auth_scheme": "up",
  "cookie_string": "eyJ…",
  "max_age_seconds": 3600,
  "max_stale_age_seconds": 1800,
  "created_at": 1744996400
}
FieldDescription
auth_scheme"up" username/password · "jwt" JWT · "cc" client cert · "f2" FIDO2 · "dc" digital credentials

AuthenticatedClientScheme

{ "username": "alice", "auth_scheme": "UsernamePassword" }

auth_scheme values: "UsernamePassword", "Jwt", "ClientCertificate", "Fido2", "DigitalCredentials"

ClientClaims

The JWT payload returned by GET /whoami:

ClaimTypeDescription
issStringIssuer
subStringSubject (authenticated username)
audString[]Audience list
expi64Expiration (Unix seconds)
nbfi64Not-before (Unix seconds)
iati64Issued-at (Unix seconds)
jtiStringJWT ID
as_asStringAuth scheme used (up/jwt/cc/f2/dc)
as_pkStringClient public key PEM (when applicable)
as_ridStringRealm ID