auth0-integration 0.6.2

Auth0 client library for M2M token retrieval and JWT validation (RS256)
Documentation
# auth0-integration

Auth0 client library for Rust — handles M2M token retrieval, JWT validation, and user management via the Auth0 Management API.

## Installation

Add the crate to your `Cargo.toml`:

```toml
[dependencies]
auth0-integration = "0.6.1"
tokio = { version = "1", features = ["full"] }
```

## Configuration

The library reads Auth0 credentials from environment variables:

| Variable | Description |
|----------|-------------|
| `AUTH0_DOMAIN` | Auth0 tenant domain (e.g. `your-tenant.us.auth0.com`) |
| `AUTH0_CLIENT_ID` | M2M application client ID |
| `AUTH0_CLIENT_SECRET` | M2M application client secret |
| `AUTH0_AUDIENCE` | API identifier registered in Auth0 |

```rust
use auth0_integration::Auth0Config;

let config = Auth0Config::from_env().expect("Missing Auth0 env vars");
```

## Usage

### Obtaining an M2M access token

Use `Auth0ClientToken` to run the Client Credentials flow.

```rust
use auth0_integration::{Auth0Config, services::Auth0ClientToken};

#[tokio::main]
async fn main() {
    let config = Auth0Config::from_env().unwrap();
    let client = Auth0ClientToken::new(&config);

    let response = client.get_access_token().await.unwrap();

    // Print the raw JWT string
    println!("{}", response);

    // Access the decoded claims
    let decoded = response.access_token.decoded().unwrap();
    println!("Subject: {}", decoded.sub);
    println!("Issuer:  {}", decoded.iss);
}
```

### Calling the Auth0 Management API

Pass the raw token string to `Auth0Client` to interact with the Management API.

```rust
use auth0_integration::{Auth0Config, services::{Auth0Client, Auth0ClientToken}};
use auth0_integration::models::UpdateUserRequest;

#[tokio::main]
async fn main() {
    let config = Auth0Config::from_env().unwrap();
    let response = Auth0ClientToken::new(&config).get_access_token().await.unwrap();
    let client = Auth0Client::new(&config, response.access_token.token.clone());

    // Look up a user by email
    let users = client.get_user_by_email("user@example.com").await.unwrap();

    // Create a user with a role (returns error if creation fails)
    let role: auth0_integration::Role = "admin".parse().unwrap(); // also: "super_admin", "worker"
    let user = client
        .create_user("Jane Doe", "jane@example.com", &role)
        .await
        .unwrap();

    // Update a user
    let mut req = UpdateUserRequest::new();
    req.name = Some("Jane Doe".to_string());
    req.blocked = Some(false);
    let updated = client.update_user(&user.user_id, req).await.unwrap();
}
```

### Validating a JWT (RS256)

`TokenValidator` verifies the token signature, issuer, and audience against Auth0's JWKS endpoint.
It caches the JWKS keys in memory and refreshes them automatically on a cache miss or validation failure.

Create one instance per application (e.g. wrap in `Arc`) and reuse it across requests.

```rust
use std::sync::Arc;
use auth0_integration::{Auth0Config, TokenValidator};

#[tokio::main]
async fn main() {
    let config = Auth0Config::from_env().unwrap();
    let validator = Arc::new(TokenValidator::new());

    match validator.validate("eyJ...", &config).await {
        Ok(data) => println!("Valid! sub = {}", data.claims.sub),
        Err(e)   => eprintln!("Invalid token: {e}"),
    }
}
```

#### Using with Axum state

```rust
use std::sync::Arc;
use axum::{extract::State, http::HeaderMap};
use auth0_integration::{Auth0Config, TokenValidator};

#[derive(Clone)]
struct AppState {
    config: Arc<Auth0Config>,
    validator: Arc<TokenValidator>,
}

async fn protected(State(state): State<AppState>, headers: HeaderMap) {
    let token = headers
        .get("Authorization")
        .and_then(|v| v.to_str().ok())
        .and_then(|v| v.strip_prefix("Bearer "))
        .unwrap();

    let data = state.validator.validate(token, &state.config).await.unwrap();
    println!("sub = {}", data.claims.sub);
}
```

### Checking permissions (scopes)

```rust
use auth0_integration::models::AccessToken;

let token = AccessToken::new("eyJ...".to_string());

// Single permission
let can_read = token.validate_permissions("read:users");

// Multiple — all must be present to return true
let can_manage = token.validate_permissions(["read:users", "update:users"]);
```

### Decoding a token payload (without verification)

```rust
use auth0_integration::models::AccessToken;

let token = AccessToken::new("eyJ...".to_string());

match token.decoded() {
    Ok(decoded) => println!("{:?}", decoded),
    Err(e)      => eprintln!("Invalid token format: {e}"),
}
```

## Key types

| Type | Module | Description |
|------|--------|-------------|
| `Auth0Config` | `auth0_integration` | Auth0 credentials loaded from env |
| `AppError` | `auth0_integration` | Unified error type |
| `Auth0ClientToken` | `auth0_integration::services` | Fetches M2M access tokens |
| `Auth0Client` | `auth0_integration::services` | Auth0 Management API client |
| `TokenValidator` | `auth0_integration` | Validates JWT strings against Auth0 JWKS with in-memory key cache |
| `AccessToken` | `auth0_integration::models` | JWT wrapper with lazy decoded payload |
| `AccessTokenResponse` | `auth0_integration::models` | Full token endpoint response |
| `DecodedAccessToken` | `auth0_integration::models` | Typed JWT claims (`sub`, `iss`, `exp`, etc.) |
| `Auth0User` | `auth0_integration::models` | Auth0 user object |
| `Role` | `auth0_integration` | User role enum (`Admin`, `SuperAdmin`, `Worker`); parses from `"admin"` / `"super_admin"` / `"worker"` |
| `UpdateUserRequest` | `auth0_integration::models` | Payload for PATCH `/api/v2/users/{id}` |
| `CreateUserRequest` | `auth0_integration::models` | Payload for POST `/api/v2/users` |

## License

MIT