npm package: @schematize/authorization-code
Description: Schematize Authorization Code Auth Library
Author: Benjamin Bytheway
Latest version: 0.6.3

# @schematize/authorization-code

A comprehensive OAuth 2.0 Authorization Code flow implementation with PKCE (Proof Key for Code Exchange) support for secure client-side authentication.  This library provides a complete solution for implementing OAuth 2.0 authorization code flow in web applications with built-in security features and OpenID Connect support.

## Features

- **OAuth 2.0 Authorization Code Flow**: Complete implementation of the authorization code grant type
- **PKCE Support**: Built-in Proof Key for Code Exchange for enhanced security
- **OpenID Connect**: Full support for OpenID Connect with ID token validation
- **Secure Token Storage**: Automatic token management with expiration handling
- **CSRF Protection**: State parameter validation to prevent cross-site request forgery
- **JWT Validation**: Cryptographic signature verification for ID tokens
- **Automatic Token Refresh**: Handles token expiration and re-authorization
- **Cross-Tab Support**: Works across browser tabs and windows

## Dependencies

- `@schematize/refs`: Reference utilities for cross-platform compatibility
- `@schematize/metamodel`: Instance management and utilities
- `@schematize/util-common`: Common utility functions for encoding and data handling

## Installation

```bash
npm install @schematize/authorization-code
```

## Usage

```javascript
import AuthorizationCode from '@schematize/authorization-code';

const auth = AuthorizationCode({
  endpoints: {
    authorize: 'https://auth.example.com/authorize',
    token: 'https://auth.example.com/token',
    userInfo: 'https://auth.example.com/userinfo',
    wellKnownJwks: 'https://auth.example.com/.well-known/jwks.json',
    signout: 'https://auth.example.com/signout'
  },
  clientId: 'your-client-id',
  scope: 'openid profile email',
  responseType: 'code',
  responseMode: 'fragment'
});

// Initialize and check authorization status
await auth.initialized;

if (!auth.authorized) {
  // Redirect to authorization server
  await auth.authorize();
} else {
  // Use the access token
  console.log('Access token:', auth.accessToken);
  console.log('User info:', auth.userInfo);
}
```

## API Reference

### AuthorizationCode(config)

Creates a new authorization code flow instance with the specified configuration.

#### Parameters

- `config` (Object): Configuration object containing:
  - `endpoints` (Object): Required endpoint URLs
    - `authorize` (String): Authorization endpoint URL
    - `token` (String): Token endpoint URL or configuration object
    - `userInfo` (String): User info endpoint URL (optional)
    - `wellKnownJwks` (String): JWKS endpoint URL for token validation (optional)
    - `signout` (String): Sign-out endpoint URL (optional)
  - `clientId` (String): OAuth client identifier (required)
  - `scope` (String): Space-separated list of scopes (default: 'openid')
  - `responseType` (String): Response type (default: 'code')
  - `responseMode` (String): Response mode - 'query' or 'fragment' (default: 'fragment')
  - `issuer` (String): OpenID Connect issuer identifier (optional)

#### Returns

- `Object`: Authorization instance with methods and properties

#### Usage

```javascript
const auth = AuthorizationCode({
  endpoints: {
    authorize: 'https://auth.example.com/authorize',
    token: {
      url: 'https://auth.example.com/token',
      contentType: 'application/json'
    },
    userInfo: 'https://auth.example.com/userinfo',
    wellKnownJwks: 'https://auth.example.com/.well-known/jwks.json'
  },
  clientId: '274381130411827200',
  scope: 'openid profile email',
  responseType: 'code',
  responseMode: 'fragment',
  issuer: 'https://auth.example.com'
});
```

#### Features

- **Singleton Pattern**: Returns the same instance for identical configurations
- **Secure Context Required**: Only works in HTTPS contexts
- **Automatic Initialization**: Runs initialization checks on creation
- **Configuration Validation**: Validates required parameters and endpoints

### auth.initialize()

Initializes the authorization flow by checking for authorization codes, validating stored tokens, and setting up the authorization state.

#### Returns

- `Promise<void>`: Promise that resolves when initialization is complete

#### Usage

```javascript
await auth.initialize();
```

#### Features

- **Code Exchange**: Automatically exchanges authorization codes for tokens
- **Token Validation**: Validates stored tokens and checks expiration
- **ID Token Verification**: Cryptographically validates ID tokens
- **State Verification**: Prevents CSRF attacks using state parameter
- **URL Cleanup**: Removes authorization parameters from URL after processing

### auth.authorize(options)

Initiates the authorization flow by redirecting the user to the authorization server.

#### Parameters

- `options` (Object): Optional configuration object
  - `force` (Boolean): Force re-authorization even if already authorized
  - `redirectUri` (String): Custom redirect URI (defaults to current URL)
  - `state` (String): Custom state parameter (auto-generated if not provided)

#### Returns

- `Promise<Object|false>`: Returns the auth instance or false if redirecting

#### Usage

```javascript
// Basic authorization
await auth.authorize();

// Force re-authorization
await auth.authorize({ force: true });

// Custom redirect URI
await auth.authorize({ 
  redirectUri: 'https://app.example.com/callback' 
});
```

#### Features

- **PKCE Implementation**: Generates code verifier and challenge
- **State Generation**: Creates cryptographically secure state parameter
- **Automatic Redirect**: Redirects browser to authorization server
- **Storage Management**: Clears old tokens before new authorization
- **Error Handling**: Throws descriptive errors for authorization failures

### auth.signout(options)

Signs out the user and clears all stored authorization data.

#### Parameters

- `options` (Object): Optional configuration object
  - `redirectUri` (String): Redirect URI after sign-out (defaults to current URL)

#### Returns

- `Promise<false>`: Always returns false due to redirect

#### Usage

```javascript
await auth.signout();

// Custom redirect after sign-out
await auth.signout({ 
  redirectUri: 'https://app.example.com/goodbye' 
});
```

#### Features

- **Complete Cleanup**: Removes all stored tokens and user data
- **Server Sign-out**: Redirects to authorization server for SSO sign-out
- **State Reset**: Resets all authorization state properties
- **Storage Clearing**: Removes tokens from browser storage

### auth.getUserInfo()

Retrieves user information from the user info endpoint.

#### Returns

- `Promise<Object>`: Promise that resolves to user information object

#### Usage

```javascript
const userInfo = await auth.getUserInfo();
console.log('User:', userInfo.name, userInfo.email);
```

#### Features

- **Caching**: Returns cached user info if available
- **Automatic Headers**: Uses stored access token for authentication
- **Error Handling**: Graceful handling of API errors
- **Fallback Support**: Used as fallback when ID token validation fails

## Properties

### auth.authorized

- **Type**: `Boolean`
- **Description**: Indicates whether the client is currently authorized
- **Usage**: Check authorization status before making API calls

### auth.accessToken

- **Type**: `String`
- **Description**: The current access token for API authentication
- **Usage**: Include in Authorization header for API requests

### auth.headers

- **Type**: `Object`
- **Description**: Pre-configured headers object with Authorization header
- **Usage**: Use directly in fetch requests or API calls in connection with the OAuth 2.0 Bearer Token specification.

### auth.userInfo

- **Type**: `Object`
- **Description**: User information from ID token or user info endpoint
- **Usage**: Access user profile data like name, email, etc.

### auth.error

- **Type**: `String`
- **Description**: Error code from authorization server
- **Usage**: Handle authorization errors and display user-friendly messages

### auth.errorDescription

- **Type**: `String`
- **Description**: Human-readable error description
- **Usage**: Display detailed error information to users

### auth.errorUri

- **Type**: `String`
- **Description**: URI with additional error information
- **Usage**: Link to detailed error documentation

### auth.initialized

- **Type**: `Promise`
- **Description**: Promise that resolves when initialization is complete
- **Usage**: Wait for initialization before using other methods

## Security Features

### PKCE (Proof Key for Code Exchange)

- **Code Verifier**: 32-byte cryptographically random string
- **Code Challenge**: SHA-256 hash of code verifier, base64url encoded
- **Method**: Uses S256 method for enhanced security
- **Storage**: Code verifier stored temporarily in localStorage

### State Parameter

- **CSRF Protection**: Prevents cross-site request forgery attacks
- **Random Generation**: 32-byte cryptographically random value
- **Base64 Encoding**: Encoded for URL transmission
- **Validation**: Verified on return from authorization server

### Token Validation

- **JWT Verification**: Cryptographic signature validation using JWKS
- **Expiration Checking**: Validates token expiration times
- **Issuer Validation**: Verifies token issuer matches configuration
- **Audience Validation**: Ensures token is intended for this client

### Storage Security

- **Session Storage**: Tokens stored in sessionStorage by default
- **Automatic Cleanup**: Tokens removed after use or expiration
- **Secure Context**: Only works in HTTPS environments
- **Cross-Tab Support**: Uses localStorage for code verifier to support cross-tab flows

## Error Handling

The library provides comprehensive error handling for various scenarios:

```javascript
try {
  await auth.authorize();
} catch (error) {
  if (auth.error) {
    console.error('Authorization failed:', auth.error);
    console.error('Description:', auth.errorDescription);
    console.error('URI:', auth.errorUri);
  } else {
    console.error('Unexpected error:', error.message);
  }
}
```

## License

MIT

## Author

Benjamin Bytheway