AXIAM PHP SDK

OidcPolicy
in package
implements JsonSerializable

FinalYes

OpenID Connect surface controls (X7 G8, plan §4.6/§4.8; T21.4). Settings that are not password rules, here because this is the org-baseline-plus-tenant-override surface every other per-tenant control lives on. They are not all of the same kind as each other, and which is which is the whole of what [`validate_tenant_override`] and [`clamp_overrides_to_org`] read, so it is set out rather than inferred. **Ordered** — a tenant may be stricter than its organization and never more permissive: * [`Self::sensitive_scopes_enabled`], validated **disable-only** — the mirror image of `mfa_enforced`, because releasing personal data is the less-restrictive direction, so a tenant can turn its organization's decision off but never on. * [`Self::saml_idp_enabled`], validated **disable-only** exactly like [`Self::sensitive_scopes_enabled`] (D-20): a tenant may turn its organization's `true` off and never its `false` on. * [`Self::ssf_enabled`], validated **disable-only** the same way (D-45). * [`Self::dynamic_registration`], on the ladder `disabled` → `initial_access_token` → `anonymous`: a tenant may move down it and never up. * [`Self::dcr_max_clients`] and [`Self::dcr_unused_client_ttl_days`], on the ordinary `tenant <= org` rule — with the wrinkle that `0` on the second means *never sweep*, which is the longest window of all and is handled by [`dcr_ttl_strictness`]. **Not ordered**, therefore never validated against the baseline and never clamped: * [`Self::default_locale`]. A language is a presentation preference; there is no sense in which Italian is stricter than French. * [`Self::dcr_allowed_scopes`], [`Self::dcr_allowed_redirect_hosts`] and [`Self::external_client_allowed_resources`]. Each names per-tenant resources — *this* tenant's MCP servers, *this* tenant's callback hosts — and there is no sense in which one such list is stricter than another. A subset rule would force an organization to enumerate every tenant's resource servers in its own baseline before any tenant could name one. The model's rule is "a tenant may only be more restrictive", which binds every field that *has* a restrictiveness; a field that has none cannot violate it. One cross-field interlock spans both groups and is checked on the resolved policy rather than on either input: see [`validate_dcr_policy`].

Table of Contents

Interfaces

JsonSerializable

Properties

$cimd  : CimdPolicy|null
$dcrAllowedRedirectHosts  : array<string|int, mixed>|null
$dcrAllowedScopes  : array<string|int, mixed>|null
$dcrMaxClients  : int|null
$dcrUnusedClientTtlDays  : int|null
$defaultLocale  : string|null
$dynamicRegistration  : string|null
$externalClientAllowedResources  : array<string|int, mixed>|null
$samlIdpEnabled  : bool|null
$sensitiveScopesEnabled  : bool
$ssfEnabled  : bool|null
$ssfInactiveReason  : string|null

Methods

__construct()  : mixed
Constructs a OidcPolicy.
fromArray()  : self
Rebuilds a OidcPolicy from one decoded JSON object.
jsonSerialize()  : array<string, mixed>
Renders this object for `json_encode()`.
toArray()  : array<string, mixed>
Renders this object back to its wire form.

Properties

$dcrAllowedRedirectHosts read-only

public array<string|int, mixed>|null $dcrAllowedRedirectHosts = null

$dcrAllowedScopes read-only

public array<string|int, mixed>|null $dcrAllowedScopes = null

$dcrMaxClients read-only

public int|null $dcrMaxClients = null

$dcrUnusedClientTtlDays read-only

public int|null $dcrUnusedClientTtlDays = null

$defaultLocale read-only

public string|null $defaultLocale = null

$dynamicRegistration read-only

public string|null $dynamicRegistration = null

$externalClientAllowedResources read-only

public array<string|int, mixed>|null $externalClientAllowedResources = null

$samlIdpEnabled read-only

public bool|null $samlIdpEnabled = null

$sensitiveScopesEnabled read-only

public bool $sensitiveScopesEnabled

$ssfEnabled read-only

public bool|null $ssfEnabled = null

$ssfInactiveReason read-only

public string|null $ssfInactiveReason = null

Methods

__construct()

Constructs a OidcPolicy.

public __construct(bool $sensitiveScopesEnabled[, CimdPolicy|null $cimd = null ][, array<int, string>|null $dcrAllowedRedirectHosts = null ][, array<int, string>|null $dcrAllowedScopes = null ][, int|null $dcrMaxClients = null ][, int|null $dcrUnusedClientTtlDays = null ][, string|null $defaultLocale = null ][, string|null $dynamicRegistration = null ][, array<int, string>|null $externalClientAllowedResources = null ][, bool|null $samlIdpEnabled = null ][, bool|null $ssfEnabled = null ][, string|null $ssfInactiveReason = null ]) : mixed
Parameters
$sensitiveScopesEnabled : bool

Whether address and phone may be registered on a client, requested at the authorization endpoint, and released at UserInfo (X7 G8). Off unless an organization turns it on. The two scopes release a postal address and a telephone number — categories of personal data AXIAM has no other use for — so the deployment that has never thought about them releases nothing, and the operator who has thought about them says so once, at the organization level, where the lawful basis for holding the data was decided. The switch is a capability, not a grant: with it on, a client still has to register the scope, the request still has to ask for it, and the user still has to have consented. It is the first of four gates, and it is the only one an operator can close for everybody at once.

$cimd : CimdPolicy|null = null

T21.5 — whether a URL-shaped client_id is resolved by fetching the document it names, and on what terms. See [CimdPolicy]; off unless somebody turns it on (I1). Nested, and therefore inherited or overridden whole: the fields are terms of one decision, and a half-merged posture is one neither the organization nor the tenant wrote. (optional)

$dcrAllowedRedirectHosts : array<int, string>|null = null

T21.4 — hosts a self-registered client's redirect_uris may point at, as globs (*.example.com, or * for any). The loopback hosts (127.0.0.1, [::1], localhost) are always allowed whatever this says, because RFC 8252 §7.3 is how every desktop MCP client receives its callback and a tenant that forbade them would have turned registration on for nobody. (optional)

$dcrAllowedScopes : array<int, string>|null = null

T21.4 — the scopes a self-registered client may ask for. A scope a registration names that is not on this list is invalid_client_metadata; an empty list means a self-registered client gets no scopes at all, which is the honest default for a tenant that has turned registration on without deciding what it grants. May not contain address or phone — see this module's [sensitive_scope_in_dcr_list]. (optional)

$dcrMaxClients : int|null = null

T21.4 — how many externally registered clients this tenant may hold. See [DEFAULT_DCR_MAX_CLIENTS]. Counted once per mechanism, against the same number (T21.8): managed_by: dcr rows and managed_by: cimd rows each have this many. So a tenant running both cannot have shadow rows materialised from documents exhaust the allowance for self-registration, or the reverse. The CIMD count is checked before* the document is fetched, so a tenant at its ceiling is not an outbound amplifier either. It keeps its dcr_ name because dynamic registration defined it, on the same precedent as [Self::dcr_allowed_scopes]. (optional)

$dcrUnusedClientTtlDays : int|null = null

T21.4 — how long an externally registered client survives without being used. See [DEFAULT_DCR_UNUSED_CLIENT_TTL_DAYS]. 0 disables the sweep for this tenant, which an operator who prunes out of band may legitimately want. Two sweeps read it, over different clocks (T21.8). A managed_by: dcr row is measured from its last authorization, falling back to when it was registered. A managed_by: cimd row is measured from the last time its document was presented, which every authorize, token and PAR request moves — so a document in daily use is never swept however old its registration is, and one nobody has presented since the window is, and re-materialises on the next request if it is still published. Like the ceiling, it keeps its dcr_ name. (optional)

$defaultLocale : string|null = null

The BCP 47 tag the sign-in page falls back to when the relying party's ui_locales selects nothing (W5's chain, plan §4.6). None means "no tenant preference", which lands on the deployment default (en) — the behaviour every deployment had before this field existed. A tag this build does not ship also lands there: the parse is exact rather than a language lookup, so a stored fr-CA reads as "somebody wrote something this binary does not ship" rather than as a guess at French. Stored as a string rather than as the Locale enum because that enum lives in axiam-oauth2, four layers above this crate, and the crate layering points inward. (optional)

$dynamicRegistration : string|null = null

T21.4 — whether a client may register itself (RFC 7591), and on what terms. disabled unless somebody says otherwise (I1). (optional)

$externalClientAllowedResources : array<int, string>|null = null

D3 — the audiences an externally registered client may address. The single most important field on this policy, and the reason the settings handler refuses dynamic_registration: anonymous while it is empty. A client an unrelated party registered cannot declare its own allowed_resources; it inherits this list verbatim, so what a stranger can mint a token for* is a decision the tenant took in advance rather than one the registration request makes. Empty means an externally registered client can obtain only today's axiam:user tokens — which AXIAM's own APIs accept. That is why the interlock exists: the empty list is not a safe default for an open registration endpoint, it is the most dangerous one. Shared with T5 (CIMD), which inherits the same list for the same reason. (optional)

$samlIdpEnabled : bool|null = null

G-2 / D-20 — whether this tenant may act as a SAML 2.0 identity provider: publish IdP metadata and accept AuthnRequests on /saml/v2/{tenant}/{metadata,sso,slo}. Off unless an organization turns it on. A SAML IdP issues assertions that other systems accept as proof of identity, so a deployment that has never decided to be one issues none, and the three endpoints answer 404 as if they did not exist. The switch lives on this policy, beside the other OpenID Provider surface controls, because the SSO endpoint is the same browser login hop and OP session with a different wire format. Disable-only, with the shape of [Self::sensitive_scopes_enabled]: a tenant may turn its organization's true off but never its false on, because the decision to issue identity assertions on behalf of the organization's tenants is the organization's. A deployment built without the saml feature answers 404 whatever this says; the setting is a capability, not a grant (each SP must still be registered, and allow_idp_initiated is its own opt-in). (optional)

$ssfEnabled : bool|null = null

G-5 / D-45 — whether the tenant is a Shared Signals Framework transmitter: its /.well-known/ssf-configuration is served, its receivers can use the stream management API, and events are signed and transmitted on its streams. Default false. Disable-only, with the shape of [Self::saml_idp_enabled]: sending security events about the organization's users to third parties is the organization's decision. Streams can be registered while it is off; they carry nothing until it is on. (optional)

$ssfInactiveReason : string|null = null

Read-only, D-55: set on a settings response when ssf_enabled is on but the transmitter is inactive anyway, saying why — the deployment holds more than one tenant and serves no per-tenant issuers. Never stored. (optional)

fromArray()

Rebuilds a OidcPolicy from one decoded JSON object.

public static fromArray(array<string, mixed> $data) : self
Parameters
$data : array<string, mixed>

The raw wire object.

Return values
self

jsonSerialize()

Renders this object for `json_encode()`.

public jsonSerialize() : array<string, mixed>

Any Sensitive it carries stays WRAPPED here, so a log line or a json_encode($model) in application code prints [SENSITIVE]. The one place a secret is revealed is ManagementTransport, on the way to the wire and nowhere else (§27.5).

Rendering for a log line never fails: an unknown union arm renders its discriminator and an unrecognised enum value its empty Unknown spelling here, and only self::toArray() -- the request path -- refuses to send it (CONTRACT.md §31.2, §34.2 P12.2).

Return values
array<string, mixed>

toArray()

Renders this object back to its wire form.

public toArray() : array<string, mixed>

§27.4 rule 5: a null property is OMITTED, not emitted as null. On a sparse update those two say opposite things — "leave this alone" versus "set this to nothing" — and only omission means the first.

Return values
array<string, mixed>
On this page

Search results