The most common usage of Catalyst's Authentication framework, is to map a user to a DBIC/CDBI user in a database (via Store::DBIC) .
At my work, we've created our own TypeKey server for use as a Signle Sign-On point for all of our internal apps. The TypeKey auth. plugin is by default a storeless credential. Meaning once a user if authenticated a basic hash of information sent back from the TypeKey server will be used as the user information for your app.
This is fine, except that you'll have no access to the nice Roles and ACL plugins for Authorization that the framework also provides. So, we should strive to integrate Credential::TypeKey with Store::DBIC somehow.
The process initially sounds simple, except for the fact that when a TypeKey authentication comes your app's way for the first time, the user will not be in your database!
Here's what I had to do to make everything play nice:
Credential/Store config
I'll use a snippet of my YAML config file as the example:
authentication: typekey: server : http://typekeyserver.com/ key_url : http://typekeyserver.com/regkeys.txt version : 1 auth_store : default dbic: user_class : MyApp::Model::DBIC::User user_field : username authorization: dbic: role_class : MyApp::Model::DBIC::Role role_field : role role_rel : map_user_role user_role_user_field: user
The main points of interest here are the authentication -> typekey -> auth_store
and authentication -> dbic
sections. By default the TypeKey plugin doesn't use an auth store, but Store::DBIC will set itself as the default store, so we need to make that connection. Also, the dbic
section only needs information about the user and nothing about passwords since that's all done remotely on the TypeKey server.
Create our Custom Plugin
Unfortunately, the Store::DBIC plugin expects a user to exist in the database, so in order to make things work the way we want, we'll have to create our own little plugin that subclasses Store::DBIC.
Your MyApp.pm's use Catalyst qw(...);
section should look something like this:
use Catalyst qw( ... Authentication Authentication::Credential::TypeKey Authentication::Store::DBIC::AutoCreate Authorization::ACL Authorization::Roles .... );
Catalyst::Plugin::Authentication::Store::DBIC::AutoCreate
doesn't exist anywhere on CPAN, so we'll have to put it in our own lib
directory.
This plugin consists of two files:
AutoCreate.pm
package Catalyst::Plugin::Authentication::Store::DBIC::AutoCreate; use strict; use warnings; use base qw( Catalyst::Plugin::Authentication::Store::DBIC ); use Catalyst::Plugin::Authentication::Store::DBIC::AutoCreate::Backend; sub setup { my $c = shift; $c->NEXT::setup(@_); $c->default_auth_store( Catalyst::Plugin::Authentication::Store::DBIC::AutoCreate::Backend->new( { auth => $c->config->{authentication}->{dbic}, authz => $c->config->{authorization}->{dbic} } ) ); } 1;
This will set the default store to an instance of our backend module.
AutoCreate/Backend.pm
package Catalyst::Plugin::Authentication::Store::DBIC::AutoCreate::Backend; use strict; use warnings; use base qw( Catalyst::Plugin::Authentication::Store::DBIC::Backend ); sub get_user { my( $self, $username, $verify, $results ) = @_; my $user = $self->SUPER::get_user( $username ); return $user if $user; $self->config->{ auth }->{ user_class }->create( $results ); $user = $self->SUPER::get_user( $username ); return $user; } 1;
This will let the superclass attempt to get the user as normal. If it fails, then we need to execute a create
then re-fetch the user.
Other than that, your user class will have to do something useful with that data passed to the create
sub.
Hopefully someone else will find this mini-tutorial useful. :)