Skip to main content

Interface: IdentityProvider

Defined in: packages/identity/src/port.ts:605

The identity provider behind the platform, as the setup engine and the identity module see it: one port, a Keycloak adapter and a WSO2 Identity Server adapter, and one contract suite that passes against both (chapter 07, vendor adapters).

Rules every method keeps​

  • Realms are named logically (oktoid-staff, oktoid-holders). The adapter maps a name to its own thing: a Keycloak realm, a WSO2 tenant domain.
  • No vendor type crosses the port. Views carry no provider id, no representation, no secret. A client's secret is write-only.
  • ensure* converges. It reads, writes only what differs, and says what it wrote (Ensured). A second call with the same input changes nothing.
  • The engine owns what it marked. An object created here carries the managed marker (marker.ts). An object without it is never modified, except that IdentityProvider.assertNoDirectGrants switches off the direct grant on every client, because a built-in one is a risk we did not create.
  • Nothing deletes. A deleted client's secret and the azp of tokens it issued cannot be recovered for an audit. IdentityProvider.disableClient is the strongest action. The setup engine calls it on a managed client the answer file no longer declares (PO-QA1) and, in a bundled deployment, on an undeclared one (PO-QA2); never on a provider's built-in client (BUILT_IN_CLIENTS).
  • Nothing re-enables. A disabled client was disabled by someone, on purpose or by the engine on a removal; IdentityProvider.ensureClient converges the rest of it and leaves it disabled. Enabling it again is an administrator's act at the provider. The one way round it: a disabled client that somebody deletes at the provider, while the answer file still declares it, is created again on the next run, and a created client is enabled. Disabling, not deleting, is how a client is kept off.
  • Errors name what to do. See IdentityProviderErrorCode; an unreachable provider is indeterminate, never a refusal.

Order​

ensureRealm, then ensureScope, then ensureAudienceMapper, then ensureClient, then ensureSessionSettings, then assertNoDirectGrants.

Properties​

kind​

readonly kind: "keycloak" | "wso2"

Defined in: packages/identity/src/port.ts:607

Which vendor this is.

Methods​

assertNoDirectGrants()​

assertNoDirectGrants(realm): Promise<Ensured<{ checked: number; }>>

Defined in: packages/identity/src/port.ts:790

Turns the direct access grant off on every client of the realm that has it on, ours and the provider's built-ins, then reads the realm again and refuses to return while any client still allows it. Run on every converge.

Parameters​

realm​

string

Returns​

Promise<Ensured<{ checked: number; }>>

changed when it had to switch any off; the value counts clients checked.

Throws​

direct_grants_remain, naming the clients.


disableClient()​

disableClient(realm, clientId, options?): Promise<Ensured<ClientView>>

Defined in: packages/identity/src/port.ts:681

Disables a client so it can obtain no tokens. Does not delete it and does not revoke tokens already issued, which expire with their own lifetime. Idempotent: a disabled client gives changed: false.

By default only a managed client (PO-QA1: one the answer file no longer declares). With options.undeclared, also one without the marker (PO-QA2: a client nobody declared, in a realm the platform owns). Never a provider's built-in client (BUILT_IN_CLIENTS), whatever the options say.

Disabling is what keeps a client off. A disabled client that is then deleted at the provider is created again, enabled, by IdentityProvider.ensureClient on the next run if the answer file still declares it.

Parameters​

realm​

string

clientId​

string

options?​

DisableClientOptions

Returns​

Promise<Ensured<ClientView>>

Throws​

not_managed for a built-in client, and for a client without the marker unless options.undeclared; not_found when there is none.


ensureAudienceMapper()​

ensureAudienceMapper(realm, spec): Promise<Ensured<MapperView>>

Defined in: packages/identity/src/port.ts:697

Makes tokens obtained with spec.scope carry spec.audience. On Keycloak that is a mapper on the client scope; on WSO2 it is a registry the adapter keeps and applies to each client when the client is ensured.

Parameters​

realm​

string

spec​

AudienceMapperSpec

Returns​

Promise<Ensured<MapperView>>

Throws​

not_found when the scope does not exist.


ensureClient()​

ensureClient(realm, spec): Promise<Ensured<ClientView>>

Defined in: packages/identity/src/port.ts:659

Creates or converges one client and marks it managed. Direct access grants are always off. See ClientSpec for what each kind must be.

A client that exists and is disabled stays disabled (value.enabled is false): the call converges everything else about it and never enables it. A machine client's secret is then neither tested nor set, because a disabled client obtains no token to test it with; on WSO2 that also means a disabled application is never deleted and created again for a new secret. The caller reports the client; the secret converges on the first run after an administrator enables it.

A client that does not exist is created, enabled. That includes one the engine or an administrator disabled and somebody then deleted at the provider: if the answer file still declares it, the next run brings it back, enabled.

Parameters​

realm​

string

spec​

ClientSpec

Returns​

Promise<Ensured<ClientView>>

Throws​

conflict when a client of that id exists and is not managed; not_found when a named scope does not exist.


ensureRealm()​

ensureRealm(realm, spec): Promise<Ensured<RealmView>>

Defined in: packages/identity/src/port.ts:639

Creates the realm if absent, then converges the settings in spec.

Parameters​

realm​

string

The logical realm name.

spec​

RealmSpec

What it should be. Absent fields are left alone.

Returns​

Promise<Ensured<RealmView>>

Throws​

forbidden when the credential may not create realms.


ensureScope()​

ensureScope(realm, spec): Promise<Ensured<ScopeView>>

Defined in: packages/identity/src/port.ts:688

Creates or converges a scope.

Parameters​

realm​

string

spec​

ScopeSpec

Returns​

Promise<Ensured<ScopeView>>


ensureSessionSettings()​

ensureSessionSettings(realm, settings): Promise<Ensured<SessionView>>

Defined in: packages/identity/src/port.ts:777

Converges the session and token settings in settings (absent fields are left alone). Settings the provider keeps per client are applied to every managed client. Fields it cannot set are in unsupported.

Parameters​

realm​

string

settings​

Partial<SessionSettings>

Returns​

Promise<Ensured<SessionView>>


ensureUser()​

ensureUser(realm, spec): Promise<EnsuredUser>

Defined in: packages/identity/src/port.ts:712

Creates a user if absent and returns its provider subject. A user that exists is not modified (the person may already have completed the actions) and no mail is sent. For a new user with invite, sends the required-action mail through the realm's SMTP; a mail failure is reported in invitationFailure and does not undo the user.

Parameters​

realm​

string

spec​

UserSpec

Returns​

Promise<EnsuredUser>


listClients()​

listClients(realm): Promise<readonly ClientView[]>

Defined in: packages/identity/src/port.ts:662

Every client of the realm, ours and the provider's own, with managed set.

Parameters​

realm​

string

Returns​

Promise<readonly ClientView[]>


listMappers()​

listMappers(realm, scope?): Promise<readonly MapperView[]>

Defined in: packages/identity/src/port.ts:703

The audience mappers of the realm, optionally of one scope.

Parameters​

realm​

string

scope?​

string

Returns​

Promise<readonly MapperView[]>


probe()​

probe(realm?): Promise<ProbeResult>

Defined in: packages/identity/src/port.ts:620

Reads the server's version and tries each management capability: first a read, then a create-and-delete of an object named oktoid-probe-<uuid>. Whatever it creates it deletes, including on failure. It never writes outside the probe's own objects.

Parameters​

realm?​

string

The realm to probe the realm-scoped capabilities in. When it does not exist those capabilities are indeterminate; probe again after ensureRealm.

Returns​

Promise<ProbeResult>

Every capability, once. Use probeOutcome for D1.14's three outcomes.

Throws​

credential_refused when the credential is not accepted at all; every other failure becomes a capability state.


readInvitationAcceptance()​

readInvitationAcceptance(realm, subject): Promise<InvitationAcceptance>

Defined in: packages/identity/src/port.ts:753

Reads whether an invited user has taken up their account. A read: it changes nothing and sends no mail. It exists for the setup wizard's last step, which waits for the root account's holder and has no other way to see them arrive.

What each adapter reads:

  • Keycloak: the user's outstanding required actions and whether a password credential exists. Keycloak clears an action when the person completes it, so none outstanding and a password present is accepted; anything else is pending. It never answers unknown.
  • WSO2: the account state WSO2 keeps for its ask-password and forced-reset flows. A PENDING_* state is pending and UNLOCKED is accepted. It says the password was set and nothing about the second factor, which a WSO2 user enrols at their first sign-in with no per-user record the management API returns. A user without the claim, or locked or disabled, is unknown.

A user who existed before the engine ran and was never invited reads accepted on both, which is true of the account and says nothing of an invitation.

Parameters​

realm​

string

subject​

string

The provider subject from IdentityProvider.ensureUser.

Returns​

Promise<InvitationAcceptance>

Throws​

not_found when the realm or the user does not exist; unreachable when the provider could not be asked, which a caller reports as not checked.


readRealm()​

readRealm(realm, options?): Promise<RealmView | undefined>

Defined in: packages/identity/src/port.ts:630

Reads a realm: its issuer and keys (from its own discovery document, never composed from configuration) and the settings this port knows.

Parameters​

realm​

string

The logical realm name.

options?​

ReadRealmOptions

What to read beyond the realm's settings; see ReadRealmOptions.

Returns​

Promise<RealmView | undefined>

The realm, or undefined when it does not exist.


readSessionSettings()​

readSessionSettings(realm): Promise<SessionView>

Defined in: packages/identity/src/port.ts:770

Reads the session and token settings, for the bring-your-own report, without writing.

Parameters​

realm​

string

Returns​

Promise<SessionView>


resendInvitation()​

resendInvitation(realm, subject, actions, options?): Promise<void>

Defined in: packages/identity/src/port.ts:721

Sends the required-action mail again, to a user the engine created.

Parameters​

realm​

string

subject​

string

The provider subject from IdentityProvider.ensureUser.

actions​

readonly ("verify-email" | "update-password" | "configure-totp")[]

options?​

InvitationOptions

The link's lifetime; see InvitationOptions.

Returns​

Promise<void>

Throws​

rejected when the realm has no working SMTP.


resolveMachineIdentity()​

resolveMachineIdentity(realm, clientId, secret): Promise<MachineIdentity | undefined>

Defined in: packages/identity/src/port.ts:761

Proves a machine client's secret is the one the engine holds, by using it, and returns what its tokens say. The secret is sent and never read back.

Parameters​

realm​

string

clientId​

string

secret​

Secret<string>

Returns​

Promise<MachineIdentity | undefined>

The identity, or undefined when the provider refuses the secret.