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}.
- Public Endpoints
- Authentication — Login and Session Claims
- Session Management
- Machine Authentication (AppRole / Kubernetes / Token)
- Realm Administration
- Admin Administration
- Credential Management
- TOTP Management
- Common Response Codes
- Authentication for Admin Endpoints
- Data Types
No authentication required.
Returns the server version string.
HTTP/1.1 200 OK
Content-Type: text/plain
"0.1.2"
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.
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "...",
"use": "sig"
}
]
}Authenticate a client and issue a session cookie. The authentication method is determined by the Authorization header or the request body.
Authentication methods:
| Method | How to supply credentials |
|---|---|
| Username/Password | Authorization: Basic <base64(username:password)> |
| JWT Bearer | Authorization: Bearer <jwt_token> |
| Client Certificate | Client certificate in TLS handshake |
{
"public_key_pem": null,
"totp_code": "482913"
}| Field | Type | Description |
|---|---|---|
public_key_pem |
String | null |
Client public key (FIDO2 / digital credentials flows) |
totp_code |
String | null |
TOTP 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
| Field | Type | Description |
|---|---|---|
next_step |
"Authenticated" | "TotpRequired" | "ChangePassword" |
What the client should do next |
session_id |
String | null |
UUID 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.
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.
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)
nullRetrieve session data and optionally apply a bulk logout action in the same request.
{
"authenticated_clients": [
{ "username": "alice", "auth_scheme": "UsernamePassword" }
],
"sessions_action": "LogoutOtherSessions"
}| Field | Type | Description |
|---|---|---|
authenticated_clients |
AuthenticatedClientScheme[] |
Clients whose sessions may be affected |
sessions_action |
"LogoutOtherSessions" | "LogoutAllSessions" | null |
Action to perform alongside the lookup |
sessions_action values:
| Value | Effect |
|---|---|
"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}.
Return all session IDs for a set of authenticated clients in a realm.
[
{ "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 by ID.
{
"session_ids": [
"550e8400-e29b-41d4-a716-446655440000"
]
}Response — 204 No Content — empty body.
Delete all expired sessions from the session store.
Response — 204 No Content — empty body.
Delete all sessions for a given realm (administrative bulk logout).
Response — 204 No Content — empty body.
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.
Exchange a role_id + secret_id pair for an opaque app token. Also accepts PUT (SPIRE compatibility).
{
"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.
{
"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).
Create or update an AppRole role. Requires admin session cookie.
name — role name (unique identifier for this role)
{
"token_ttl": 3600,
"secret_id_ttl": 86400,
"token_policies": ["default"],
"bind_secret_id": true
}| Field | Type | Default | Description |
|---|---|---|---|
token_ttl |
integer | 3600 | Token lifetime in seconds |
secret_id_ttl |
integer | 0 | Secret ID lifetime in seconds; 0 = no expiry |
token_policies |
array | [] |
Policies attached to every issued token |
bind_secret_id |
boolean | true | If false, login succeeds with role_id alone |
Response — 204 No Content
Read the stable role_id for a role. Requires admin session cookie.
{ "data": { "role_id": "a5d7e2f1-0c3b-4a8d-9e6f-1234567890ab" } }Generate a new secret_id for a role. Requires admin session cookie.
{ "ttl": 0, "num_uses": 1 }| Field | Type | Default | Description |
|---|---|---|---|
ttl |
integer | 0 | Per-secret-ID TTL override in seconds; 0 = use role default |
num_uses |
integer | 0 | Max login uses; 0 = unlimited |
{
"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.
Invalidate a secret_id by its accessor. Requires admin session cookie.
{ "secret_id_accessor": "9f8e7d6c-5b4a-3c2d-1e0f-abcdef012345" }Response — 204 No Content
Delete a role and all its secret IDs (cascade). Requires admin session cookie.
Response — 204 No Content
List all AppRole role names. Requires admin session cookie.
{ "data": { "keys": ["spire-server", "mistral-agents"] } }Exchange a Kubernetes service-account JWT for an app token.
{
"role": "my-k8s-role",
"jwt": "<service-account JWT from /var/run/secrets/kubernetes.io/serviceaccount/token>"
}{
"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).
Create or update a Kubernetes auth role. Requires admin session cookie.
{
"jwks_url": "https://kubernetes.default.svc/.well-known/jwks.json",
"bound_service_account_names": ["my-app"],
"bound_service_account_namespaces": ["production"],
"token_ttl": 3600
}| Field | Type | Description |
|---|---|---|
jwks_url |
string | URL of the Kubernetes JWKS endpoint |
bound_service_account_names |
array | Allowed SA names; ["*"] = any |
bound_service_account_namespaces |
array | Allowed namespaces; ["*"] = any |
token_ttl |
integer | Token lifetime in seconds |
expected_issuer |
string (optional) | If set, validates JWT iss claim |
bound_audiences |
array (optional) | If set, validates JWT aud claim |
Response — 204 No Content
Delete a Kubernetes role. Requires admin session cookie.
Response — 204 No Content
Validate an app token and return its metadata.
X-Vault-Token: hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob{
"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.
Extend a renewable token's TTL back to its configured lease_duration.
X-Vault-Token: hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob{
"auth": {
"client_token": "hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob",
"renewable": true,
"lease_duration": 3600,
"policies": ["default"],
"metadata": {}
}
}Error response: 403 Forbidden — token not found, expired, or not renewable.
Immediately invalidate the current token.
X-Vault-Token: hvs.AAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobResponse — 204 No Content
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).
Create a new realm. Super admin only.
{
"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.
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.
Replace a realm's configuration. Super admin only. Returns the updated realm.
Response — 200 OK — updated Realm object.
Delete a realm and all associated data. Super admin only.
Response — 204 No Content — empty body.
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",
...
}
]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.
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.
{
"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
}| Field | Type | Description |
|---|---|---|
id |
String |
Unique user identifier |
realms |
String[] |
Realms this user administers. Use ["_"] for super admin. |
userpass |
String | null |
Username key referencing the userpass credential table |
jwt |
String | null |
JWT subject identifier |
fido2 |
String | null |
FIDO2 identifier (future) |
digital_credentials |
Object | null |
Key-value map of digital credential identifiers (future) |
client_certificate |
String | null |
Client certificate identifier |
totp_enabled |
bool | null |
Whether TOTP is active for this user |
totp_secret |
String | null |
Base32-encoded TOTP secret (read-only via API) |
totp_auth_url |
String | null |
otpauth:// URL for QR code enrollment (read-only) |
Response — 201 Created — created Admin object.
Retrieve an admin by ID.
Response — 200 OK — Admin object.
Response — 404 Not Found — admin does not exist.
Replace an admin record. Returns the updated admin.
Response — 200 OK — updated Admin object.
Delete an admin record.
Response — 204 No Content — empty body.
List all admins. Super admin only.
Response — 200 OK — array of Admin objects.
Add a realm to an admin's realms list.
Response — 200 OK — updated Admin object.
Remove a realm from an admin's realms list.
Response — 200 OK — updated Admin object.
All /realms/{realm} endpoints require a valid session from a user who administers the given realm (see authorization_and_administration.md).
Create a username/password credential in a realm.
{
"realm": "my-service",
"username": "alice",
"password": [115, 101, 99, 114, 101, 116],
"change_password": false
}| Field | Type | Description |
|---|---|---|
realm |
String |
Realm ID (must match path parameter) |
username |
String |
Credential username |
password |
u8[] |
UTF-8 bytes of the plaintext password. The server hashes with Argon2id. |
change_password |
bool |
When true, the next login returns "ChangePassword" next step |
Response — 201 Created — created UserPass object.
List all credentials in a realm.
Response — 200 OK — array of UserPass objects. The password field is always empty.
Retrieve a single credential.
Response — 200 OK — UserPass object. The password field is always empty.
Response — 404 Not Found — credential does not exist.
Update a credential (typically to change the password or set change_password).
Response — 200 OK — updated UserPass object.
Delete a credential.
Response — 204 No Content — empty body.
See two_factor_authentication.md for the full enrollment flow.
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}
{
"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"
}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}
{
"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.
Disable TOTP for a user.
Query parameters: realm={realm_id}
Response — 200 OK — empty body.
| Code | Meaning |
|---|---|
200 OK |
Success |
400 Bad Request |
Malformed request body or invalid parameter |
401 Unauthorized |
Missing or invalid credentials |
403 Forbidden |
Authenticated but not authorised (e.g., realm admin trying to modify another realm) |
404 Not Found |
Resource does not exist |
409 Conflict |
Resource already exists (duplicate ID) |
500 Internal Server Error |
Unexpected server error |
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/realmsSee authorization_and_administration.md for the full authorization matrix.
{
"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
}{
"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
}{
"realm": "my-service",
"username": "alice",
"password": [],
"change_password": false
}The
passwordfield is always returned as an empty byte array from GET endpoints. Send the plaintext UTF-8 bytes only on create/update.
{
"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
}| Field | Description |
|---|---|
auth_scheme |
"up" username/password · "jwt" JWT · "cc" client cert · "f2" FIDO2 · "dc" digital credentials |
{ "username": "alice", "auth_scheme": "UsernamePassword" }auth_scheme values: "UsernamePassword", "Jwt", "ClientCertificate", "Fido2", "DigitalCredentials"
The JWT payload returned by GET /whoami:
| Claim | Type | Description |
|---|---|---|
iss |
String |
Issuer |
sub |
String |
Subject (authenticated username) |
aud |
String[] |
Audience list |
exp |
i64 |
Expiration (Unix seconds) |
nbf |
i64 |
Not-before (Unix seconds) |
iat |
i64 |
Issued-at (Unix seconds) |
jti |
String |
JWT ID |
as_as |
String |
Auth scheme used (up/jwt/cc/f2/dc) |
as_pk |
String |
Client public key PEM (when applicable) |
as_rid |
String |
Realm ID |