Skip to content

Get user details (auditor-only)

GET
/api/admin/users/{id}
curl --request GET \
--url https://example.com/api/admin/users/example \
--header 'X-Ssoossh-Key-Secret: <X-Ssoossh-Key-Secret>'

Returns full identity details, disable state, and counts of certificates and service enrollments for one user.

id
required
string

User ID

User details

Media typeapplication/json
object
certificate_count
required

CertificateCount is how many certificates have been issued to this user.

integer
created_at
required

CreatedAt is when the user first authenticated.

string
directory

Directory is the user’s directory bookkeeping row, absent when they have never been enriched. It is what answers “why is this person missing a group” from data already stored.

Also absent whenever ldap.enabled is false, even for a user who has a row. Switching the directory off stops the sync, so everything in that row is frozen at whatever the last pass read, and the server no longer acts on any of it — the session identity falls back to the OIDC values on the next request. Presenting frozen data beside live data with no way to tell them apart is how someone ends up granting access on a principal list that has not been true for months.

object
attributes
required

Attributes is the stored field map — the same values the login path merges onto the identity, and the last-known-good cache it falls back to when the directory is unreachable.

object
key
additional properties
Array<string>
consecutive_misses

ConsecutiveMisses is how many passes have observed the absence. Reporting only: the disable is decided on elapsed time, since a pass count measures replica count rather than absence.

integer
directory_id
required

DirectoryID is the entry’s unique, immutable identifier (the attribute named by ldap.id_attribute), and the anchor that survives a rename or a move between OUs. Empty when ldap.id_attribute is unconfigured, which leaves resolution on the DN-then-filter path.

string
dn
required

DN is the entry’s distinguished name from the last successful read.

string
first_missing_at

FirstMissingAt is when the entry was first found to be missing, and what the auto-disable threshold is measured against. Absent means the entry is currently resolving.

string
last_seen_at

LastSeenAt is the last successful read of the entry; LastSyncedAt is the last pass that reached the directory at all. They differ exactly when the entry is missing.

string
last_synced_at
string
directory_enabled

DirectoryEnabled mirrors ldap.enabled, and is what disambiguates an absent Directory. Without it, “no directory record” means either “this person has never been enriched” or “the directory is switched off and their record is being withheld” — two states that call for opposite actions from whoever is reading the page.

boolean
directory_overrides
required

DirectoryOverrides names each identity field the directory currently supplies in place of the OIDC value, with both sides shown. Empty when LDAP is disabled, unconfigured, or has never resolved this person — in which case the OIDC values above are what the server acts on.

Array<object>
object
effective
required

Effective is what the directory supplies, and therefore what the server actually acts on for this field.

Array<string>
field
required

Field is the destination name: “other_accounts”, “service_accounts”, “name”, or an operator-chosen extra field.

string
oidc
required

OIDC is what the ID token supplied for this field at the last login, shown even though it lost — a claim mapping that is quietly wrong is invisible otherwise. Empty when the token carried nothing.

Array<string>
disabled_at

DisabledAt is when an admin disabled this user. Omitted if not disabled.

string
disabled_by_user_id

DisabledByUserID and DisabledByUsername identify the admin that disabled this user. Both omitted if not disabled.

string
disabled_by_username
string
disabled_reason

DisabledReason is why the account was disabled, required at the API and shown here because this is where it meets the person deciding whether to re-enable. Omitted if not disabled.

string
disabled_source

DisabledSource says what disabled the account: “admin”, “soc” or “ldap_sync”. It is what makes auto-re-enable safe — the sync clears only its own disables — so it belongs next to the reason rather than only in the database. Omitted if not disabled, and absent on rows predating the column.

string
email
required

Email is the user’s email from OIDC, possibly empty. Not an identifier either, for the same reason as Username.

string
extra_fields
required

ExtraFields are operator-configured extra claims, decoded from stored JSON map.

object
key
additional properties
groups
required

Groups are the persisted group memberships. Both capture paths while ldap.enabled is true; OIDC only when it is false, on the same reasoning as Directory below — the directory rows are frozen, so they are neither shown nor used for notification fan-out until the directory is switched back on. Never an authorization input (see https://mnestor.github.io/ssoossh/internals/invariants/): this is what the server recorded, shown so an operator can see why a notification reached someone, or why a group they expected is missing.

Array<object>
object
first_seen_at
required

FirstSeenAt survives a refresh that keeps the membership, so “since when” is answerable; LastSeenAt is the last capture that saw it.

string
last_seen_at
required
string
name
required

Name is the reduced, comparable name: a memberOf DN is reduced to its CN before it is stored.

string
source
required

Source is “oidc” or “ldap”. The two capture paths never collide and a name can appear under both.

string
id
required

ID is the stable user identifier.

string
name
required

Name is the person’s human-readable name. Display only: it is not a principal, not a key ID input, and never an authorization input. Empty when neither the identity provider nor the directory supplied one.

string
notification_preferences
required

NotificationPreferences is every notification kind the server knows how to send, with what applies to this user and whether they chose it. Registry order, then any stored rows for kinds no longer registered.

Array<object>
object
default

Default is what would apply with no stored choice, so a reader can see at a glance which of these the user actually decided.

boolean
description
string
enabled

Enabled is what applies: the stored choice where there is one, the registered default otherwise.

boolean
explicit

Explicit reports whether the user has stored a choice for this kind. False means Enabled is the registered default.

boolean
kind
required
string
registered

Registered reports whether the kind is still in the server’s registry. False marks a stored row left behind by a removed kind.

boolean
title

Title and Description are the registry’s own wording. Empty for a stored row whose kind is no longer registered — a downgrade or a removed kind leaves rows nothing answers to, and they are still shown rather than dropped, since the choice is real and still stored.

string
updated_at

UpdatedAt is when the choice was last changed, absent when there is no stored choice to have changed.

string
oidc_fields
required

OIDCFields reports, per identity destination, whether authentication.fields names a claim that populates it. It is what separates “the ID token carried nothing for this field” from “nothing was ever asked of the ID token for this field” — two states that read identically on the page and call for opposite actions.

It also decides how a directory value is described. A field with no configured claim has no OIDC side to override, so calling the directory value an override there names a conflict that does not exist; the directory is simply the only source.

Keyed by destination name: “other_accounts”, “service_accounts”, “name”, and one entry per configured extra field.

object
key
additional properties
boolean
other_accounts
required

OtherAccounts, ServiceAccounts, ExtraFields and Name above are the OIDC capture — exactly what the ID token carried at the last login, as stored on the users row. They are deliberately not the values the server acts on: a configured ldap.fields entry overrides its OIDC counterpart wholesale, and DirectoryOverrides below names every field where that is currently happening and what the effective value is.

Showing the OIDC half even when it is overridden is the point. An operator debugging “why is this person missing a principal” needs to see both sides and which one won; showing only the winner makes a misconfigured claim mapping invisible.

Array<string>
service_accounts
required

ServiceAccounts are service accounts from OIDC, decoded from stored JSON.

Array<string>
service_enrollment_count
required

ServiceEnrollmentCount is how many live (not expired) service enrollments this user approved. Provenance, not a consequence of disabling them: the codes belong to their service accounts and keep working (see https://mnestor.github.io/ssoossh/concepts/service-certificates/).

integer
subject
required

Subject is the unique account identifier, read from the claim named by authentication.fields.subject (“sub” by default). It is the only value a login is keyed by, and the only one guaranteed not to move.

string
updated_at
required

UpdatedAt is when the user’s identity was last refreshed at login.

string
username
required

Username is the OIDC claim username. Not an identifier — it changes when a person is renamed, which is what Subject exists to survive.

string

Example generated

{
"certificate_count": 1,
"created_at": "example",
"directory": {
"attributes": {
"additionalProperty": [
"example"
]
},
"consecutive_misses": 1,
"directory_id": "example",
"dn": "example",
"first_missing_at": "example",
"last_seen_at": "example",
"last_synced_at": "example"
},
"directory_enabled": true,
"directory_overrides": [
{
"effective": [
"example"
],
"field": "example",
"oidc": [
"example"
]
}
],
"disabled_at": "example",
"disabled_by_user_id": "example",
"disabled_by_username": "example",
"disabled_reason": "example",
"disabled_source": "example",
"email": "example",
"extra_fields": {
"additionalProperty": "example"
},
"groups": [
{
"first_seen_at": "example",
"last_seen_at": "example",
"name": "example",
"source": "example"
}
],
"id": "example",
"name": "example",
"notification_preferences": [
{
"default": true,
"description": "example",
"enabled": true,
"explicit": true,
"kind": "example",
"registered": true,
"title": "example",
"updated_at": "example"
}
],
"oidc_fields": {
"additionalProperty": true
},
"other_accounts": [
"example"
],
"service_accounts": [
"example"
],
"service_enrollment_count": 1,
"subject": "example",
"updated_at": "example",
"username": "example"
}

Not authenticated

Media typeapplication/json
object
data

Data is always null on an error response.

error
required

Error is a human-readable message. Branch on the HTTP status rather than this string; it is for humans and logs.

string
error_code
required

ErrorCode is a machine-readable error classifier. One of the error_code constants from internal/apitypes; use this to decide whether to retry or branch on the failure mode.

string

Example

{
"error": "certificate request \"9f1c…\" not found",
"error_code": "not_found"
}

Not authorized as auditor

Media typeapplication/json
object
data

Data is always null on an error response.

error
required

Error is a human-readable message. Branch on the HTTP status rather than this string; it is for humans and logs.

string
error_code
required

ErrorCode is a machine-readable error classifier. One of the error_code constants from internal/apitypes; use this to decide whether to retry or branch on the failure mode.

string

Example

{
"error": "certificate request \"9f1c…\" not found",
"error_code": "not_found"
}

User not found

Media typeapplication/json
object
data

Data is always null on an error response.

error
required

Error is a human-readable message. Branch on the HTTP status rather than this string; it is for humans and logs.

string
error_code
required

ErrorCode is a machine-readable error classifier. One of the error_code constants from internal/apitypes; use this to decide whether to retry or branch on the failure mode.

string

Example

{
"error": "certificate request \"9f1c…\" not found",
"error_code": "not_found"
}