Skip to main content

Admin API: Tenants

Operations on tenants in the Odudu admin API, one section each. Every request carries a bearer token; the index says which audience it needs and how a refusal is shaped.

GET /admin/tenants​

Requires the "manage-tenants" capability.

Query parameters​

NameTypeRequiredDetails
cursorstringnoThe next value of the previous page, to continue after it.
limitintegernoHow many items to return. A value over 200 is treated as 200; the default is 50. Constraints: at least 1.
namestringnoOnly tenants whose name starts with this. Constraints: at least 1 character.
display_namestringnoOnly tenants whose display name starts with this. Constraints: at least 1 character.
enabledstringnotrue for enabled tenants only, false for disabled ones. Constraints: one of true, false.

Responses​

200 OK​

Body: application/json

FieldTypeRequiredDetails
itemsarray of objectyesThe entries of this answer.
items[]objectyes
items[].idstringyesThe tenant’s internal identifier, a UUID.
items[].namestringyesThe name in the tenant’s addresses and issuer.
items[].display_namestring or nullyesThe name shown to people on sign-in pages, or null.
items[].enabledbooleanyesA disabled tenant refuses every sign-in and token request.
items[].created_atstringyesWhen the tenant was created.
nextstringnoAn opaque cursor for the next page; absent on the last page.

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

POST /admin/tenants​

Requires the "manage-tenants" capability.

A display_name is trimmed, and a blank one is stored as none (null), which the tenant’s pages replace with its name.

Request body​

Body: application/json

FieldTypeRequiredDetails
namestringyesThe tenant name, used in every address of the tenant (/tenants/{name}/...). 1 to 63 lowercase letters, digits or hyphens, not starting or ending with a hyphen. Constraints: at least 1 character.
display_namestringnoA friendly name for people. Null when none was given. Constraints: no bidirectional control characters, at least 1 character.
{
"name": "acme",
"display_name": "Acme Corporation"
}

Responses​

201 Created​

Body: application/json

FieldTypeRequiredDetails
idstringyesThe tenant’s internal identifier, a UUID.
namestringyesThe name in the tenant’s addresses and issuer.
display_namestring or nullyesThe name shown to people on sign-in pages, or null.
enabledbooleanyesA disabled tenant refuses every sign-in and token request.
created_atstringyesWhen the tenant was created.

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

GET /admin/tenants/count​

Requires the "manage-tenants" capability.

Query parameters​

NameTypeRequiredDetails
namestringnoOnly tenants whose name starts with this. Constraints: at least 1 character.
display_namestringnoOnly tenants whose display name starts with this. Constraints: at least 1 character.
enabledstringnotrue for enabled tenants only, false for disabled ones. Constraints: one of true, false.

Responses​

200 OK​

Body: application/json

FieldTypeRequiredDetails
countintegeryesHow many items match, up to a cap.
cappedbooleanyesTrue when more match than count says, because counting stops at the cap.

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

POST /admin/tenant-imports​

Requires the "manage-tenants" capability.

A set over 200 entries, more than 1,000 scopes, more than 200 scopes marked for every new client, a client given more than 200 scopes, or more than 200 default roles or groups is refused in the same 400, naming the limit and the count. Creates a new tenant from a document GET /admin/tenants/{tenant}/export answered, with its own signing key and a fresh secret for each confidential client, answered once here. A blank display_name, in the request or the document, is stored as none. Every problem with the request is refused together in one 400 whose errors lists each by its JSON path; a name already in use is refused with 409.

Request body​

Body: application/json

FieldTypeRequiredDetails
namestringyesThe name of the tenant to create. Constraints: at least 1 character.
display_namestringnoA friendly name for the new tenant. Constraints: no bidirectional control characters, at least 1 character.
documentanyyesA tenant document, as GET /admin/tenants/{tenant}/export answers it.

Responses​

201 Created​

Body: application/json

FieldTypeRequiredDetails
tenantobjectyesThe tenant that was created.
tenant.idstringyesThe tenant’s internal identifier, a UUID.
tenant.namestringyesThe name in the tenant’s addresses and issuer.
tenant.display_namestring or nullyesThe name shown to people on sign-in pages, or null.
tenant.enabledbooleanyesA disabled tenant refuses every sign-in and token request.
tenant.created_atstringyesWhen the tenant was created.
client_secretsarray of objectyesThe secrets generated for the imported confidential clients.
client_secrets[]objectyes
client_secrets[].client_idstringyesThe imported client.
client_secrets[].secretstringyesIts newly generated secret. Shown only in this response.

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

GET /admin/tenants/{tenant}​

Requires the "manage-tenant" capability.

Path parameters​

NameTypeRequiredDetails
tenantstringyesThe tenant being administered, addressed by name.

Responses​

200 OK​

Body: application/json

FieldTypeRequiredDetails
idstringyesThe tenant’s internal identifier, a UUID.
namestringyesThe name in the tenant’s addresses and issuer.
display_namestring or nullyesThe name shown to people on sign-in pages, or null.
enabledbooleanyesA disabled tenant refuses every sign-in and token request.
created_atstringyesWhen the tenant was created.

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

PATCH /admin/tenants/{tenant}​

Requires the "manage-tenant" capability.

Amends display_name and enabled. A display_name is trimmed, and a blank one is stored as none (null). name is refused with 400: it is already in the issuer URL of every token this tenant has minted. Disabling the system tenant is refused with 409, since every cross-tenant administrator authenticates against it. Once a disable of any other has committed, every live session in it is ended in batches of 500, each its own transaction, queuing a Back-Channel Logout Token for each relying party that used one; the answer comes when all have ended. A batch that fails is answered with 500 sessions-not-ended: the disable stands, and sending enabled: false again ends the rest. Its discovery document and key set stay served.

Path parameters​

NameTypeRequiredDetails
tenantstringyesThe tenant being administered, addressed by name.

Request body​

Body: application/json

FieldTypeRequiredDetails
(body)objectyesConstraints: other fields accepted.

Responses​

200 OK​

Body: application/json

FieldTypeRequiredDetails
idstringyesThe tenant’s internal identifier, a UUID.
namestringyesThe name in the tenant’s addresses and issuer.
display_namestring or nullyesThe name shown to people on sign-in pages, or null.
enabledbooleanyesA disabled tenant refuses every sign-in and token request.
created_atstringyesWhen the tenant was created.

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

DELETE /admin/tenants/{tenant}​

Requires the "manage-tenants" capability.

Deletes the tenant and every row it holds, in one transaction, recorded in the system tenant’s trail as tenant.delete. confirm must be the tenant’s own name, refused with 400 naming it otherwise. Refused with 409 for system; with 409 tenant-enabled until the tenant is disabled, which ends its sessions and queues their Logout Tokens; with 409 sessions-live while any session is still live; and with 409 logout-deliveries-pending until each of those tokens is delivered or has spent every attempt. The tenant’s keys go with it. Refused with 403 when the tenant’s subjects hold an admin capability the caller does not (the target ceiling, over every subject at once).

Path parameters​

NameTypeRequiredDetails
tenantstringyesThe tenant being administered, addressed by name.

Query parameters​

NameTypeRequiredDetails
confirmstringyesConstraints: at least 1 character.

Responses​

204 OK​

Body: application/json

FieldTypeRequiredDetails
(body)anyyes

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

GET /admin/tenants/{tenant}/export​

Requires the "manage-tenant" capability.

A tenant too large for one document is refused with 413: more than 20,000 clients, roles or groups, 200,000 rows of a link table, or one role, group, scope or subject holding more than 200 of a set, which an import would refuse. The tenant’s configuration, every reference by name, with no secret in it: each secret a reader would expect is named under omitted by its JSON path. Additionally requires manage-clients, the capability every other client read requires, refused with 403 otherwise. ?include=subjects adds subjects and additionally requires view-users, refused with 403 otherwise and with 413 above 10,000 subjects.

Path parameters​

NameTypeRequiredDetails
tenantstringyesThe tenant being administered, addressed by name.

Query parameters​

NameTypeRequiredDetails
includestringnoConstraints: always subjects.

Responses​

200 OK​

Body: application/vnd.odudu.tenant+json

FieldTypeRequiredDetails
versionnumberyesThe document format version; always 1. Constraints: always 1.
settingsobjectyesThe tenant’s settings.
settings.display_namestring or nullyesThe name shown to people on sign-in pages. Constraints: no bidirectional control characters.
settings.enabledbooleanyesWhether the tenant accepts sign-ins and token requests.
settings.reset_password_allowedbooleanyesWhether users may ask for a password reset link by mail.
settings.sso_session_idle_secondsintegeryesHow long a browser session may sit idle before it ends.
settings.sso_session_max_secondsintegeryesThe longest a browser session lasts, however active.
settings.password_min_lengthintegeryesThe fewest characters a password may have.
settings.password_require_digitbooleanyesWhether a password must contain a digit.
settings.password_require_uppercasebooleanyesWhether a password must contain an upper-case letter.
settings.password_require_lowercasebooleanyesWhether a password must contain a lower-case letter.
settings.password_require_specialbooleanyesWhether a password must contain a character that is not a letter or digit.
settings.password_not_usernamebooleanyesWhether a password may not equal the username.
settings.password_not_emailbooleanyesWhether a password may not equal the email address.
settings.password_history_depthintegeryesHow many previous passwords a user may not reuse. 0 keeps none.
settings.password_max_age_daysintegeryesDays before a password must be changed. 0 means it never expires.
settings.otp_requiredbooleanyesWhether every user must set up a one-time-code authenticator.
settings.brute_force_max_failuresintegeryesFailed sign-ins in a row before the account is locked out.
settings.brute_force_lockout_secondsintegeryesHow long a lockout lasts.
settings.brute_force_max_lockout_secondsintegeryesThe longest a lockout can grow to when it is tripped again.
settings.brute_force_failure_reset_secondsintegeryesHow long without a failed sign-in before the count starts again.
settings.max_clientsintegeryesThe most clients the tenant may hold.
settings.max_sessions_per_browserintegeryesThe most sessions one browser may hold at once.
settings.remember_me_allowedbooleanyesWhether the sign-in form offers to stay signed in.
settings.remember_me_idle_secondsintegeryesHow long a remembered session may sit idle.
settings.remember_me_max_secondsintegeryesThe longest a remembered session lasts.
settings.audit_retention_daysintegeryesHow many days audit events are kept before odudu reap deletes them.
settings.username_editablebooleanyesWhether a username may be changed after it is set.
settings.access_token_ttl_secondsintegeryesHow long an access token lives unless the client says otherwise.
settings.id_token_ttl_secondsintegeryesHow long an ID token lives unless the client says otherwise.
settings.refresh_token_ttl_secondsintegeryesHow long a refresh token lives unless the client says otherwise.
settings.authorization_code_ttl_secondsintegeryesHow long an authorization code can be redeemed.
settings.login_ttl_secondsintegeryesHow long a started sign-in may take to finish.
settings.verify_email_ttl_secondsintegeryesHow long a mailed email-verification link works.
settings.reset_password_ttl_secondsintegeryesHow long a mailed password-reset link works.
settings.login_with_emailbooleanyesWhether a user may sign in with the email address instead of the username.
settings.audit_event_typesarray of stringyesThe audit event types the tenant stores.
flowarray of objectyesThe sign-in flow, in order.
flow[]objectyes
flow[].authenticatorstringyesThe sign-in step to run.
flow[].requirementstringyesrequired, alternative, conditional or disabled. Constraints: one of required, alternative, conditional, disabled.
clientsarray of objectyesThe tenant’s clients. Secrets are never exported.
clients[]objectyes
clients[].client_idstringyesThe client_id the application sends to the authorization and token endpoints. Chosen when the client is created and unique within the tenant. Constraints: no bidirectional control characters.
clients[].namestringyesA display name for people, shown for example on the consent screen. Constraints: no bidirectional control characters.
clients[].descriptionstring or nullyesFree text for administrators, or null when none was set. Constraints: no bidirectional control characters.
clients[].typestringyespublic for an application that cannot keep a secret (a single-page or native app), which must use PKCE; confidential for one that authenticates at the token endpoint. Constraints: one of public, confidential.
clients[].enabledbooleanyesA disabled client cannot start a sign-in or obtain tokens, and tokens already issued to it stop being accepted.
clients[].full_scope_allowedbooleanyesWhen true the client's tokens carry every role the user holds. When false, a role reaches a token only through a scope the client is assigned.
clients[].registration_originstringyesHow the client came to exist: seeded by odudu seed or odudu init, anonymous or token through dynamic registration, or operator through the admin API. Constraints: one of seeded, anonymous, token, operator.
clients[].redirect_urisarray of stringyesThe addresses the authorization server may send the user back to after sign-in. A request naming any other address is refused. Constraints: at most 200 items.
clients[].grant_typesarray of stringyesThe grants the client may use at the token endpoint, such as authorization_code, refresh_token, client_credentials.
clients[].token_endpoint_auth_methodstringyesHow a confidential client authenticates at the token endpoint, for example client_secret_basic, client_secret_post or private_key_jwt.
clients[].audiencesarray of stringyesThe audiences (aud) added to the access tokens issued to this client, besides the issuer. Constraints: at most 200 items.
clients[].access_token_ttl_secondsinteger or nullyesHow long this client's access tokens live, in seconds. Null uses the tenant's setting of the same name.
clients[].id_token_ttl_secondsinteger or nullyesHow long this client's ID tokens live, in seconds. Null uses the tenant's setting of the same name.
clients[].refresh_token_ttl_secondsinteger or nullyesHow long this client's refresh tokens live, in seconds. Null uses the tenant's setting of the same name.
clients[].client_credentials_scopesarray of stringyesThe scopes the client may request with the client_credentials grant, where it acts as itself. Constraints: at most 200 items.
clients[].web_originsarray of stringyesThe browser origins allowed to call the token and UserInfo endpoints from JavaScript (CORS). Constraints: at most 200 items.
clients[].post_logout_redirect_urisarray of stringyesThe addresses the user may be sent to after a logout the client started. Constraints: at most 200 items.
clients[].jwksany or nullyesThe public keys the client authenticates with when its method is private_key_jwt, as a JWK Set. Null when it uses jwks_uri or a secret.
clients[].jwks_uristring or nullyesThe address the server fetches the client public keys from, instead of holding them in jwks.
clients[].frontchannel_logout_uristring or nullyesThe client page the user agent loads to tell the client the user signed out (front-channel logout).
clients[].backchannel_logout_uristring or nullyesThe client endpoint the server posts a logout token to when a session ends (back-channel logout).
clients[].frontchannel_logout_session_requiredbooleanyesWhether the front-channel logout request carries the session id (sid) and issuer.
clients[].backchannel_logout_session_requiredbooleanyesWhether the back-channel logout token carries the session id (sid).
clients[].consent_requiredbooleanyesWhether the user must approve what the client asks for on a consent screen before tokens are issued.
clients[].token_exchange_impersonation_allowedbooleanyesWhether the client may use token exchange (RFC 8693) to act as a user rather than on behalf of one.
clients[].userinfo_signed_response_algstring or nullyesThe algorithm that signs this client’s UserInfo responses. Null returns plain JSON.
clients[].userinfo_encrypted_response_algstring or nullyesThe key-management algorithm that encrypts this client’s UserInfo responses, or null.
clients[].userinfo_encrypted_response_encstring or nullyesThe content-encryption algorithm for encrypted UserInfo responses, or null.
clients[].tls_client_auth_subject_dnstring or nullyesThe certificate subject a client using tls_client_auth must present, or null.
clients[].client_uristring or nullyesA web page about the client application, shown to users.
clients[].policy_uristring or nullyesThe client privacy policy page, shown to users.
clients[].tos_uristring or nullyesThe client terms of service page, shown to users.
clients[].id_token_signed_response_algstring or nullyesThe algorithm that signs this client’s ID tokens. Null uses the tenant’s default.
clients[].default_max_ageinteger or nullyesThe longest age in seconds a sign-in may have before the user must sign in again, when the request does not say. Null sets none.
clients[].require_auth_timebooleanyesWhether ID tokens always carry the auth_time claim.
clients[].service_account_rolesarray of objectyesConstraints: at most 200 items.
clients[].service_account_roles[]objectyes
clients[].service_account_roles[].namestringyesOnly roles whose name starts with this.
clients[].service_account_roles[].clientstring or nullyesOnly the roles of one owner: tenant for the tenant’s own roles, or a client’s internal id for the roles scoped to it.
rolesarray of objectyesThe tenant’s roles.
roles[]objectyes
roles[].namestringyesThe role name. Constraints: no bidirectional control characters.
roles[].clientstring or nullyesThe client_id the role belongs to, or null for a tenant-wide role.
roles[].descriptionstring or nullyesFree text for administrators, or null. Constraints: no bidirectional control characters.
roles[].default_for_new_subjectsbooleanyesWhether new users are given the role.
roles[].builtinbooleanyesTrue for a role every tenant has, such as the admin roles.
roles[].compositesarray of objectyesThe roles this role contains. Constraints: at most 200 items.
roles[].composites[]objectyes
roles[].composites[].namestringyesOnly roles whose name starts with this.
roles[].composites[].clientstring or nullyesOnly the roles of one owner: tenant for the tenant’s own roles, or a client’s internal id for the roles scoped to it.
groupsarray of objectyesThe tenant’s groups.
groups[]objectyes
groups[].pathstringyesThe group’s place in the hierarchy, such as /staff/eu. Constraints: no bidirectional control characters.
groups[].descriptionstring or nullyesFree text for administrators, or null. Constraints: no bidirectional control characters.
groups[].default_for_new_subjectsbooleanyesWhether new users join the group.
groups[].rolesarray of objectyesThe roles every member of the group is given. Constraints: at most 200 items.
groups[].roles[]objectyes
groups[].roles[].namestringyesOnly roles whose name starts with this.
groups[].roles[].clientstring or nullyesOnly the roles of one owner: tenant for the tenant’s own roles, or a client’s internal id for the roles scoped to it.
scopesarray of objectyesThe tenant’s client scopes.
scopes[]objectyes
scopes[].namestringyesThe scope name clients request.
scopes[].descriptionstring or nullyesFree text for administrators, or null. Constraints: no bidirectional control characters.
scopes[].include_in_id_tokenbooleanyesWhether its claims appear in the ID token.
scopes[].include_in_access_tokenbooleanyesWhether its claims appear in the access token.
scopes[].default_client_assignmentstring or nullyesHow every new client gets the scope, or null for neither. Constraints: one of default, optional.
scopes[].consent_textstring or nullyesThe sentence the consent screen shows for it, or null. Constraints: no bidirectional control characters.
scopes[].display_orderintegeryesWhere it sorts on the consent screen; lowest first.
scopes[].builtinbooleanyesTrue for a scope every tenant has.
scopes[].rolesarray of objectyesThe roles the scope carries into tokens. Constraints: at most 200 items.
scopes[].roles[]objectyes
scopes[].roles[].namestringyesOnly roles whose name starts with this.
scopes[].roles[].clientstring or nullyesOnly the roles of one owner: tenant for the tenant’s own roles, or a client’s internal id for the roles scoped to it.
scopes[].mappersarray of stringyesThe claim mappers the scope uses.
scopes[].clientsarray of objectyesThe clients the scope is assigned to, by client_id.
scopes[].clients[]objectyes
scopes[].clients[].client_idstringyes
scopes[].clients[].assignmentstringyesConstraints: one of default, optional.
registration_policyobjectyesWho may register themselves and register clients.
registration_policy.registration_allowedbooleanyesWhether people may register themselves.
registration_policy.verify_emailbooleanyesWhether a registered address must be verified before sign-in.
registration_policy.client_registration_policystringyesWho may register clients dynamically: nobody (disabled), anybody (open) or the holder of an initial access token (token). Constraints: one of disabled, open, token.
smtpobject or nullyesThe tenant’s mail relay, or null when it has none.
smtp.hoststringyesThe SMTP relay’s host.
smtp.portintegeryesThe relay’s port.
smtp.from_addressstringyesThe address mail is sent from.
smtp.usernamestring or nullyesThe user name for the relay, or null. The password is never exported.
smtp.starttlsbooleanyesWhether STARTTLS is required.
subjectsarray of objectnoThe tenant’s users, only when requested with include=subjects.
subjects[]objectyes
subjects[].usernamestringyesThe sign-in name. Constraints: no bidirectional control characters.
subjects[].emailstring or nullyesThe email address, or null.
subjects[].enabledbooleanyesWhether the subject may sign in.
subjects[].profileobjectyesThe standard profile claims.
subjects[].profile.namestring or nullyes
subjects[].profile.given_namestring or nullyes
subjects[].profile.family_namestring or nullyes
subjects[].profile.middle_namestring or nullyes
subjects[].profile.nicknamestring or nullyes
subjects[].profile.preferred_usernamestring or nullyes
subjects[].profile.profilestring or nullyes
subjects[].profile.picturestring or nullyes
subjects[].profile.websitestring or nullyes
subjects[].profile.genderstring or nullyes
subjects[].profile.birthdatestring or nullyes
subjects[].profile.zoneinfostring or nullyes
subjects[].profile.localestring or nullyes
subjects[].profile.phone_numberstring or nullyes
subjects[].profile.phone_number_verifiedbooleanyes
subjects[].profile.email_verifiedbooleanyes
subjects[].profile.address_formattedstring or nullyes
subjects[].profile.address_streetstring or nullyes
subjects[].profile.address_localitystring or nullyes
subjects[].profile.address_regionstring or nullyes
subjects[].profile.address_postal_codestring or nullyes
subjects[].profile.address_countrystring or nullyes
subjects[].rolesarray of objectyesThe roles the subject holds directly. Constraints: at most 200 items.
subjects[].roles[]objectyes
subjects[].roles[].namestringyesOnly roles whose name starts with this.
subjects[].roles[].clientstring or nullyesOnly the roles of one owner: tenant for the tenant’s own roles, or a client’s internal id for the roles scoped to it.
subjects[].groupsarray of stringyesThe groups the subject belongs to, by path. Constraints: at most 200 items.
subjects[].required_actionsarray of stringyesThe actions the subject must complete at next sign-in.
omittedarray of stringyesWhat the document leaves out on purpose, such as secrets and credentials.

401 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

403 RFC 9457 problem details​

Body: application/problem+json, an RFC 9457 problem document described under problem details.

Documentation coverage: 175 of 202 fields described (86%).