BetterIAM
Server API

trust

A trust lets one named identity, usually from another tenant, temporarily assume a role in this tenant.

A trust lets one named identity, usually from another tenant, temporarily assume a role in this tenant. Identities belong to one tenant and parent membership grants nothing in child tenants, so a support engineer or an automation that must act inside a customer's tenant needs a controlled way in. A trust gives exactly that person short-lived, audited access to one role, instead of a second account or standing access. It works like a role trust policy in cloud IAM.

How role assumption works

  1. A root administrator creates a trust that names the source tenant and identity, the target role, and optional safeguards.
  2. The source identity calls roles.assume with the trust's ID (and the external ID, when the trust requires one). The source also needs iam:roles:assume on the role's ID in its own tenant, so its own administrators decide who may use trusts at all.
  3. The call returns a short-lived role session token. Requests made with it act in the target tenant with the role's permissions, never more than the trust's ceiling.

The safeguards are checked on every assumption:

  • requireMfa (default true): the source session must be MFA-verified.
  • externalId: a shared value the caller must present, which protects against a confused deputy (someone tricking a trusted service into assuming the role on their behalf). Only its SHA-256 hash is stored.
  • ceiling: a policy document that caps what role sessions may do, whatever the role grants. Without one, the role's own permissions are the limit.

A role session cannot assume another role, and a "view as" (impersonation) session cannot assume roles at all. Role sessions stop working at their next use once the trust is revoked. The protected Owner role can never be the target of a trust.

Trust options and session attributes

Identity trusts also carry typed knobs, set at creation or later with update (root only):

  • maxSessionSeconds: the longest role session the trust issues, from 60 up to the deployment's sts.maxRoleSessionSeconds; 3600 when unset.
  • passSourceAttributes: whether the source identity's attributes reach policies as principal.{attribute} in role sessions. New cross-tenant trusts default to false, so another tenant's attribute values cannot satisfy this tenant's conditions; same-tenant trusts default to true, and trusts created before the option existed keep passing attributes until you change them. analysis.findings reports cross-tenant trusts that still pass them.
  • allowedTagKeys: the session tag keys a caller may set on roles.assume (at most 50, or ['*'] for any); none by default. Tags reach policies as principal.sessionTags.{key}.
  • sourceIdentityMode: whether a caller may (optional), must (required), or may not (forbidden, the default) name a source identity, which policies read as principal.sourceIdentity.
  • description: up to 512 characters for the people reviewing the trust.

Tags and a source identity are closed by default because a caller chooses their values, and both can satisfy policy conditions. Revocation is covered by revokeSessions, which ends sessions issued before a point in time without revoking the trust.

Web-identity trusts

A trust of kind web-identity admits tokens from an external OpenID Connect provider (see oidcProviders) instead of a named identity, for CI jobs and workloads that exchange their platform token through sts.assumeRoleWithWebIdentity. Unlike identity trusts, they are tenant-managed: an administrator with iam:trust:create on the role creates one, and only that administrator's grant authority (or root) may change or revoke it. Each one names:

  • the provider (providerId) and an active service account of the tenant (serviceAccountId) that sessions act as;
  • conditions on the verified token's claims, written in the policy condition grammar over token.{claim} keys (nested claims are joined with dots, such as token.kubernetes.io.namespace). They must pin token.sub with StringEquals or StringLike without a leading wildcard, or creation fails with WEAK_TRUST_CONDITIONS;
  • optionally tagClaims (session tag key to claim name, at most 10), sourceIdentityClaim, maxSessionSeconds, ceiling, passSourceAttributes (default true), and description.

Sessions are bounded by the role, the trust's ceiling, and the grant authorities of the trust's and the provider's creators; revoking either authority, disabling the provider or the service account, or revoking the trust ends them. Web identity must be enabled on the deployment (sts.webIdentity.enabled) to create, change, or revoke one.

Methods6
Serveriam.api.trust
Clientclient.trust
HTTPPOST /api/iam/trust/*
MethodWhat it doesAccess
createCreates a trust that lets one source identity assume one role of this tenant.Credential
evaluateWebIdentityChecks what a web-identity trust would conclude about an external token, without issuing anything.Credential
listLists the trusts that target this tenant's roles.Credential
revokeRevokes a trust so its source can no longer assume the role, and ends the role sessions already issued under it.Credential
revokeSessionsEnds the role sessions issued under one trust before a point in time, without revoking the trust.Credential
updateChanges a trust's safeguards and options, such as its MFA requirement, ceiling, session length, attribute passing, tag keys, source identity mode, or a web-identity trust's conditions and claim mappings.Credential

create

Creates a trust that lets one source identity assume one role of this tenant.

POST/api/iam/trust/create
client.trust.create()Credential

Used inTemporary access

  • Permission: iam:trust:create on the role (iam/{roleId}); root administrators only, with recent authentication.
  • Audited as: iam:trust:create.
  • Errors: ACCESS_DENIED for anyone but a root administrator; RECENT_AUTH_REQUIRED without recent authentication; PROTECTED_RESOURCE when the role is the Owner role; NOT_FOUND when the role is not in this tenant or the source identity is not in sourceTenantId; INVALID_POLICY, INVALID_ACTION, or INVALID_RESOURCE_TYPE for a ceiling storage would reject; INVARIANT_VIOLATION when the trust would newly break an enforced access invariant.

Trusts are platform-controlled because they cross tenant boundaries: a tenant administrator cannot open their tenant to an outside identity on their own. Keep requireMfa on; analysis.findings reports trusts without it. Give each trust the narrowest ceiling the task needs.

With kind: 'web-identity' the call creates a web-identity trust instead: iam:trust:create on the role without root, recent authentication, and sts.webIdentity.enabled (else FEATURE_DISABLED). It fails with WEAK_TRUST_CONDITIONS when the conditions do not pin token.sub, INVALID_IDENTITY when the service account is not an active service account, and INVALID_INPUT for identity-trust fields such as sourceTenantId or requireMfa. Both kinds return the public trust, which reports requiresExternalId instead of the stored hash.

const trust = await iam.api.trust.create(rootCredential, {
  tenantId: customerTenantId,
  sourceTenantId: rootTenantId,
  sourceIdentityId: supportEngineerId,
  roleId: supportRoleId,
  externalId: 'ticket-routing-7f3a',
  ceiling: {
    version: 1,
    statements: [{ effect: 'allow', actions: ['iam:identities:read', 'iam:audit:read'], resources: ['*'] }],
  },
});
Input

One of IdentityTrustCreateInput | WebIdentityTrustCreateInput.

Returns

A PublicTrust object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/trust/create" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "sourceTenantId": "<sourceTenantId>",
  "sourceIdentityId": "<sourceIdentityId>",
  "roleId": "<roleId>"
}'
Signature
iam.api.trust.create(
  credential: CredentialInput,
  input: TrustCreateInput,
): Promise<PublicTrust>

evaluateWebIdentity

Checks what a web-identity trust would conclude about an external token, without issuing anything.

POST/api/iam/trust/evaluateWebIdentity
client.trust.evaluateWebIdentity()Credential

Used inTemporary access

  • Permission: iam:trust:read on the trust, and sts.webIdentity.enabled.
  • Audited as: iam:trust:read.
  • Errors: FEATURE_DISABLED when web identity is off; INVALID_INPUT for a trust that is not a web-identity trust; NOT_FOUND.

The public exchange answers every refusal with the same WEB_IDENTITY_REJECTED, so callers cannot probe which trusts exist. This dry run is how administrators find out why a token is refused: it returns verified, the reason the exchange would give (a verification failure such as audience, expired, or unknown-key, or conditions or source-identity), the verified registered claims, each failing condition as Operator:key, and the session tags and source identity the claim mappings would produce. No replay record is written, so the token stays redeemable.

const result = await iam.api.trust.evaluateWebIdentity(credential, { tenantId, trustId, webIdentityToken });
if (result.conditions && !result.conditions.matched) console.log(result.conditions.failed); // ['StringLike:token.sub']
Input

Prop

Type

Returns

A WebIdentityEvaluation object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/trust/evaluateWebIdentity" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "trustId": "<trustId>",
  "webIdentityToken": "<webIdentityToken>"
}'
Signature
iam.api.trust.evaluateWebIdentity(
  credential: CredentialInput,
  input: { tenantId: string; trustId: string; webIdentityToken: string },
): Promise<WebIdentityEvaluation>

list

Lists the trusts that target this tenant's roles.

POST/api/iam/trust/list
client.trust.list()Credential
  • Permission: iam:trust:read on the tenant.
  • Audited as: iam:trust:read.

Revoked trusts are left out unless includeRevoked is true. External ID hashes are never returned; instead, requiresExternalId tells you whether callers must present one. Tenant administrators can use this to see who outside the tenant may assume which role. Trusts of kind web-identity, which admit tokens from an external OpenID Connect provider, appear here too.

Input

Prop

Type

Returns

An array of PublicTrust.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/trust/list" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.trust.list(
  credential: CredentialInput,
  input: { tenantId: string; includeRevoked?: boolean },
): Promise<PublicTrust[]>

revoke

Revokes a trust so its source can no longer assume the role, and ends the role sessions already issued under it.

POST/api/iam/trust/revoke
client.trust.revoke()Credential
  • Permission: iam:trust:revoke on the trust (iam/{trustId}); root administrators only, with recent authentication.
  • Audited as: iam:trust:revoke.
  • Errors: ACCESS_DENIED for anyone but a root administrator; RECENT_AUTH_REQUIRED without recent authentication; NOT_FOUND when the trust is not in this tenant.

Existing role sessions under the trust are refused at their next use. The record is kept with revoked: true, so it still appears with includeRevoked and in the audit history. Revoking an already revoked trust succeeds.

The trust's live role sessions are also deleted in the same transaction, so they disappear from roles.listSessions at once. A web-identity trust is revoked by the administrator whose authority created it (or root) rather than root only, and needs sts.webIdentity.enabled (FEATURE_DISABLED otherwise).

Input

Prop

Type

Returns

A PublicTrust object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/trust/revoke" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "trustId": "<trustId>"
}'
Signature
iam.api.trust.revoke(
  credential: CredentialInput,
  input: { tenantId: string; trustId: string },
): Promise<PublicTrust>

revokeSessions

Ends the role sessions issued under one trust before a point in time, without revoking the trust.

POST/api/iam/trust/revokeSessions
client.trust.revokeSessions()Credential
  • Permission: iam:roles:revoke-sessions on the trust's role (iam/{roleId}), with recent authentication. It is not root-only, so the target tenant's administrators can end sessions under a platform-controlled trust.
  • Audited as: iam:roles:revoke-sessions and role:sessions-revoked (with the watermark and the number of sessions deleted).
  • Errors: INVALID_INPUT when before is not a whole number of milliseconds, is negative, or lies in the future; NOT_FOUND when the trust is not in this tenant; RECENT_AUTH_REQUIRED; ACCESS_DENIED.

before defaults to now, which ends every session issued so far. The trust's sessionsRevokedBefore watermark only moves forward, so older sessions are refused at their next use and the matching rows are deleted at once, while new assumptions keep working. Session JWTs that other services verify offline stay valid there until they expire; those services can check sts.getCallerIdentity for an online answer.

const { revoked } = await iam.api.trust.revokeSessions(credential, { tenantId, trustId });
Input

A TrustRevokeSessionsInput object:

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/trust/revokeSessions" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "trustId": "<trustId>"
}'
Signature
iam.api.trust.revokeSessions(
  credential: CredentialInput,
  input: TrustRevokeSessionsInput,
): Promise<{ trustId: string; sessionsRevokedBefore: number; revoked: number }>

update

Changes a trust's safeguards and options, such as its MFA requirement, ceiling, session length, attribute passing, tag keys, source identity mode, or a web-identity trust's conditions and claim mappings.

POST/api/iam/trust/update
client.trust.update()Credential
  • Permission: iam:trust:update on the trust, with recent authentication. Identity trusts need a root administrator; web-identity trusts need the grant authority that created them (or root).
  • Audited as: iam:trust:update.
  • Errors: ACCESS_DENIED for anyone else; CONFLICT (409) for a revoked trust; INVALID_INPUT when nothing changes or a field belongs to the other kind of trust (conditions, tagClaims, or sourceIdentityClaim on an identity trust; requireMfa, allowedTagKeys, or sourceIdentityMode on a web-identity trust); WEAK_TRUST_CONDITIONS for conditions that do not pin token.sub; FEATURE_DISABLED for a web-identity trust while web identity is off; RECENT_AUTH_REQUIRED; NOT_FOUND.

Only the fields you pass change, and null returns ceiling, maxSessionSeconds, allowedTagKeys, description, tagClaims, or sourceIdentityClaim to its default. Tightening what the trust admits (its tag keys, source identity mode, conditions, claim mappings, or a shorter maxSessionSeconds) also moves the trust's sessionsRevokedBefore watermark to now, so sessions issued under the looser rules end at their next use. Use it to turn off passSourceAttributes on older cross-tenant trusts.

await iam.api.trust.update(rootCredential, { tenantId, trustId, passSourceAttributes: false, maxSessionSeconds: 900 });
Input

A TrustUpdateInput object:

Prop

Type

Returns

A PublicTrust object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/trust/update" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "trustId": "<trustId>"
}'
Signature
iam.api.trust.update(
  credential: CredentialInput,
  input: TrustUpdateInput,
): Promise<PublicTrust>

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page