# Connecting with AWS Identity Federation

Authenticate to the Twisp GraphQL API using AWS IAM Outbound Identity Federation.

AWS IAM Outbound Identity Federation lets your application obtain a signed JWT from AWS STS using its existing AWS credentials. Twisp accepts this token directly in API requests and uses client policies to authorize access to your ledger.

This guide walks through requesting a token, creating a Twisp client for the AWS issuer, and making a GraphQL request. See [Security and Auth](https://www.twisp.com/docs/infrastructure/security-and-auth.md) for the authentication model and policy reference.

## Prerequisites

- An AWS account with outbound identity federation enabled and an IAM role or user allowed to call `sts:GetWebIdentityToken`. Follow the [AWS setup guide](https://aws.amazon.com/blogs/aws/simplify-access-to-external-services-using-aws-iam-outbound-identity-federation/) to enable federation and configure permissions.
- An AWS CLI version that supports `aws sts get-web-identity-token`, configured with credentials for the identity you want to use. These may come from IAM Identity Center or your workload's IAM role.
- `curl`, `jq`, and Python 3 for the command-line examples.
- Your Twisp GraphQL endpoint and Twisp account ID, plus existing administrator access to create a client in that tenant.

## 1. Request an AWS web identity token

Run this command with your application's AWS identity:

```shell
AWS_TOKEN_RESPONSE=$(aws sts get-web-identity-token \
  --audience ledger-service \
  --signing-algorithm RS256 \
  --output json)
TWISP_TOKEN=$(printf '%s' "$AWS_TOKEN_RESPONSE" | jq -er '.WebIdentityToken')
```

The response contains `WebIdentityToken`, the JWT to send to Twisp, and `Expiration`, its expiry time. The audience `ledger-service` identifies the intended recipient; use the same value in the Twisp policy below. See the [AWS CLI command reference](https://docs.aws.amazon.com/cli/latest/reference/sts/get-web-identity-token.html) for the available options.

## 2. Inspect the token claims

Decode the payload locally to find the issuer and subject that your Twisp client will trust:

```shell
printf '%s' "$TWISP_TOKEN" | python3 -c '
import base64, json, sys
payload = sys.stdin.read().strip().split(".")[1]
claims = json.loads(base64.urlsafe_b64decode(payload + "=" * (-len(payload) % 4)))
print(json.dumps(claims, indent=2))
'
```

This only decodes the claims; it does not verify the signature. An abbreviated payload might look like this:

```json
{
  "iss": "https://EXAMPLE.tokens.sts.global.api.aws",
  "aud": "ledger-service",
  "sub": "arn:aws:iam::123456789012:role/ledger-service",
  "iat": 1788796800,
  "exp": 1788797100
}
```

Use your token's actual values in the next step:

- `iss` is the AWS account's issuer URL and becomes the Twisp client `principal`.
- `aud` identifies the intended service and is checked by a policy assertion.
- `sub` identifies the AWS caller and is checked to restrict access to your chosen role or user.

## 3. Create a Twisp client and policy

Using your existing Twisp administrator credentials, run the following mutation in the target tenant. Replace the example issuer URL and role ARN with the `iss` and `sub` values from your token. The new AWS token can access the ledger only after this client is configured.

```graphql
mutation CreateAWSFederationClient {
  auth {
    createClient(
      input: {
        name: "AWS ledger service"
        principal: "https://EXAMPLE.tokens.sts.global.api.aws"
        policies: [
          {
            actions: [SELECT]
            resources: ["*"]
            effect: ALLOW
            assertions: {
              isLedgerService: "context.auth.claims.aud == 'ledger-service'"
              isAllowedRole: "context.auth.claims.sub == 'arn:aws:iam::123456789012:role/ledger-service'"
            }
          }
        ]
      }
    ) {
      principal
    }
  }
}
```

This policy grants read access across the tenant's resources only when both assertions pass. The subject check limits access to the specified AWS identity; checking the audience alone would also admit other identities under the same issuer that can request that audience.

The example requests one audience and checks its string value. If you request multiple audiences, inspect the resulting `aud` claim and adapt the assertion to its representation.

Adjust `actions` and `resources` for your application's access requirements. See [Creating Clients and Policies](https://www.twisp.com/docs/infrastructure/security-and-auth.md#creating-clients-and-policies) for resource identifiers and policy evaluation, and [CEL](https://www.twisp.com/docs/reference/cel.md) for assertion syntax.

## 4. Call the Twisp GraphQL API

Set your endpoint and Twisp account ID. The Twisp account ID selects your tenant; it is separate from the AWS account number in the role ARN.

```shell
TWISP_ENDPOINT='https://api.us-east-1.cloud.twisp.com/financial/v1/graphql'
TWISP_ACCOUNT_ID='<your-twisp-account-id>'
```

Use the endpoint for your environment. If the token expired while you configured the client, repeat step 1, then query accounts:

```shell
curl --fail-with-body --silent --show-error "$TWISP_ENDPOINT" \
  -H "Authorization: Bearer $TWISP_TOKEN" \
  -H "x-twisp-account-id: $TWISP_ACCOUNT_ID" \
  -H 'Content-Type: application/json' \
  --data-raw '{"query":"query { accounts(index: { name: STATUS }, first: 5) { nodes { accountId code name } } }"}'
```

Twisp validates the token, looks up the client by its issuer, and evaluates the client's policies against the request. A successful response contains `data.accounts.nodes`, which can be empty if the tenant has no accounts. Check the response body for GraphQL `errors` as well as the HTTP status; see [Response Format](https://www.twisp.com/docs/reference/api/response-format.md).

## 5. Refresh tokens in your application

AWS tokens default to a five-minute lifetime. Cache the token in memory and request a replacement before the returned `Expiration` time, allowing time for requests in flight. Use the AWS SDK's `GetWebIdentityToken` operation in your application to repeat the same flow. The [AWS CLI reference](https://docs.aws.amazon.com/cli/latest/reference/sts/get-web-identity-token.html) describes token duration options.

Reuse a valid token across requests and coordinate refreshes within your application to avoid unnecessary STS calls. Keep tokens out of logs and ensure the AWS credentials used to refresh them remain valid.

## Troubleshooting

- **AWS rejects the token request:** Confirm that outbound federation is enabled and the calling identity has `sts:GetWebIdentityToken` permission. Check that its AWS credentials are still valid.
- **The CLI does not recognize the command:** Update to an AWS CLI version that supports `get-web-identity-token`.
- **Twisp rejects authentication:** Request a fresh token and check that you sent the complete JWT and the correct `x-twisp-account-id` header.
- **Twisp denies access:** Check that a client exists in the selected tenant with `principal` exactly matching `iss`, both assertions match the token, and the policies allow the requested resources and actions. A matching `DENY` policy takes precedence.

For this flow, the client principal is the AWS issuer URL. The [Twisp-issued IAM token flow](https://www.twisp.com/docs/infrastructure/security-and-auth.md#issuing-tokens-with-aws-identity-and-access-management-iam) exchanges a presigned `GetCallerIdentity` request and uses an AWS identity ARN as the principal; it is a separate authentication option.
