# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Package Overview

Auth0 CLI tool for searching and deleting users across multiple Auth0 tenants. Credentials are fetched from AWS Secrets Manager rather than environment variables.

## Running tests

```bash
cd lambda/auth0_cli && uv run pytest tests/                                                                        # All tests for this package
cd lambda/auth0_cli && uv run pytest tests/unit/                                                                   # Unit tests only
cd lambda/auth0_cli && uv run pytest tests/integration/                                                            # Integration tests only
cd lambda/auth0_cli && uv run pytest tests/unit/test_auth0_client.py                                               # Single file
cd lambda/auth0_cli && uv run pytest tests/unit/test_auth0_client.py::test_returns_user_list_when_email_exists     # Single test
```

## CLI usage

```bash
cd lambda/auth0_cli && uv run auth0-cli search-user-by-email --email user@example.com
cd lambda/auth0_cli && uv run auth0-cli delete-user-by-id --user-id auth0|abc123 --tenant prod-orchard
cd lambda/auth0_cli && uv run auth0-cli delete-user-by-id --user-id auth0|abc123 --tenant prod-orchard --dry-run
cd lambda/auth0_cli && uv run auth0-cli suspend-user-by-id --user-id auth0|abc123 --tenant prod-orchard
```

## Lambda actions

The handler in `auth0_client/app.py` dispatches on `event["action"]`:

| Action | Description | Write? |
|---|---|---|
| `search-user-by-email` | Search all tenants for a user by email | No |
| `delete-user-by-id` | Hard-delete a user from one tenant (offboarding) | Yes |
| `suspend-user-by-id` | Block a user in one tenant via `PATCH blocked:true` (suspension) | Yes |

`delete-user-by-id` and `suspend-user-by-id` accept `dry_run: true`. Both treat a 404
(user already absent) as idempotent success.

## Key Architecture

### Multi-tenant design
`search_user_by_email` iterates across all tenants in `Auth0Config.auth0_tenants` (11 tenants hardcoded, overridable via `AUTH0_TENANTS` env var). `delete_user_by_id` and `block_user_by_id` (the suspend mechanism) target a single tenant specified via `--tenant`.

### Delete vs. suspend
`delete_user_by_id` issues an HTTP `DELETE` (success = 204). `block_user_by_id` issues
an HTTP `PATCH` with `{"blocked": true}` (success = 200), disabling login while keeping
the account for later reinstatement. The Lambda action name is `suspend-user-by-id`
(matching `scripts/block-user-by-email.sh`); the underlying client method is
`block_user_by_id`.

### Credential flow
`SecretsManagerClient.get_tenant_credentials(tenant_name)` fetches from AWS Secrets Manager at path `shared/offboarding-automation/{tenant_name}/auth0_management_credentials`. The secret JSON must contain `auth0_domain`, `auth0_client_id`, `auth0_client_secret`. Requires AWS credentials in the environment (e.g. via IAM role or `AWS_PROFILE`).

### Token caching
`Auth0Client` caches the OAuth2 access token in-memory and reuses it until 60 seconds before expiry. In practice a fresh `Auth0Client` is constructed per tenant per invocation and makes a single Management API call, so the token is effectively fetched once per tenant — the cache exists but rarely saves a call in the current call paths.

### HTTP retry strategy
Both token requests and API calls use `urllib3.Retry` with 3 retries, backoff factor 1, on status codes 429/500/502/503/504.
