# Grant cloud roles without stored keys

Let people assume AWS roles with their AuthFI identity and receive credentials that expire after 15 minutes, with no access key created or stored.

Your AWS account trusts your AuthFI project as an identity provider. A person who holds a grant on a role exchanges their AuthFI identity for that role's temporary credentials. They have no cloud user, and no long-lived access key exists anywhere.

Use this to replace IAM users and stored access keys. Keyless roles on AWS are in pilot, and the AuthFI team sets up the trust in your account with you.

## How keyless access works

AuthFI uses standard AWS web identity federation, the `sts:AssumeRoleWithWebIdentity` call.

1. Your AWS account gets an IAM OIDC provider that points at your project.
2. Each role you choose gets one trust statement that names that provider.
3. AuthFI lists the account's roles, and you grant them to groups.
4. A person asks for a role. AuthFI checks the grant and signs a token for that one role.
5. AWS verifies the token against your project's published keys and returns temporary credentials.

AuthFI decides who may ask for a role. AWS decides whether the role can be assumed. Both must agree before anyone receives credentials.

## Before you begin

You need:

- An AWS account, and the roles in it that people should assume.
- A one-time administrator credential for that account. It is used once to set up the trust, and then discarded. Nothing long-lived is kept.
- Access to **Integrations > Cloud Access** in the AuthFI console.
- The groups in your project that should receive each role. See [Roles, groups and grants](/concepts/access).

<Steps>

### Tag the roles that can be assumed

A role opts in with a tag. Nothing else in the account is touched.

| Tag                | Value                  | Meaning                                              |
| ------------------ | ---------------------- | ---------------------------------------------------- |
| `authfi:assumable` | `true`                 | This role may be assumed through AuthFI              |
| `authfi:groups`    | `engineering,platform` | Optional. Only these groups, enforced by AWS itself. |

Tag every role you want listed, including the role AuthFI uses to list the others.

### Set up the trust in your account

The AuthFI team sets up the trust with you, using the one-time administrator credential. The setup creates an identity provider in the account and adds one trust statement to each role. Both are described in [What the trust setup creates](#what-the-trust-setup-creates).

### Connect the account

In the console, open **Integrations > Cloud Access** and choose **Connect an account**. In the **Connect a cloud account** dialog, choose the **AWS** tile under **Provider**, then fill in the fields.

| Field           | Enter                                                                               |
| --------------- | ----------------------------------------------------------------------------------- |
| Account id      | Your AWS account number, for example `123456789012`                                 |
| Federation role | The ARN of the role, for example `arn:aws:iam::123456789012:role/authfi-federation` |
| Display name    | Optional. How the account reads in the console, for example `Production AWS`.       |

Choose **Connect**. The account appears on the **Accounts** tab.

### Discover the account's roles

Open the account from the **Accounts** tab, and choose **Run discovery**. Discovery reads role names and trust policies. It grants nobody anything.

The **Roles** section then lists the account's roles. Each role carries a trust pill:

| Pill                | Meaning                                               |
| ------------------- | ----------------------------------------------------- |
| `can be assumed`    | The role's trust policy allows your project           |
| `cannot be assumed` | The role's trust policy no longer allows your project |
| `never checked`     | The trust policy has not been read                    |

To read the roles again later, choose **Run discovery again**.

### Give access to a role

On the role's row, choose **Give access**.

| Field        | Choose                                                                                         |
| ------------ | ---------------------------------------------------------------------------------------------- |
| Who          | **A person**, **A group** or **An agent**                                                      |
| Who gets it  | The person, group or agent                                                                     |
| For how long | **Until somebody takes it away**, **One hour**, **Eight hours**, **One day** or **Seven days** |

Choose **Give access**. The role's row lists the new holder.

Grant a role the way you grant anything else: to a person or, better, to a group. A grant with a time limit lapses by itself.

</Steps>

## Check that it works

As a person who holds a grant, open the account in **Integrations > Cloud Access** and find the role in the **Roles** section.

1. On the role's row, choose **My credentials**.
2. Choose **A sign-in link** or **Keys for a shell**.
3. Choose **Mint**.

You receive temporary credentials for the role, as described in [What a person receives](#what-a-person-receives).

## What the trust setup creates

### The identity provider

The IAM OIDC provider tells AWS to accept tokens signed by your project.

| Setting      | Value                                                               |
| ------------ | ------------------------------------------------------------------- |
| Provider URL | `https://acme.authfi.io/oidc`                                       |
| Audience     | Your AWS account number                                             |
| Keys         | `https://acme.authfi.io/oidc/.well-known/jwks.json`, fetched by AWS |

### The trust statement on each role

```json
{
  "Sid": "AuthFIKeylessWebIdentity",
  "Effect": "Allow",
  "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/acme.authfi.io/oidc" },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringEquals": {
      "acme.authfi.io/oidc:aud": "123456789012",
      "acme.authfi.io/oidc:role": "arn:aws:iam::123456789012:role/ReadOnly"
    }
  }
}
```

In this example, `123456789012` is the AWS account number and `ReadOnly` is the role the statement is attached to.

The `role` condition makes a token good for one role only. A token issued for `ReadOnly` cannot be used to assume another role, even one that trusts the same provider.

<Note title="Condition keys have no https://">
  Write the condition keys as `acme.authfi.io/oidc:aud`, with no `https://` in front. With it, AWS saves the policy and
  the policy matches nothing.
</Note>

## The token AuthFI presents to AWS

The token is a JWT signed with `RS256` by your project's key.

| Claim    | Value                                       |
| -------- | ------------------------------------------- |
| `iss`    | `https://acme.authfi.io/oidc`               |
| `sub`    | The person, for example `user:ada@acme.com` |
| `aud`    | Your AWS account number                     |
| `role`   | The one role this token is for              |
| `groups` | The person's groups                         |

## What a person receives

A person chooses the form of the credentials when they mint them.

| Choice in the console | Receives                                                                           |
| --------------------- | ---------------------------------------------------------------------------------- |
| **Keys for a shell**  | An access key ID, a secret and a session token: temporary credentials for the role |
| **A sign-in link**    | A one-time link into the AWS console as that role                                  |

The credentials expire 15 minutes after issue, by default. 15 minutes is the shortest lifetime AWS issues credentials for.

## How AuthFI detects a role that cannot be assumed

A grant in AuthFI depends on the role's trust policy in AWS. If someone edits that policy, the grant still allows the request and AWS denies it.

Each time AuthFI lists the roles, it reads every role's trust policy and checks that the policy still allows your project. A role that no longer does carries the pill `cannot be assumed`, with the reason on its row. You see the pill before a person is denied.

To find these roles, use the **cannot be assumed** filter in the **Roles** section. When you give access to such a role, the dialog shows the alert **This role will refuse them**. Fix the role's trust policy in the AWS account, then choose **Run discovery again**.

## Take access away

In the **Roles** section, choose the **×** beside the holder on the role's row, then confirm with **Take it away**.

The holder can no longer obtain credentials for the role, from their next attempt. Credentials already minted keep working until they expire. Nothing in the AWS account changes.

## Troubleshooting

| What you see                                    | Cause                                          | Fix                                                              |
| ----------------------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------- |
| The alert **Its roles are not known**           | Discovery has not run for the account          | Choose **Run discovery**                                         |
| A role is missing from the **Roles** section    | The role has no `authfi:assumable` tag         | Tag the role, then choose **Run discovery again**                |
| A role carries the pill `cannot be assumed`     | Its trust policy no longer allows your project | Restore the trust statement, then choose **Run discovery again** |
| AWS denies a person who holds a grant           | The role's trust policy was edited             | Compare the policy with the trust statement on this page         |
| The trust policy saves, and AWS denies everyone | The condition keys start with `https://`       | Remove `https://` from the condition keys                        |

## Next steps

<Cards>
  <Card href="/concepts/access" title="Roles, groups and grants">
    How a grant to a group reaches every member, and how an end date works.
  </Card>
  <Card href="/access/servers" title="Set up server access with SSH certificates">
    Give people five-minute SSH certificates instead of keys.
  </Card>
  <Card href="/security/keys" title="Keys and secrets">
    The keys your project signs with, and what happens when one rotates.
  </Card>
  <Card href="/security/audit" title="Use the audit record">
    Read and search what happened in your project.
  </Card>
</Cards>
