Catalyst-Plugin-OpenIDConnect

 view release on metacpan or  search on metacpan

IMPLEMENTATION_GUIDE.md  view on Meta::CPAN

The standard, most secure flow for web applications:

```
1. Client redirects user to /openidconnect/authorize with:
   - response_type=code
   - client_id
   - redirect_uri
   - scope
   - state (CSRF protection)
   - nonce (optional, binds to session)
   - code_challenge (required for public clients; recommended for all — see PKCE below)
   - code_challenge_method=S256 (required when code_challenge is supplied)

2. User authenticates (handled by application)

3. Server issues authorization code via redirect

4. Client exchanges code for tokens at /openidconnect/token with:
   - grant_type=authorization_code
   - code
   - redirect_uri
   - client_id
   - client_secret (omit for public clients)
   - code_verifier (required when code_challenge was sent in step 1)

5. Server verifies and issues:
   - id_token (JWT with user claims)
   - access_token (JWT for API access)
   - refresh_token (JWT for token refresh)
   - expires_in
```

### Token Types

#### ID Token
- Contains user identity claims (name, email, etc.)
- Signed JWT (RS256)
- Expires in 1 hour
- Includes nonce (if provided) for CSRF protection

#### Access Token
- Authorization token for API access
- Signed JWT
- Expires in 1 hour
- Contains scope information

#### Refresh Token
- Long-lived token for refreshing access tokens
- Signed JWT
- Expires in 30 days
- Not accessible to browser (HTTP-only cookies in production)

## Configuration

### Issuer Configuration

```perl
<Plugin::OpenIDConnect>
    <issuer>
        url = http://localhost:5000
        private_key_file = /path/to/private.pem
        public_key_file = /path/to/public.pem
        key_id = my-key-123
    </issuer>
```

**Fields:**
- `url` - The issuer identifier (in iss claim)
- `private_key_file` - Path to RSA private key (PEM format)
- `public_key_file` - Path to RSA public key (optional, derived from private)
- `key_id` - Key identifier for JWK Set

### Client Configuration

```perl
<clients>
    <my-client>
        client_secret             = secret123
        redirect_uris             = http://app.example.com/callback
        post_logout_redirect_uris = http://app.example.com/logged-out
        response_types            = code
        grant_types               = authorization_code refresh_token
        scope                     = openid profile email
    </my-client>
</clients>
```

**Fields:**
- `client_secret` - Shared secret for token endpoint
- `redirect_uris` - Arrayref or whitespace-separated string of URIs the client is permitted to redirect to after authorization
- `post_logout_redirect_uris` - Arrayref or whitespace-separated string of URIs the client is permitted to redirect to after logout. Required when the client uses `post_logout_redirect_uri` at the logout endpoint.
- `response_types` - Supported response types (e.g., "code")
- `grant_types` - Supported grant types (e.g., "authorization_code")
- `scope` - Default/allowed scopes

> Both `redirect_uris` and `post_logout_redirect_uris` accept the same formats:
> an arrayref in YAML/JSON/Perl-hash config, or a whitespace-separated string
> in Apache-style (`Config::General`) config. Both are matched by exact string
> comparison — prefix matching and host-only matching are not permitted.

### User Claims Mapping

Map user object attributes to OpenID Connect claims:

```perl
<user_claims>
    sub = id
    name = full_name
    email = email_address
    picture = avatar_url
    email_verified = is_email_verified
</user_claims>
```

The format is: `<oidc_claim> = <user_attribute_path>`

Nested attributes use dot notation: `claims = user.profile.claims`

## Standard OpenID Connect Claims

The plugin supports the following standard claims:

**Profile Claims:**
- `sub` - Subject (unique user identifier)
- `name` - Full name
- `given_name` - Given (first) name
- `family_name` - Family (last) name
- `middle_name` - Middle name
- `nickname` - Nickname



( run in 1.616 second using v1.01-cache-2.11-cpan-13bb782fe5a )