IAM roles and STS AssumeRole

DotID models roles the same way AWS IAM does: a role is an assumable identity in an account with a trust policy (who may assume it) and attached permission policies (what it may do). A caller assumes a role through STS AssumeRole and receives short-lived credentials that act with the role’s permissions in the role’s account. This is the mechanism behind cross-account access.

Authentication

Role management and STS are self-managed by the account admin. Sign requests with an account service-user access key using FGAI-HMAC-SHA256 (see [CLI & service authentication](https://docs.flexgalaxy.ai/dev/dotid/cli-authentication/)); a bearer JWT is also accepted. Your credential is pinned to one account — you manage roles only in that account.

Base URL: https://api.flexgalaxy.ai (production) or http://api.dev.fgai.test (local dev).

Roles

Method

Path

Purpose

POST

/identity/v1/accounts/{accountId}/roles

Create a role

GET

/identity/v1/accounts/{accountId}/roles

List roles

GET

/identity/v1/accounts/{accountId}/roles/{roleId}

Get a role

PUT

/identity/v1/accounts/{accountId}/roles/{roleId}

Update trust policy / description / max session

DELETE

/identity/v1/accounts/{accountId}/roles/{roleId}

Delete a role

POST

/identity/v1/accounts/{accountId}/roles/{roleId}/policies

Attach a permission policy

GET

/identity/v1/accounts/{accountId}/roles/{roleId}/policies

List attached policies

DELETE

/identity/v1/accounts/{accountId}/roles/{roleId}/policies/{policyId}

Detach a policy

Create a role

The trustPolicy is required and answers “who may assume me”. name is required; description, path, and maxSessionDuration (seconds) are optional.

{
  "name": "DeviceReader",
  "description": "Read-only device access, assumable by the ops tooling account.",
  "maxSessionDuration": 3600,
  "trustPolicy": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": { "Account": ["acc-0a1b2c3d4e5f"] },
        "Action": "sts:AssumeRole"
      }
    ]
  }
}

The response is the created role, including its id:

{
  "id": "8b2f0a3e-1c4d-4e6f-9a01-2b3c4d5e6f70",
  "accountId": "11111111-1111-1111-1111-111111111111",
  "name": "DeviceReader",
  "trustPolicy": { "…": "…" },
  "maxSessionDuration": 3600,
  "version": 1,
  "createdAt": "2026-07-07T09:00:00Z"
}

Trust policy

Each statement admits a caller when Effect is Allow, Action includes sts:AssumeRole, the Principal block matches the caller, and every Condition is satisfied. Evaluation is fail-closed: with no matching Allow the caller is denied, and a matching Deny always wins.

Principal accepts:

Form

Meaning

"*"

Any caller.

{ "Account": ["acc-…"] }

Any principal in that account.

{ "FGAI": ["frn:acc-…:iam:user/name"] }

A specific principal, by [FRN](https://docs.flexgalaxy.ai/dev/dotid/frn-format/).

{ "FGAI": ["frn:acc-…:iam:account/acc-…"] }

A whole account (account-form FRN).

Optionally require a shared secret with an sts:ExternalId condition:

{
  "Effect": "Allow",
  "Principal": { "Account": ["acc-0a1b2c3d4e5f"] },
  "Action": "sts:AssumeRole",
  "Condition": { "StringEquals": { "sts:ExternalId": "shared-secret-xyz" } }
}

Attach a permission policy

Attach one of your account’s IAM policies by id to grant the role what it may do:

curl -fsS -X POST \
  "{{ dotid_api_prod }}/identity/v1/accounts/$ACCOUNT_ID/roles/$ROLE_ID/policies" \
  -H "Content-Type: application/json" \
  -d '{ "policyId": "a1b2c3d4-…" }'
  # + FGAI-HMAC-SHA256 signing headers

STS AssumeRole

Exchange your credential for temporary credentials scoped to a role.

Method

Path

Purpose

POST

/identity/v1/sts/assume-role

Assume a role → temporary credentials

Request

roleFrn and roleSessionName are required; durationSeconds and externalId are optional. Pass externalId when the target role’s trust policy requires one.

{
  "roleFrn": "frn:acc-target00acct:iam:role/DeviceReader",
  "roleSessionName": "ops-tooling-run-42",
  "durationSeconds": 3600,
  "externalId": "shared-secret-xyz"
}

durationSeconds is clamped to [900, role.maxSessionDuration].

Response

{
  "assumedRoleFrn": "frn:acc-target00acct:iam:role/DeviceReader",
  "accountId": "22222222-2222-2222-2222-222222222222",
  "roleSessionName": "ops-tooling-run-42",
  "accessKeyId": "ASIA…",
  "secretAccessKey": "…",
  "sessionToken": "…",
  "expiration": "2026-07-07T10:00:00Z"
}

The returned credentials are temporary (ASIA… key id). Use them to sign subsequent requests with FGAI-HMAC-SHA256, adding the session token as the X-FGAI-Security-Token header. Requests signed with these credentials act in the role’s account with the role’s permissions — so authorization decisions for the session resolve from the role’s attached policies (see Check authorization (PDP)).

Failures

  • A missing or invalid signature returns 401.

  • If the role does not exist or its trust policy denies the caller, the call returns 403 — the two cases are indistinguishable, so a caller cannot probe for roles in another account.

→ Manage workforce users

A common use of an assumed role is headless workforce (Identity Center) user management: attach a managed policy granting identitycenter:User:* to a role, assume it, and manage users in the organization Identity Center directory with the temporary credentials — no interactive login. See the worked recipe in Manage workforce (Identity Center) users headlessly (M2M).

See also