sts
The sts group issues and inspects temporary credentials, the way a cloud security token service does.
The sts group issues and inspects temporary credentials, the way a cloud security token service does. A CLI or a
CI job can trade a long-lived credential for a short-lived, narrowed one (getSessionToken), a workload can trade an
external OpenID Connect token for a role session without holding any IAM secret (assumeRoleWithWebIdentity), and
any holder can ask who a credential acts as (getCallerIdentity). Role sessions through a named trust are issued by
roles.assume.
Temporary credentials
Every issuer returns the same shape: { token, tokenType: 'Bearer', format, expiresAt, expiresIn, audience?, session }.
The token appears once, in the response body; these routes never set a cookie, so the credential only ever travels as
Authorization: Bearer. session is an allowlist summary (id, tenant, kind, identity, expiry, MFA, and the role,
trust, session name, and source identity when they apply) that never carries hashes, policies, or authority ids.
Opaque tokens are typed and checksummed: biam_sts_… for session tokens and biam_rol_… for role sessions, 58
characters, so secret scanners can find a leaked one. With format: 'jwt' the credential is instead a session JWT
signed with the deployment's sts.jwt keys, which downstream services can verify offline against
GET {basePath}/.well-known/jwks.json with createSessionTokenVerifier from better-iam/session-tokens. IAM itself
still checks the stored session on every use, so revocation inside IAM is immediate; offline verifiers only see it
when the token expires. Configure the ceilings, signing keys, and web-identity switches in
temporary credentials.
A temporary credential never outlives its source, never passes recent-authentication checks, is never treated as an
owner or root administrator, and cannot accept agreements, activate eligible bindings, or make other self-service
changes. Policies can tell temporary credentials apart with principal.sessionKind and the session keys
(principal.sessionName, principal.sessionTags.{key}, principal.webIdentitySubject, …) described in
context keys.
| Method | What it does | Access |
|---|---|---|
assumeRoleWithWebIdentity | Exchanges a verified external OpenID Connect token, such as a GitHub Actions or Kubernetes token, for a role session under a web-identity trust. | Public |
getCallerIdentity | Returns who the presented credential acts as: identity, tenant, session kind and id, format, MFA, expiry, and any role, trust, session name, tags, or web identity behind it. | Credential |
getSessionToken | Mints a short-lived session token for your own identity from a signed-in session or an API key, optionally narrowed by a policy and attested with a fresh MFA code. | Credential |
assumeRoleWithWebIdentity
Exchanges a verified external OpenID Connect token, such as a GitHub Actions or Kubernetes token, for a role session under a web-identity trust.
Used inTemporary access
- Permission: None: public. The external token is the only credential, and the trust's claim conditions decide whether it is admitted.
- Audited as:
role:assumed-with-web-identityin the trust's tenant (outcomeallow, ordenywith areasononce the trust has resolved). - Errors:
WEB_IDENTITY_REJECTED(403) for every refusal that depends on stored state or on the token (unknown or revoked trust, disabled provider, bad signature, issuer, audience or lifetime, unmet conditions, replay, inactive service account, and so on), always with the same body;FEATURE_DISABLEDwhensts.webIdentity.enabledis off, or forformat: 'jwt'withoutsts.jwt;INVALID_INPUTfor a malformed request,durationSeconds, oraudience;RATE_LIMITEDonce the trust's exchange budget (sts.webIdentity.maxExchangesPerWindow) is spent;LIMIT_EXCEEDED(409) when the trust already holdssts.webIdentity.maxSessionsPerTrustlive sessions;IP_BLOCKEDorIP_NOT_ALLOWEDfrom the tenant's network rules;ACCESS_DENIEDfor a JWT audience the service account may not obtain.
The session acts as the trust's service account in the trust's tenant, with the role's permissions bounded by the
trust's ceiling, the optional scope-down policy, and the grant authorities of whoever created the trust and the
provider. It is kind role with session.webIdentity set, never carries MFA, and ends when the provider is disabled,
the trust is revoked, the service account is disabled, or either authority is revoked. sessionName is required and
reaches policies as principal.sessionName; the verified subject reaches them as principal.webIdentitySubject.
Because callers cannot tell why a token was refused, administrators debug with
trust.evaluateWebIdentity, which reports the reason and every
failing condition. Each token can be redeemed once per provider unless the provider sets replayProtection: 'off'.
# In a GitHub Actions job with `permissions: id-token: write`.
ID_TOKEN=$(curl -sH "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://iam.example.com" | jq -r .value)
curl -s https://iam.example.com/api/iam/sts/assumeRoleWithWebIdentity \
-H 'Content-Type: application/json' -H 'X-Better-IAM: 1' \
-d "{\"tenantId\":\"$TENANT_ID\",\"trustId\":\"$TRUST_ID\",\"webIdentityToken\":\"$ID_TOKEN\",\"sessionName\":\"deploy-$GITHUB_RUN_ID\"}"A WebIdentityExchangeInput object:
Prop
Type
A WebIdentityCredential 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/sts/assumeRoleWithWebIdentity" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"trustId": "<trustId>",
"webIdentityToken": "<webIdentityToken>",
"sessionName": "<sessionName>"
}'iam.api.sts.assumeRoleWithWebIdentity(
input: WebIdentityExchangeInput,
): Promise<WebIdentityCredential>getCallerIdentity
Returns who the presented credential acts as: identity, tenant, session kind and id, format, MFA, expiry, and any role, trust, session name, tags, or web identity behind it.
Used inTemporary access
- Permission: None beyond a valid credential. It works for every session kind.
- Audited as: nothing; it records no event.
- Errors:
UNAUTHENTICATEDwhen the credential is invalid, expired, or revoked.
The credential is re-validated exactly as for any other call, so this doubles as the online revocation check for a
service that holds a session JWT: a token that still verifies offline but was revoked in IAM fails here. The result
is an allowlist projection and never includes hashes, policies, the source session, or authority ids. The CLI's
whoami command prints it.
const caller = await iam.api.sts.getCallerIdentity({ token });
if (caller.sessionKind === 'role') console.log(`acting as role ${caller.roleId} via trust ${caller.trustId}`);A CallerIdentity 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/sts/getCallerIdentity" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{}'iam.api.sts.getCallerIdentity(
credential: CredentialInput,
): Promise<CallerIdentity>getSessionToken
Mints a short-lived session token for your own identity from a signed-in session or an API key, optionally narrowed by a policy and attested with a fresh MFA code.
Used inTemporary access
- Permission:
iam:session-tokens:createon your own identity (iam/{identityId}) in your tenant. Owners hold it through the Owner role; anyone else needs an explicit grant. - Audited as:
iam:session-tokens:createandsession-token:issued(with the new session id, duration, format, and whether an MFA code was verified). - Errors:
CREDENTIAL_CHAINING_DISABLED(400) from a role session or another session token;IMPERSONATION_RESTRICTEDfrom a "view as" session;MFA_NOT_ENROLLEDformfaCodefrom an API key or a person without an authenticator;INVALID_MFAfor a wrong or reused code;RATE_LIMITEDafter too many codes;LIMIT_EXCEEDED(409) when you already holdsts.maxSessionTokensPerIdentitylive tokens (50 by default);INVALID_INPUTfor a duration, session name, format, or audience out of bounds;FEATURE_DISABLEDforformat: 'jwt'withoutsts.jwt;ACCESS_DENIED.
The token acts with your own grants, bounded by policy and by the source's own limits (an API key's scopes and
authority carry over), and lasts durationSeconds: 3600 by default, at most sts.maxSessionTokenSeconds (12 hours
unless configured), and never beyond the source's expiry. It ends when its source ends, and using it never refreshes
the source's idle timer, so prefer an API key as the source for long-running automation.
With mfaCode (a current authenticator code; people only), the token carries mfa: true and a fresh MFA time, so it
can then assume roles through trusts that require MFA. This is the MFA-then-assume pattern for command-line tools:
roles cannot take a code themselves. Without a code, the source's MFA state is copied; API keys never carry MFA.
The token's policy and its source's scopes decide which roles it may assume, but they do not carry into the role
session, which acts with the role's permissions within the trust's ceiling and its own session policy.
// A CLI step-up: a one-hour, read-only token that can assume an MFA-gated role.
const { token, expiresAt } = await iam.api.sts.getSessionToken(
{ token: signedInToken },
{
mfaCode: '123456',
sessionName: 'deploy-cli',
policy: {
version: 1,
statements: [{ effect: 'allow', actions: ['iam:roles:assume', 'documents:read'], resources: ['*'] }],
},
},
);A GetSessionTokenInput object (the argument is optional):
Prop
Type
A TemporaryCredential 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/sts/getSessionToken" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{}'iam.api.sts.getSessionToken(
credential: CredentialInput,
input?: GetSessionTokenInput | undefined,
): Promise<TemporaryCredential>Better IAM is created by Sean Filimon
Last updated