# auth0-m2m-config
Tools for managing machine-to-machine (M2M) clients in Auth0

## Setup
This application uses Poetry for dependency management. If you're planning on doing development locally
and running without the Dockerized setup, you'll need to ensure [Poetry is installed](https://python-poetry.org/docs/#installation).

This application uses Makefile for common functionality. Go ahead and try it out:

### Environment

Copy `.env.shadow` to `.env`.

```sh
make clean env lint test_unit
```

## M2M Configuration File

Each lambda must add an entry in [m2m.json](./m2m.json) with the fields listed in the following table. Once your PR is
merged, `auth0-m2m-config` will create
an [Auth0 M2M application](https://auth0.com/docs/get-started/auth0-overview/create-applications/machine-to-machine-apps)
for each entry and store the corresponding Auth0 credentials in Secrets Manager.

| Field                             | Description                                                          | 
|:----------------------------------|:---------------------------------------------------------------------|
| name                              | The auth0 application name will be: `LAMBDA-NAME-machine-to-machine` |
| prod_aws_account                  | Production AWS account used to store auth0 secrets.                  |
| qa_aws_account                    | QA AWS account used to store auth0 secrets.                          |
| uat_aws_account                   | (Optional) UAT AWS account used to store auth0 secrets.              |
| client_metadata.m2m_identity_uuid | Value to be used as the `orchardIdentityId` in the JWT.              |

**NOTE** The secrets must exist before adding an entry to [m2m.json](./m2m.json).

### Running the M2M Validation Locally

Run `awsume prod && make docker_m2m_lint` to verify new entries. Verify the output has 0 `INVALID_REGISTRY_ENTRY`
errors.

### Syncing the M2M Entries

To ensure an Auth0 client and its client credentials are stored in AWS Secrets Manager for each entry in the M2M configuration file, use `make docker_sync`.


|Parameter|Required|Description|
|:--------|:-------|:----------|
|`AUTH0_DOMAIN`|Yes|Domain for auth0's management API. This is specific to the environment, and is one of `qa-orchard.auth0.com` or `prod-orchard.auth0.com`|
|`ENV`|Yes|The environment to be configured for. This will ensure the qa or prod AWS Account of the registry entry is configured with the appropriate `M2M_AUTH0_CLIENT_CREDENTIALS`.|
|`ALL_OR_ONE`|Yes|Use `all` to sync all entries in the file. Use `one` in conjunction with `ENTRY` to sync exactly one entry/machine_name.|
|`ENTRY`|No|Use in conjunction with `ALL_OR_ONE=one` to sync exactly one entry/machine_name.|
|`DRY_RUN`|No|Use `--dry-run` to see what `sync all` would execute. Defaults to False for `sync all` and does not work for `sync one`.|
|`OVERWRITE_SECRET`|No|Use `--overwrite-secret` to always update `M2M_AUTH0_CLIENT_CREDENTIALS`, even when the value can be validated. Defaults to False for `sync all` and True for `sync one`.|
|`ROTATE_SECRET`|No|Use `--rotate-secret` to rotate the Auth0 Application's Client Credentials, and save the updated client credentials to AWS SecretsManager. Defaults to `--no-overwrite-secret`. In the Jenkins job, this is presented as `ROTATE_AUTH0_CLIENT_CREDENTIALS`|

#### Example usages
To sync all entries to QA, but not overwrite any secrets:

```sh
AUTH0_DOMAIN=qa-orchard.auth0.com \
ENV=qa \
ALL_OR_ONE=all \
  make docker_sync
```

To sync a single entry `perfect-machine` to Prod but NOT overwrite what may have been previously staged:

```sh
AUTH0_DOMAIN=prod-orchard.auth0.com \
ENV=prod \
ALL_OR_ONE=one \
ENTRY=perfect-machine \
OVERWRITE_SECRET=--no overwrite-secret \
  make docker_sync
```

## Secrets Manager

The service will store Auth0 credentials to the following secrets.

| Secret                                              | Description                                         | 
|:----------------------------------------------------|:----------------------------------------------------|
| \<env\>/M2M-ENTRY-NAME/M2M_AUTH0_CLIENT_CREDENTIALS | JSON Object with Auth0 Client ID and Client Secret. |
| \<env\>/M2M-ENTRY-NAME/M2M_JWT_ACCESS_TOKEN         | JWT Bearer token generated by Auth0.                |

### Client Credentials JSON Object

The `AUTH0_CLIENT_CREDENTIALS` object contains fields required
to [generate a JWT using the Auth0 client-credentials flow](https://auth0.com/docs/api/authentication?http#client-credentials-flow).

```json
{
  "grant_type": "client_credentials",
  "audience": "[API_AUDIENCE]",
  "client_secret": "some-secret-string",
  "client_id": "some-client-id"
}
```
### Supporting cross-account secrets

[AssumeRoleSecretsManager](https://github.com/theorchard/auth0-m2m-config/blob/master/m2mconfig/connectors/assume_role_secrets_manager.py) was implemented to enable an application running in one AWS Account to assume a role to interact with another AWS Account's Secrets Manager Service. This is regularly referred to as Cross-Account access.

As this connector is found meaningful/useful to other teams needing Cross-Account AWS Secrets Manager service access, we can refactor it into its own library (e.g. `python-assume-role-secrets-manager`)

[This Notion doc/section](https://www.notion.so/AssumeRoleSecretsManager-dc998904919c4298aa27223b7c040f26?pvs=4#654ac620478d4d63961bf3d167237294) summarizes general principles used.


[This whimsical](https://whimsical.com/spike-m2m-provisioning-and-auth0-integration-VLPapq2raTjkqMwoqbdmNM@2bsEvpTYSt1Hj3r8MtCohFhkDPdrNvcA2Fa) shows an example of how the connector can be used.

#### Specific for `auth0-m2m-config`

Greater detail (and diagrams!) are available [here in Notion](https://www.notion.so/AssumeRoleSecretsManager-dc998904919c4298aa27223b7c040f26?pvs=4#62e4436c13144dff9cc240c52f15f59c).

Every AWS Account requiring `auth0-m2m-config` needs to create a role `<env>-assume-auth0-m2m-config` which attaches a read/write policy for the AWS Account's `<env>/*/M2M_AUTH0_CLIENT_CREDENTIALS` secrets. To provide the Trust Policy for the Permissions Platform <env> task role to assume the `<env>-assume-auth0-m2m-config` role in the AWS Account, and thus, manage those secrets:

1. Provision this `terraform-iam-policies` [module](https://github.com/theorchard/terraform-iam-policies/tree/master?tab=readme-ov-file#auth0-m2m-config) into the AWS Account.
2. Ensure the AWS account is added to the list (per env) of [supported aws accounts](https://github.com/theorchard/terraform-infra/blob/33ef11415528caa395451993273eebf2698e60b2/permissions-platform/qa/auth0-m2m-config/variables.tf#L17). The appropriate string for the AWS account can be found [here](https://github.com/theorchard/terraform-aws-accounts-map/blob/master/main.tf).

The entity performing the execution must assume the role `<env>-auth0-m2m-config-task-role` in the Permissions Platform <env> AWS account FIRST, before attempting to use AssumeRoleSecretsManager. Currently, [the following entities](https://github.com/theorchard/terraform-infra/blob/33ef11415528caa395451993273eebf2698e60b2/permissions-platform/qa/auth0-m2m-config/main.tf#L50-L54) can assume the `<env>-auth0-m2m-config-task-role`:
* `arn:aws:iam::437795906767:role/prod-jenkins-aws-pipeline-agent`
* `arn:aws:iam::591204808501:role/generic-engineer-role`
* `arn:aws:iam::591204808501:role/permissions-platform-role`

This list of allowed entities can be extended by making a PR to terraform-infra.

#### Manual Testing
We do not have dockerized STS, SecretsManager. The following shows how to use the AssumeRoleSecretsManager to interact with real AWS infrastructure:

1. Make sure you are locally using `awsume` and configure the ability to assume `qa-auth0-m2m-config-task-role`. Open `~/.aws/config` and add:

```
[profile permissions-platform-qa]
source_profile = prod
role_session_name = your username
role_arn = arn:aws:iam::591204808501:role/permissions-platform-role

[profile permissions-platform-qa-auth0-m2m-config-task-role]
source_profile = permissions-platform-qa
role_arn = arn:aws:iam::591204808501:role/qa-auth0-m2m-config-task-role
```

2. Assume the `qa-auth0-m2m-config-task-role` role
```sh
awsume permissions-platform-qa-auth0-m2m-config-task-role
```

3. Run `poetry run python scripts/test_assume_role_secrets_manager.py`. You should see output like:

```
#####################################
Testing AWS Account Id: 591204808501
#####################################
~~qa/lambda-test-m2m-client/M2M_AUTH0_CLIENT_CREDENTIALS~~
>>>>IT EXISTS
>>>>GET: {"grant_type": "client_credentials", "audience": "api_audience", "client_id": "some-auth0-client-id", "client_secret": "some-secret-string"}
>>>>SET: True
>>>>GET: {"grant_type": "client_credentials", "audience": "api_audience", "client_id": "some-auth0-client-id", "client_secret": "some-secret-string"}
~~~~~~~~~~~~~~~~~~~~~~~~
~~qa/this-asterisk/M2M_AUTH0_CLIENT_CREDENTIALS~~
Secret does not exist
~~~~~~~~~~~~~~~~~~~~~~~~
~~qa/lambda-test-m2m-client/SOME_OTHER_SECRET~~
User: arn:aws:sts::591204808501:assumed-role/qa-assume-auth0-m2m-config/3635fe0d-bf60-44ed-9a46-51c89052fb4b is not authorized to perform: secretsmanager:DescribeSecret on resource: qa/lambda-test-m2m-client/SOME_OTHER_SECRET because no identity-based policy allows the secretsmanager:DescribeSecret action
~~~~~~~~~~~~~~~~~~~~~~~~
#####################################
Testing AWS Account Id: 285943604611
#####################################
~~qa/lambda-test-m2m-client/M2M_AUTH0_CLIENT_CREDENTIALS~~
Secret does not exist
~~~~~~~~~~~~~~~~~~~~~~~~
~~qa/this-asterisk/M2M_AUTH0_CLIENT_CREDENTIALS~~
Secret does not exist
~~~~~~~~~~~~~~~~~~~~~~~~
~~qa/lambda-test-m2m-client/SOME_OTHER_SECRET~~
User: arn:aws:sts::285943604611:assumed-role/qa-assume-auth0-m2m-config/fca27abb-80ba-4040-83c1-3e7a85e308da is not authorized to perform: secretsmanager:DescribeSecret on resource: qa/lambda-test-m2m-client/SOME_OTHER_SECRET because no identity-based policy allows the secretsmanager:DescribeSecret action
~~~~~~~~~~~~~~~~~~~~~~~~
```

4. If you are running integration tests locally, you should also be sure to:

* Set the environment variable `AUTH0_M2M_CONFIG_CLIENT_CREDENTIALS` with the value (get rid of the `\`) from`aws secretsmanager get-secret-value --secret-id=qa/auth0-m2m-config/M2M_AUTH0_CLIENT_CREDENTIALS`
* Build the integration container:

```sh
awsume prod
docker compose build integration
```

* Assume the correct role:

```sh
awsume permissions-platform-qa-auth0-m2m-config-task-role
```

* Start the integration tests:

```sh
docker compose up \
  --exit-code-from integration \
  --abort-on-container-exit \
  --remove-orphans \
  integration
```
