Skip to content

Federation

Federation is gated by [federation] enabled = true (off by default). An enabled provider appears on the interactive login page by default. A client can narrow that set in Clients → Edit → Federated identity providers: Allow all (the default), an explicit list of provider slugs, or an empty list to disable federation for that client. The broker checks the setting at start, callback, and code issuance; hiding a button is not the security gate. The OIDC broker validates the original client’s authorization request, binds a single-use upstream state to the browser session and tenant, sends PKCE S256 and nonce, verifies the upstream ID token, and resumes the original authorization code flow. The local authorization code it issues is single-use and bound to the original client’s PKCE verifier.

OIDC federation has not yet been verified against a live upstream provider. OIDC discovery and token/JWKS exchange use the public-HTTPS egress gate, so an upstream reachable only on localhost or a private address is deliberately rejected. Use a publicly routable HTTPS hostname with a valid certificate, including for a trial environment; do not disable the egress gate to reach a private upstream.

By default, accounts are exact-link or zero-role JIT creations: an upstream email never selects an existing local account. A provider admin may explicitly enable verified-email matching for that provider; both the upstream and local email must be verified, and administrator accounts are excluded. Keep this switch off unless the upstream is trusted to verify email ownership for the whole tenant.

Browser handoffs are limited per source address, tenant, and provider: at most 30 starts and 60 callbacks in a rolling 60-second window. Exceeding a limit returns HTTP 429 before another upstream discovery request or callback exchange. Pending sign-in and account-link handoffs, and the single-use upstream logout return states, are stored in the tenant’s namespaced key-value family and consumed atomically, so they are single-use across every node of a clustered deployment (PostgreSQL mode) and survive an individual node’s restart; expired records are swept opportunistically. The per-source attempt limiter is counted per node: a deployment of N nodes behind one address effectively allows N times the window limits, which is the standard per-node rate-limiting degradation rather than a correctness gap.

For Google Workspace, optionally set Google Workspace domain to the organization’s hosted domain (for example example.com). Tinyguard then requires the exact hd claim in Google’s signature-verified ID token; an email address ending in that domain is not sufficient. Leave the field empty to allow ordinary Google accounts. This restriction applies only to the Google issuer.

Topologies: through TinyGuard, or directly

Section titled “Topologies: through TinyGuard, or directly”

A TinyBlox component that signs people in can reach a customer’s directory in two ways, and both are supported.

  • Brokered. The component trusts TinyGuard, and TinyGuard federates the customer’s OIDC or SAML directory (this page). Each customer is its own TinyGuard tenant with its own issuer, and the component sees one claim shape whatever the customer uses. This is the platform’s default, and the one to choose when customers bring different directories; the recommended set-up below shows it end to end.
  • Direct. The component trusts the customer’s identity provider itself. TinyGuard is not in the path. The component’s own claim mapping must then absorb what that provider does differently:
Provider What to handle
Keycloak Roles are in realm_access.roles and resource_access.<client>.roles, so the mapping must read dotted paths.
Entra ID A user in more than 200 groups gets an overage link instead of a groups claim; app roles arrive as roles; aud may be api://….
Okta groups appears only when a groups claim filter is configured on the authorization server.
Auth0 Custom claims must be namespaced URLs such as https://example.com/roles.

Brokering removes these differences for the component, at the cost of one more hop and of TinyGuard being in the sign-in path.

Section titled “Recommended set-up: employees and customers”

The usual shape of a customer’s tenant: the customer’s employees sign in with their company account, the customer’s own customers sign in with a TinyGuard account (or their Google account), and the application gives each kind of person different rights. The application trusts one identity provider, the customer’s TinyGuard tenant, and tells the two kinds apart by group. Nobody maintains group membership by hand.

This example uses a company called Contoso, whose staff are in Microsoft Entra ID with addresses at contoso.com, and TinyConductor as the application.

The deployment needs tenants, federation and self-registration switched on:

[tenants]
enabled = true
[federation]
enabled = true
[users]
registration_enabled = true

Each step can be done in the console or over the API. In the API examples, $GUARD is the deployment’s address (https://guard.example.com) and $AUTH the contoso tenant administrator’s credentials.

1. Create the tenant and connect the company directory. Create the tenant contoso (see Tenants). In its Identity providers, add a Microsoft Entra ID provider for Contoso’s directory and set Default groups to employees. Over the API:

Terminal window
curl -X POST "$GUARD/auth/v1/contoso/providers" -H "$AUTH" -H 'Content-Type: application/json' -d '{
"slug": "contoso-entra",
"display_name": "Contoso Entra ID",
"issuer": "https://login.microsoftonline.com/<directory-id>/v2.0",
"client_id": "<application-id>",
"client_secret": "<client-secret>",
"subject_mode": "entra_oid",
"default_groups": ["employees"],
"enabled": true
}'

Register the provider’s callback address (https://guard.example.com/auth/v1/contoso/providers/contoso-entra/callback) in the Entra application. Every account this provider creates joins employees. With the provider’s every-sign-in refresh on (sync_profile_on_login), someone removed from the group by mistake is put back at their next sign-in.

2. Claim the company’s domain. Claim contoso.com with the Entra provider as its target:

Terminal window
curl -X PUT "$GUARD/auth/v1/contoso/sign_in_domains/contoso.com" -H "$AUTH" \
-H 'Content-Type: application/json' \
-d '{"target": {"kind": "provider", "provider": "contoso-entra"}}'

Contoso publishes the TXT record the answer names (_tinyguard-challenge.contoso.com, value tinyguard-verification=…). Once TinyGuard finds it, the domain is verified; see Sign-in domains.

3. Create the groups and let customers register. Create the groups employees and customers in the tenant. Set the tenant’s registration default groups to customers (registration.default_groups), and keep e-mail confirmation on (the default), so every customer’s address is confirmed. Optionally add providers your customers use, such as Google or Microsoft personal accounts, each with Default groups customers.

4. Register the application. Add TinyConductor as a client of the tenant (Clients → New): a confidential client with the redirect URI https://bpm.example.com/auth/callback and the post-logout redirect URI https://bpm.example.com/, and add groups to its scopes. The groups claim is released only to a client that asks for the groups scope.

5. Configure the application. TinyConductor gets one identity provider, this tenant’s issuer, and two mapping rules on groups:

Terminal window
CONDUCTOR_OIDC_ISSUER=https://guard.example.com/auth/v1/contoso
CONDUCTOR_OIDC_CLIENT_ID=<the client id from step 4>
CONDUCTOR_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc-client-secret
CONDUCTOR_OIDC_REDIRECT_URI=https://bpm.example.com/auth/callback
CONDUCTOR_OIDC_DISPLAY_NAME=Contoso
CONDUCTOR_OIDC_SCOPES=openid,profile,email,groups
CONDUCTOR_AUTH_MAPPING_RULES_JSON=[{"claim":"groups","equals":"employees","roles":["operator"]},{"claim":"groups","equals":"customers","roles":["task-worker"]}]

Any other application works the same way: one OIDC provider, the groups scope, and a rule per group.

What happens at sign-in:

Who What they see What the application gets
Ana, an employee (ana@contoso.com) She types her address, and TinyGuard sends her straight to Contoso’s Microsoft sign-in with her address filled in. The first time, TinyGuard creates her account. groups: ["employees"], so the operator role.
Bob, a customer (bob@example.net) He registers with his address and a password, confirms his address from the e-mail, and signs in with his password (or with Google, if you added it). groups: ["customers"], so the task-worker role.
Someone at contoso.com who tries to register Registration is refused with “Use your company sign-in.” and a link to it. An employee’s account cannot be given a TinyGuard password either. Nothing: they sign in as an employee.

If Contoso has staff with local accounts at the same domain (contractors, for example), give the domain a choice target instead, listing the Entra provider and passwords; registration at the domain is then allowed. For the other way, where TinyConductor trusts each company’s directory directly with no TinyGuard in between, see TinyConductor’s Connecting your identity provider.

Open Identity providers in the console, select the tenant, and add a provider. The OIDC editor is a full page with separate Connection, Claim mapping, Credentials, and Sign-in behavior sections; an unsaved draft is discarded when you switch tenants. Google, one-directory Microsoft Entra, Okta org/default, and custom OIDC issuer presets are available. Register the displayed callback URI in the upstream application. Set its issuer, client ID, upstream scopes, token-endpoint authentication method, and either a sealed client secret or a name in the tenant’s configured secret source. The secret is write-only. The default upstream scope set is openid email profile. You may replace it with up to 16 unique scopes required by the provider; openid is mandatory. The requested scopes are encoded into both sign-in and account-link handoffs. The scope list is not a local role grant and does not bypass claim-mapping rules.

The optional Link verified email switch is off by default and never links to a platform, tenant, or delegated admin account. Test discovery checks the issuer and metadata and reports whether the configured sealed or tenant-managed credential can currently be resolved, without returning its value. It does not redeem an upstream code, verify that the credential is accepted, or complete a sign-in.

Upstream assurance (explicit opt-in). By default an upstream assertion is only ever a first factor: a client with force_mfa demands a local passkey step-up, and upstream amr/acr claims are never trusted. A provider record may name explicitly trusted upstream amr values (trusted_upstream_amr) and/or an acr value (trusted_upstream_acr). When the signature-verified ID token carries one of the named values, that upstream ceremony may satisfy the force-MFA gate directly: the login completes without the local passkey step-up and the session records the trusted ceremony. Values come only from the verified token; an unlisted value — or no list at all — changes nothing. Local tokens never copy upstream amr/acr strings: the local amr claim keeps its own conservative grammar.

The Claim mapping fields may name other top-level, string-valued claims in the signature-verified ID token for email, given name, and family name. The OIDC subject and email_verified claim cannot be remapped. To assign local groups or roles on newly provisioned accounts, name the top-level array-valued claim and enter an explicit JSON map from an upstream value to an existing group or role in this tenant, for example {"upstream-team-id":"engineers"}. Role mappings cannot grant platform_admin, tinyguard_admin, or delegated tinyguard_admin:* roles; those require local assignment. There is no implicit mapping, registry creation, or role/group synchronization for existing accounts. The optional Refresh names and default groups at every sign-in setting updates only a returning exact-linked account’s mapped given and family names when the signature-verified ID token supplies bounded, nonempty values, and adds back the provider’s default groups (below). It never changes email or roles, never removes a group, never touches the account link, and is off by default.

Default groups (default_groups) are groups of this tenant that every account the provider creates joins, on top of any group mappings. Use them to mark where people come from without configuring groups in the upstream directory: employees for the company’s directory, customers for Google. With the every-sign-in refresh on, they are added back at each sign-in. They can never name platform_admin, tinyguard_admin or a delegated tinyguard_admin:* role.

Forward sign-in hints (forward_hints, on by default) passes the address the person already gave to the provider, so its sign-in page does not ask again; see Signing in with an e-mail address. The editor also lists the sign-in domains that send people to the provider. Unknown values are ignored. Malformed or oversized claims, including Entra group-overage references, are refused rather than following token-provided URLs or guessing memberships.

Tenant administrators manage their own providers. platform_admin can manage any tenant’s providers. APIs include:

Action Tenant-session path Platform-admin path
List/create /auth/v1/{tenant}/providers /auth/v1/tenants/{tenant}/providers
Read/edit/delete /auth/v1/{tenant}/providers/{slug} /auth/v1/tenants/{tenant}/providers/{slug}
Test discovery POST /auth/v1/{tenant}/providers/{slug}/test POST /auth/v1/tenants/{tenant}/providers/{slug}/test

The default tenant omits {tenant} from its tenant-session paths. Runtime start and callback URLs use /auth/v1/{tenant}/providers/{slug}/start and .../callback (default tenant again omits the segment). The platform-admin path is for configuration only, not an upstream callback URI.

Once created, a provider’s issuer and subject-key mode cannot be changed: existing federated account links are bound to that identity source. A provider with linked accounts cannot be deleted and recreated under the same slug. Disable it while planning an account-link migration; credential rotation and other non-identity settings remain editable. Pending browser sign-ins are bound to a provider record generation and fail closed if the provider is disabled or replaced before the callback completes.

When Propagate browser logout upstream is enabled, also register /auth/v1/{tenant}/providers/{slug}/logout/callback as a post-logout redirect URI at the upstream provider. Tinyguard terminates the local session first, then navigates to the upstream end_session_endpoint with the verified ID token as id_token_hint. A short-lived, single-use state returns the browser to the locally validated downstream destination. The switch is off by default; backchannel logout does not navigate a browser.

On Account settings, the Linked identity providers panel lists available upstream providers. Connect starts a CSRF-protected, single-use upstream OIDC handoff from a recently authenticated local session. The callback verifies the upstream signature, issuer, audience, nonce, and subject before attaching that subject to the same local account; it never links by email. Administrator accounts must have completed local MFA. A subject already owned by another account and a second subject for the same provider are refused. The account’s tenant and browser session must remain unchanged throughout the handoff.

Unlink is a confirmed, CSRF-protected self-service action requiring a recent local sign-in (and MFA for administrators). It refuses to remove the account’s last available sign-in method. Disabled-but-linked providers remain visible so their links can be removed before deleting the provider. These link routes are tenant-prefixed for non-default tenants.

For Entra, select a concrete directory GUID, not common or organizations. The entra_oid account key combines signed tid and oid claims and refuses a mismatched directory. Other providers use the signed sub claim. Provider discovery and key retrieval require public HTTPS.

The Tenant secret manager console page supports ten backends today:

Set GUARD_DEPLOYMENT_MODE=saas on a hosted deployment. With the variable absent or set to self_hosted, the installation retains the operator-managed backends. An unknown value uses the restrictive SaaS policy rather than silently enabling operator features. The console reads the server’s /auth/v1/secret-manager/capabilities response; the server enforces the same policy on writes, so hiding a form control is not the security boundary.

SaaS choices: tenant administrators can use the built-in managed store, provide a sealed Vault token, configure an Azure Key Vault application, or configure an AWS cross-account IAM role when the platform has enabled that integration. Google, mounted files, and the legacy AWS credential-sidecar backend remain operator-managed. Choosing a backend does not itself grant cloud access.

  • Managed by TinyGuard: save the managed connection, then add named secret values in the console. Values are write-only, limited to 64 KiB, sealed with the deployment’s value cipher, and stored in the tenant’s namespaced KV data. The list API returns only names and update times. No tenant environment variable or file mount is required. The deployment operator still controls the root encryption key and storage backups; this is not bring-your-own KMS. Removing the connection leaves encrypted values dormant; deleting a value explicitly removes it. Use the same named-secret reference in a federation provider definition.
  • Vault KV v2: tenant-specific HTTPS address, mount, path prefix, and optional Vault namespace. Authenticate with a write-only sealed token or a tenant-bound server environment variable named with the prefix GUARD_TENANT_<SLUG>_ (uppercase, hyphens changed to underscores). An arbitrary deployment or another tenant’s variable is refused. A referenced name is read from the value string field under the selected KV v2 path. In SaaS mode, a tenant-managed Vault must use a public HTTPS hostname: TinyGuard filters the resolved socket addresses before connecting, so loopback, private, link-local, and metadata endpoints cannot receive the tenant’s token through a private-DNS or DNS-rebinding route. A platform-admin-configured connection may intentionally use a private network endpoint; tenant administrators cannot edit or remove that operator-managed connection. Lookups have been verified against a real Vault KV v2 instance; hosted public-HTTPS Vault deployments and Vault auth modes other than a token are not yet verified.
  • Operator-mounted files: set GUARD_TENANT_SECRETS_ROOT in the server environment to an operator-controlled directory. Place secret files at <root>/<tenant>/<relative-prefix>/<secret-name>, then select Docker/Kubernetes mounted files and enter only the relative prefix (for example providers). Tenant admins cannot choose the root, and traversal or symlink escapes outside the tenant directory are rejected. Files must contain UTF-8 text of at most 64 KiB; trailing line endings are removed. The test action returns success/failure, never the secret value.
  • Google Secret Manager: set GUARD_TENANT_TOKENS_ROOT to an operator-controlled directory. A workload-identity sidecar must refresh an OAuth access token at <root>/<tenant>/gcp; Tinyguard reads it on each lookup and never lets tenant admins choose its path. Configure a Google Cloud project and a nonempty secret-name prefix. A lookup for google under prefix acme- reads projects/<project>/secrets/acme-google/versions/latest via the verified public HTTPS API. The token’s Google IAM permissions must be scoped to this tenant’s secrets. This backend does not acquire or refresh Google credentials itself; the operator must run the token sidecar.
  • Google Secret Manager (tenant workload federation): on a public HTTPS deployment, a tenant administrator can configure their Google Cloud project, secret-name prefix, and the exact workload identity pool provider audience (//iam.googleapis.com/projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<provider>). The console shows the exact HTTPS OIDC issuer and the server-generated, stable workload subject after save; use those values rather than guessing the tenant URL or constructing a subject yourself. In Google Cloud, create an OIDC workload provider for this tenant’s issuer, map google.subject=assertion.sub, restrict the provider’s attribute condition to that exact subject (and tenant claim), and grant the resulting principal only Secret Manager access to the intended prefixed secrets. TinyGuard signs a five-minute RS256 assertion with its published key, sends it to the fixed Google STS endpoint, and uses the short-lived bearer token for one Secret Manager read. It does not collect a service-account JSON key or require a tenant token file. Changing the project or provider audience rotates the workload subject, so update the Google IAM binding before testing. See Google’s SaaS workload federation guide and STS token API.
  • Azure Key Vault: use the same operator-controlled token root. A workload sidecar must refresh a Key Vault-scoped OAuth access token at <root>/<tenant>/azure. Configure the Azure vault name (not an arbitrary URL) and a secret-name prefix. A lookup for google under prefix acme- reads the latest version of acme-google from https://<vault>.vault.azure.net over verified public HTTPS. The operator must scope the token’s permissions to that tenant’s vault or secrets. Tinyguard does not acquire or refresh Azure credentials itself.
  • Azure Key Vault (tenant application): for SaaS or self-hosted deployments, a tenant administrator may enter a Microsoft Entra directory GUID, application (client) GUID, Key Vault name, secret-name prefix, and a write-only client secret. TinyGuard seals the client secret, then requests a short-lived token from the fixed Microsoft identity-platform endpoint with the https://vault.azure.net/.default scope for each lookup. It does not persist the access token. Grant that application only the Key Vault secrets/get permission needed for the tenant’s vault. Changing the directory or application ID requires supplying a new client secret. This is the client-credentials flow used with the Key Vault Get Secret API.
  • Azure Key Vault (workload federation): on a public HTTPS deployment, configure the tenant’s Entra directory GUID, application (client) GUID, Key Vault name, and secret-name prefix without a client secret. The console shows the exact TinyGuard tenant issuer and a stable, server-generated workload subject. Add a federated identity credential to that Entra app whose issuer and subject match these values and whose audience is api://AzureADTokenExchange; then grant the app only the necessary Key Vault secrets/get permission. TinyGuard signs a five-minute RS256 assertion, exchanges it at the fixed Microsoft token endpoint for a Key Vault-scoped access token, and performs one secret read. Changing the directory, application ID, or vault rotates the subject and requires an updated federated credential. See Microsoft’s federated credential trust setup and client-credentials token request.
  • AWS Secrets Manager: use the same operator-controlled credential root. A sidecar must refresh a compact JSON file at <root>/<tenant>/aws with access_key_id, secret_access_key, and session_token from temporary STS credentials. Configure the AWS region and a mandatory secret-name prefix; Tinyguard signs a GetSecretValue request with SigV4 for the prefixed secret name and uses the SecretString result. The operator must scope IAM secretsmanager:GetSecretValue to the tenant’s secrets. Tenant admins cannot choose the credential path or supply access keys in the console.
  • AWS cross-account role (SaaS self-service): the deployment operator configures GUARD_AWS_PLATFORM_CREDENTIALS_FILE with rotating temporary platform credentials and GUARD_AWS_PLATFORM_ROLE_ARN with their IAM role ARN. The console then permits a tenant administrator to enter only a cross-account role ARN, AWS region, and secret-name prefix. After saving, copy the server-generated, tenant-specific External ID and the displayed platform role ARN into the role’s trust policy. Grant the tenant role only secretsmanager:GetSecretValue for its intended secrets. Each lookup sends a signed AssumeRole request to AWS STS over verified public HTTPS, uses its temporary credentials for one signed secret read, and never persists those credentials. A changed role ARN rotates the External ID. No tenant AWS access key is collected. This initial path supports the commercial arn:aws partition and the global STS endpoint, not GovCloud or China. AWS recommends External IDs to prevent the cross-account confused-deputy problem, and documents the AssumeRole parameters.

No tenant lookup falls back to the deployment or another tenant’s secret source. Suspending a tenant disables lookups. Deployment boot secrets remain platform-scoped. Native credential refresh for the operator-sidecar backends are not yet implemented. Each tenant mints its own workload-federation subject, issuer, and secret path, and suspension stops further requests. The Google, Azure, and AWS secret-source paths have not yet been verified against the live cloud services.

A tenant-scoped SAML provider configuration API ships in every build. Administrators provide metadata XML, the exact IdP entity ID, and SHA-256 fingerprints of trusted signing certificates; the server validates the pins and HTTPS endpoints (including any Single Logout endpoints the metadata declares) before storing the record. Single-use browser handoff state is stored in the tenant’s storage namespace, so a handoff started before a restart or on one node of a cluster completes after it and stays single-use. After configuration, the public SP metadata is available at /auth/v1/saml/{slug}/metadata (or /auth/v1/{tenant}/saml/{slug}/metadata for a non-default tenant); import that document into the upstream IdP to register the fixed POST ACS. When the IdP metadata declares a Single Logout endpoint, the SP metadata additionally advertises SingleLogoutService endpoints at {entity}/slo for the HTTP-Redirect and HTTP-POST bindings and carries the SP’s signing certificate — a fresh EC P-256 keypair generated when the provider record is created and stored sealed with the deployment value cipher, exactly like a client secret. It signs every logout message; AuthnRequests remain unsigned. After configuration, the provider appears on the login page and the browser uses GET /saml/{slug}/start followed by a signed assertion at POST /saml/{slug}/acs under the tenant’s /auth/v1 prefix. The ACS binds RelayState to a short-lived, Secure, HttpOnly, SameSite=None handoff cookie: the ordinary Lax session cookie cannot accompany a cross-site form POST. The SP requires an HTTPS public URL. A handoff cookie that does not match is refused, each sign-in yields one local code, and a replayed assertion or a second redemption of the code is refused. SAML sign-in has been verified end to end against Keycloak 26.7.4, through the downstream PKCE token exchange. That is one independent IdP, not a production interoperability sign-off: a second IdP and an external security review are outstanding.

Single Logout. When the pinned IdP metadata advertises a Single Logout endpoint, a completed SAML sign-in stores a sealed logout context (provider slug, NameID, SessionIndex) on the local session — the SAML twin of the OIDC end-session hint — plus a hashed NameID-to-session index. Ending that session locally (/oidc/logout) then continues front-channel: the server sends a signed LogoutRequest to the IdP’s SLO endpoint and redirects the browser there, while the validated downstream post-logout target travels inside a single-use, ten-minute server-side tracker record (the tenant-scoped saml_slo/ family) rather than in the upstream URL. The IdP’s LogoutResponse returns to GET /auth/v1/saml/{slug}/slo (or the /auth/v1/{tenant}/saml/{slug}/slo variant; POST binding accepted too), which validates it against the stored tracker and then redirects the browser to the original post-logout target. The same endpoint also answers an IdP-initiated LogoutRequest: the named local session (matched by NameID and, when present, SessionIndex against the sealed context) is terminated through the standard termination path — tokens revoked, session deleted — and a signed LogoutResponse returns on the binding the request used. A tampered signature, an unknown tracker, or a response answering a request that was never issued is refused. Providers whose IdP metadata declares no SLO endpoint store no logout context and their end-session behavior is unchanged; provider records created before Single Logout shipped gain their signing key on the next edit.

The control plane accepts POST /auth/v1/saml-providers from a tenant administrator (or POST /auth/v1/tenants/{tenant}/saml-providers from a platform administrator), with this shape:

The console’s Identity providers → SAML 2.0 tab offers the same tenant-scoped create, edit, and delete controls and displays the SP metadata and ACS URLs to register upstream. If the registry cannot be reached, the tab explains the failure.

{
"slug": "corporate",
"display_name": "Corporate SSO",
"idp_entity_id": "https://idp.example/saml",
"metadata_xml": "<EntityDescriptor ...>",
"signing_certificate_sha256": ["0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"],
"given_name_attribute": "urn:oid:2.5.4.42",
"family_name_attribute": "urn:oid:2.5.4.4",
"group_attribute": "groups",
"group_mappings": {"upstream-engineering": "engineering"},
"role_attribute": "roles",
"role_mappings": {"upstream-reader": "reader"}
}

The two profile-attribute names are optional. TinyGuard reads only an exact, single-valued attribute from a verified assertion; ambiguous or oversized values are ignored. They may populate a new account’s given and family names, but never verify email or link an existing account by email. Group and role attributes are optional and map only through explicit upstream-value to existing tenant-registry entries on first account provisioning. Unknown values are ignored, and mappings to platform or tenant administrator roles are refused. Returning accounts are not silently synchronized.

Replace the illustrative fingerprint with the SHA-256 digest of the real signing certificate DER. It must be obtained through an independently trusted channel; copying a fingerprint from the same untrusted metadata URL does not establish trust. Read, replace and delete use /auth/v1/saml-providers/{slug}. Replacing a record rotates its in-flight provider generation and cannot change its IdP entity ID. A client’s allowed_federation_providers policy must permit the provider slug; an empty list hides its button for that client.

Do not treat this as completed federation support. A second independent SAML IdP interoperability run, existing-account mapping synchronization, and provider-specific live integration coverage (including a full link callback against a live upstream and a live Single Logout round trip) are pending. A client that requires local MFA continues through a local passkey ceremony when one is enrolled; a newly provisioned account without a passkey cannot yet enroll in that callback. Pending profile/terms work is refused rather than bypassed. Only tenant-specific Entra OIDC is supported; multi-directory issuer templates are not.

The per-provider logout switch seals the verified ID token inside the local session only when discovery advertises a valid HTTPS end_session_endpoint. The token is not exposed by session APIs. Pending upstream logout return states are single-use records in the tenant’s storage namespace, so a node restart or a callback arriving at a different node of a clustered deployment consumes them correctly; a state older than its ten-minute lifetime answers an expired-state error either way.