Catalyst-Plugin-OpenIDConnect
view release on metacpan or search on metacpan
IMPLEMENTATION_GUIDE.md view on Meta::CPAN
clients => { ... },
},
);
MyApp->setup;
```
**Why this approach?**
Catalyst only auto-discovers controllers in the application's namespace. By creating an extending controller in your app's namespace, Catalyst can properly register the routes with the dispatcher. This ensures compatibility with ACL and other route-p...
### Protecting Routes
Check for authentication in your controllers:
```perl
sub protected : Local {
my ($self, $c) = @_;
unless ( $c->session->{user} ) {
return $c->response->redirect( $c->uri_for('/login') );
}
# User is authenticated
my $user = $c->session->{user};
}
```
### Accessing OIDC Context
From any controller:
```perl
my $oidc = $c->openidconnect;
# Get client config
my $client = $oidc->get_client('client-id');
# Get user claims
my $claims = $oidc->get_user_claims($user);
# Access JWT handler
my $token = $oidc->jwt->sign_token(sub => 'user-123');
# Access store
my $code = $oidc->store->create_authorization_code(...);
```
### Implementing the Login Action
When the OpenID Connect plugin redirects an unauthenticated user to your login page, it includes a `back` parameter specifying where to return after successful authentication. Your login action **must support the `back` parameter** to resume the auth...
```perl
sub login : Local {
my ( $self, $c ) = @_;
if ( $c->request->method eq 'POST' ) {
my $username = $c->request->params->{username};
my $password = $c->request->params->{password};
# Validate credentials against your user store
if ( validate_user($username, $password) ) {
my $user = get_user($username);
# Store user in session
$c->session->{user} = $user;
$c->session->{user_id} = $user->id;
# IMPORTANT: Redirect to the 'back' parameter if provided
# This resumes the authorization flow after authentication.
# Validate it to prevent open redirect (only allow relative paths).
my $back = $c->request->params->{back} || '/';
$back = '/' unless $back =~ m{^/[^/]};
return $c->response->redirect( $c->uri_for($back) );
}
$c->stash->{error} = 'Invalid credentials';
}
# Display login form
$c->stash->{template} = 'login.html';
}
```
The plugin will redirect to your login page like: `/login?back=/openidconnect/authorize`. After successful authentication, redirect back to the `back` URL to resume the authorization process.
## Security Considerations
### HTTPS Requirement
- In production, always use HTTPS for all OIDC endpoints
- Tokens are sensitive and must be transmitted over encrypted connections
### Key Management
- Store private keys securely (file permissions, secrets management)
- Rotate keys periodically
- Publish public keys via JWK Set endpoint
### Token Security
- ID tokens should be verified by clients using the public key
- Access tokens are bearer tokens - handle with care
- Refresh tokens should be stored securely (HTTP-only cookies)
### CSRF Protection
- Always verify the `state` parameter matches the session
- Nonce binding support (client responsibility to validate nonce matches)
### Code Security
- Authorization codes are one-time use only
- Codes expire after 10 minutes
- Code exchange requires client authentication
- Atomic fetch-and-delete in the store layer prevents TOCTOU race conditions
### PKCE (Proof Key for Code Exchange â RFC 7636)
- Public clients (those without a registered `client_secret`) **must** send `code_challenge` and `code_challenge_method=S256` in the authorization request
- Confidential clients are also strongly encouraged to use PKCE
- Only the `S256` method is supported; `plain` is rejected per OAuth 2.1 / security BCP
- The verifier must be 43â128 characters using only unreserved URI characters (`A-Z`, `a-z`, `0-9`, `-`, `.`, `_`, `~`)
- The server verifies `BASE64URL(SHA256(ASCII(code_verifier))) == code_challenge` with a constant-time comparison before issuing tokens
## Testing
Run the included tests:
```bash
# JWT functionality
prove -l t/01_jwt.t
# Store functionality
prove -l t/02_store.t
# All tests
prove -l t/
```
## Example Application
Start the example app:
( run in 1.992 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )