Skip to main content

Trust and identity

Who is asking, and who vouches for them? PolicyArc answers both before policy decides anything. It holds no passwords. Users sign in at an identity provider you connect, and the server trusts other parties only when a control row names them.

How it works​

Users come from identity providers. You register each OpenID Connect provider with its issuer, client ID and secret, scopes and endpoints. At /authorize the server sends the user to one of them and reads the result. First environment connects one.

Policy picks the provider. The row pbac.operator.controls.idp_selection routes each authorization request. Its settings apply in this order: acr_to_idp, then resource_to_idp keyed by resource indicator, then resource_type_to_idp, then default_idp.

A user is a subject. Policy sees the user at input.subject, with the sub from the provider, the provider key in idp, the acr, and identity claims the server captured at sign-in. The same person signing in through two providers is two subjects. A token keeps its subject when it is refreshed or exchanged.

Other parties are trusted by row. These rows say who else the server believes.

RowSettingWhat it trusts
pbac.operator.controls.trusttrusted_issuersIssuers of software statements, and of ID tokens sent to token exchange
pbac.operator.controls.trustspiffe_trust_bundle_urlThe keys that sign SPIFFE JWT-SVIDs
pbac.operator.controls.spiffe_policytrust_domainsWhich SPIFFE trust domains get tokens

A new environment has no trust row, so it accepts no software statement and no SPIFFE workload until you add one.

The denylist overrides trust. The row pbac.operator.controls.denylist refuses what it lists, wherever the server can see it.

SettingChecked at
client_ids/authorize and /token, and at introspection of tokens already issued
subjects/authorize and /token, and at introspection of tokens already issued
domains/register for redirect and client URIs, and /authorize for the redirect URI
software_ids, issuers/register only

We listed a client in client_ids after it got a token. Its next token request was refused with reason client_denied, and its existing token introspected as inactive. We did the same for a user in subjects, with a token from a token exchange. The next exchange was refused and the existing token introspected as inactive. In both cases inactive_reason was insufficient_scope.

Why it works this way​

Every trust decision is a row that policy reads, not a setting baked into the server. An admin changes one with the Admin API, the change is audited, and it takes effect within seconds without a restart. A missing or empty trusted_issuers list trusts no issuer, and a missing bundle URL admits no SPIFFE workload, so a new environment starts closed. trust_domains is the exception. Left empty, it accepts every domain the bundle signs for.

What this means for you​

Before a software statement or a SPIFFE workload can work, write the trust row. Keep trusted_issuers and spiffe_trust_bundle_url in the same PUT, because a PUT replaces the row. To cut off a client or a user at once, use the denylist. Control data shows how to write each row.

Next steps​