Catalyst-Plugin-OpenIDConnect
view release on metacpan or search on metacpan
lib/Catalyst/Plugin/OpenIDConnect.pm view on Meta::CPAN
use Crypt::OpenSSL::RSA;
use Crypt::PK::RSA;
use JSON::MaybeXS qw(encode_json decode_json);
use Try::Tiny;
use DateTime;
use DateTime::Format::ISO8601;
use Data::UUID;
use URI;
our $VERSION = '0.15';
=head1 NAME
Catalyst::Plugin::OpenIDConnect - OpenID Connect provider plugin for Catalyst
=head1 DESCRIPTION
A Catalyst plugin implementing the OpenID Connect specification,
providing OAuth 2.0 authentication and authorization. Note that this plugin
does not implement the OIDC Client role; it is intended for applications
acting as OIDC providers (authorization servers).
This plugin provides the core OpenIDConnect functionality (JWT handling,
state management, and a reusable controller). To use it in your application,
you must create a controller in your app's namespace that extends the plugin's
controller (see below). This allows you to keep full control over your routing
while cooperating with ACL and other route-processing plugins.
=head1 CONFIGURATION
package MyApp;
use Catalyst qw/
OpenIDConnect
Session
Session::Store::File
Session::State::Cookie
/;
MyApp->config(
'Plugin::OpenIDConnect' => {
issuer => {
url => 'http://localhost:5000',
private_key_file => '/path/to/private.pem',
public_key_file => '/path/to/public.pem',
key_id => 'key-123',
},
clients => {
'my-client' => {
client_secret => 'secret123',
redirect_uris => ['http://localhost:3000/callback'],
response_types => ['code'],
grant_types => ['authorization_code'],
scope => 'openid profile email',
},
},
},
);
=head1 CREATING THE OPENIDCONNECT CONTROLLER
To enable the OpenIDConnect endpoints, create a controller in your app that extends
the plugin's controller. Create the file C<lib/MyApp/Controller/OpenIDConnect.pm>
(where MyApp is your app's namespace) with the following content:
package MyApp::Controller::OpenIDConnect;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Plugin::OpenIDConnect::Controller::Root' }
__PACKAGE__->meta->make_immutable;
1;
Then, in your main app module, explicitly load this controller before setup:
package MyApp;
use Catalyst qw/
OpenIDConnect
Session
Session::Store::File
Session::State::Cookie
/;
# Load the controller before setup so Catalyst discovers it
use MyApp::Controller::OpenIDConnect;
MyApp->config(...);
MyApp->setup(...);
Setting up the controller in this way allows you to keep full control over your
routing, and avoid namespace conflicts with ACL and other route-processing plugins.
The plugin's controller will automatically mount the standard OpenID Connect
endpoints (e.g. C</authorize>, C</token>, C</userinfo>) under the C</openidconnect>
path, so you can access them at C</openidconnect/authorize>, etc.
=head1 ROUTES ADDED TO THE APPLICATION
The plugin's controller adds the following routes to the application:
GET /.well-known/openid-configuration
GET /openidconnect/authorize
POST /openidconnect/token
GET /openidconnect/userinfo
GET /openidconnect/jwks
POST /openidconnect/logout
=cut
requires 'config', 'log', 'uri_for', 'user', 'request', 'response';
# Per-application-class storage for JWT and Store instances.
# Keyed by consuming application class name so that multiple Catalyst apps
# loaded in the same Perl interpreter each hold their own instances and cannot
# accidentally overwrite each other's JWT keys or stores (MED-3).
my %_oidc_jwt_by_class;
my %_oidc_store_by_class;
=head1 ATTRIBUTES
=head2 _oidc_jwt
JWT handler instance.
=cut
# Accessor method for JWT handler
sub _oidc_jwt {
my ($self, $value) = @_;
my $class = ref($self) || $self;
if (defined $value) {
die 'JWT handler must be an instance of Catalyst::Plugin::OpenIDConnect::Utils::JWT'
unless ref $value && $value->isa('Catalyst::Plugin::OpenIDConnect::Utils::JWT');
$_oidc_jwt_by_class{$class} = $value;
}
return $_oidc_jwt_by_class{$class};
}
=head2 _oidc_store
State and code storage.
=cut
# Accessor method for Store handler
sub _oidc_store {
my ($self, $value) = @_;
my $class = ref($self) || $self;
if (defined $value) {
die 'Store handler must implement Catalyst::Plugin::OpenIDConnect::Role::Store'
unless ref $value && $value->DOES('Catalyst::Plugin::OpenIDConnect::Role::Store');
$_oidc_store_by_class{$class} = $value;
}
return $_oidc_store_by_class{$class};
( run in 0.852 second using v1.01-cache-2.11-cpan-9581c071862 )