# backend-jwtauth-testing

TypeScript library for JWT operations, including generating, validating, and decoding JWT tokens with Auth0 integration.

## Installation

```bash
pnpm add @theorchard/backend-jwtauth-testing
```

## Features

- Generate JWT bearer tokens with Auth0 authentication
- Support for Multi-Factor Authentication (MFA)
- Fetch credentials from AWS Secrets Manager
- Type-safe interfaces for Auth0 and user credentials

## Usage

## Environment Variables

Configure the `Auth0 endpoint` and `audience` using environment variables:

```bash
export AUTH_URL="https://your-domain.auth0.com/oauth/token"
export AUTH_AUDIENCE="https://your-api.example.com/api"
```

If not set, defaults to:
- `AUTH_URL`: `https://qa-orchard.auth0.com/oauth/token`
- `AUTH_AUDIENCE`: `https://workstation.qaorch.com/api`

### Secret Contents
📝 _Editor's Note: To use `loginFromSecretsManager` you will need your secrets to match these schemas. If they don't, you can use the `generateBearerJwtToken` or `generateBearerJwtTokenMfa` methods directly. This is consistent across all `jwtauth-testing` language libraries._ 

The auth0 app credentials secret must contain a JSON object matching
the [Auth0CredsSchema](./src/types/auth0Creds.ts).

Example:
```json
{
    "auth0_client_id": "<client-id>",
    "auth0_client_secret": "<secret>"
}
```

The user credentials secret must contain a JSON object matching the
[UserCredsSchema](./src/types/userCreds.ts). Set `otp_secret_key` to the value generated when setting
up the user (see ["How to integration test authenticated endpoints"](https://www.notion.so/How-to-integration-test-authenticated-endpoints-699b4ea8b0f242c1b53bc124ae76ea2b?source=copy_link#28197177520f80a4b444ef32306dc9d4)) or set it to `null`
when disabling MFA.

Example:
```json
{
    "email": "myapptester@sonymusic-pde.com",
    "password": "<password>",
    "otp_secret_key": "<base32secret3232>"
}
```

### Login from Secrets Manager
Fetch credentials from AWS Secrets Manager and generate a token in one step!

📝 _Editor's Note: This is likely the method you want to use in your tests. This will call `generateBearerJwtToken` or `generateBearerJwtTokenMfa` as needed._  


```typescript
import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager';
import { SecretsManager } from '@theorchard/backend-secrets-manager-sdk-v3';
import { loginFromSecretsManager } from '@theorchard/backend-jwtauth-testing';

// Initialize AWS Secrets Manager client
const secretsManager = new SecretsManager({
  secretsManagerClient: new SecretsManagerClient({ region: 'us-east-1' }),
});

const getUserCredsArgs = {
  service_name: 'my-service',
  secret_name: 'USER_CREDENTIALS',
  environment: 'qa',
};

const getAuth0CredsArgs = {
  service_name: 'my-service',
  secret_name: 'AUTH0_CREDENTIALS',
  environment: 'qa',
};

try {
  const token = await loginFromSecretsManager(
    getUserCredsArgs,
    getAuth0CredsArgs,
    secretsManager
  );
  console.log('Bearer token from secrets:', token);
} catch (error) {
  console.error('Login failed:', error);
}
```


### Generate Bearer JWT Token
Generate a JWT token using `user credentials` and `Auth0 credentials`.

**Note:** The `generateBearerJwtToken` function automatically handles MFA challenges. If Auth0 requires MFA, it will automatically call `generateBearerJwtTokenMfa` internally.


```typescript
import { generateBearerJwtToken, UserCredsSchema, Auth0CredsSchema } from '@theorchard/backend-jwtauth-testing';

const userCreds = UserCredsSchema.parse({
  email: 'user@example.com',
  password: 'secure-password',
  otp_secret_key: null, // Optional: only needed for MFA
});

const auth0Creds = Auth0CredsSchema.parse({
  auth0_client_id: 'your-client-id',
  auth0_client_secret: 'your-client-secret',
});

try {
  const token = await generateBearerJwtToken(userCreds, auth0Creds);
  console.log('Bearer token:', token);
} catch (error) {
  console.error('Failed to generate token:', error);
}
```

### Generate Bearer JWT Token with MFA

Generate a JWT token when Multi-Factor Authentication is required.

```typescript
import { 
  generateBearerJwtTokenMfa, 
  MfaTokenLookupError,
  UserCredsSchema,
  Auth0CredsSchema 
} from '@theorchard/backend-jwtauth-testing';

const userCreds = UserCredsSchema.parse({
  email: 'user@example.com',
  password: 'secure-password',
  otp_secret_key: 'JBSWY3DPEHPK3PXP', // Required for MFA
});

const auth0Creds = Auth0CredsSchema.parse({
  auth0_client_id: 'your-client-id',
  auth0_client_secret: 'your-client-secret',
});

const mfaToken = 'mfa-token-from-initial-auth';

try {
  const token = await generateBearerJwtTokenMfa(
    userCreds,
    auth0Creds,
    mfaToken
  );
  console.log('MFA Bearer token:', token);
} catch (error) {
  if (error instanceof MfaTokenLookupError) {
    console.error('MFA token generation failed:', error.message);
  }
}
```

## API Reference

### `loginFromSecretsManager(getUserCredsArgs, getAuth0CredsArgs, secretsManager): Promise<string>`

Fetch credentials from AWS Secrets Manager and generate a bearer token.

**Parameters:**
- `getUserCredsArgs`: Secret lookup info for user credentials
  - `service_name`: Service name
  - `secret_name`: Secret name
  - `environment`: Environment (e.g., 'qa', 'prod')
- `getAuth0CredsArgs`: Secret lookup info for Auth0 credentials
  - `service_name`: Service name
  - `secret_name`: Secret name
  - `environment`: Environment (e.g., 'qa', 'prod')
- `secretsManager`: AWS Secrets Manager instance

**Returns:** JWT bearer token string

**Throws:** `Error` if credential fetching or token generation fails

### `generateBearerJwtTokenMfa(userCreds, auth0Creds, mfaToken): Promise<string>`

Generate a bearer token with Multi-Factor Authentication.

**Parameters:**
- `userCreds`: User credentials with OTP secret key
- `auth0Creds`: Auth0 credentials (client ID and secret)
- `mfaToken`: MFA token from initial authentication

**Returns:** JWT bearer token string

**Throws:** `MfaTokenLookupError` if MFA token generation fails

### `generateBearerJwtToken(userCreds, auth0Creds): Promise<string>`

Generate an Auth0 bearer token. Automatically handles MFA if required.

**Parameters:**
- `userCreds`: User credentials (email, password, optional OTP secret key)
- `auth0Creds`: Auth0 credentials (client ID and secret)

**Returns:** JWT bearer token string

**Throws:** `Auth0Error` if authentication fails

## Examples

```typescript
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
import { SecretsManager } from "@theorchard/backend-secrets-manager-sdk-v3";
import { loginFromSecretsManager } from "@theorchard/backend-jwtauth-testing";

describe('Do some thing that requires PP permissions', () => {
    let token: string;

    beforeAll(async () => {
        const secretsManager = new SecretsManager({
                secretsManagerClient: new SecretsManagerClient(),
            });
        token = await loginFromSecretsManager(
            {
                service_name: "pdp-integration-test",
                secret_name: "PDP_TEST_USER_CREDENTIALS",
                environment: "qa",
            },
            {
                service_name: "pdp-integration-test",
                secret_name: "PDP_TEST_APP_AUTH0_CREDENTIALS",
                environment: "qa",
            },
            secretsManager,
        );
    });

    it('should fetch a token that can be used in a GraphQL request', () => {
        expect(token).toBeDefined();
    });
});
```
