Last active
September 25, 2018 18:34
Bits and pieces that will hopefully some day become an OpenID plugin for Mojolicious (with alternative dummy backend for testing and token backend for API users)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package MyApp::Controller::Auth; | |
use Mojo::Base 'Mojolicious::Controller'; | |
sub check { | |
my $self = shift; | |
my $role = $self->stash('role'); | |
my $user = $self->current_user; | |
# User needs to log in or a different role | |
$self->render('permissions', status => 403) and return undef | |
unless $user && $self->users->has_role($user, $role); | |
return 1; | |
} | |
sub logout { | |
my $self = shift; | |
delete $self->session->{user}; | |
$self->redirect_to('dashboard'); | |
} | |
1; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package MyApp::Controller::Auth::Dummy; | |
use Mojo::Base 'Mojolicious::Controller'; | |
sub login { | |
my $self = shift; | |
my $user = $self->users->find_or_create( | |
login => 'tester', | |
email => 'tester@example.com', | |
fullname => 'Dummy Test User', | |
roles => ['manager', 'admin'] | |
); | |
$self->session(user => $user->{login}); | |
$self->redirect_to('dashboard'); | |
} | |
1; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package MyApp; | |
use Mojo::Base 'Mojolicious'; | |
... | |
sub startup { | |
my $self = shift; | |
... | |
# Authentication | |
my $public = $self->routes; | |
my $bot = $public->under('/')->to('Auth::Token#check'); | |
my $manager = $public->under('/' => {role => 'manager'})->to('Auth#check'); | |
my $admin = $public->under('/' => {role => 'admin'})->to('Auth#check'); | |
if ($config->{openid}) { | |
$public->get('/login')->to('Auth::OpenID#login')->name('login'); | |
$public->get('/openid')->to('Auth::OpenID#openid')->name('openid'); | |
$public->get('/response')->to('Auth::OpenID#response')->name('response'); | |
} | |
else { $public->get('/login')->to('Auth::Dummy#login')->name('login') } | |
$public->get('/logout')->to('Auth#logout')->name('logout'); | |
... | |
# Bot API | |
$bot->get(...)->...; | |
# Manager API | |
$manager->post(...)->...; | |
# Admin API | |
$admin->patch(...)->...; | |
... | |
} | |
1; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package MyApp::Controller::Auth::OpenID; | |
use Mojo::Base 'Mojolicious::Controller'; | |
use LWP::UserAgent; | |
use Net::OpenID::Consumer; | |
sub login { | |
my $self = shift; | |
$self->csrf_token; | |
$self->redirect_to('openid'); | |
} | |
sub openid { | |
my $self = shift; | |
my $base = $self->req->url->base->to_string; | |
my $csr = Net::OpenID::Consumer->new( | |
ua => LWP::UserAgent->new, | |
required_root => $base, | |
consumer_secret => $self->csrf_token | |
); | |
my $claimed_id | |
= $csr->claimed_identity($self->app->config->{openid}{provider}); | |
return $self->render(text => $csr->err, status => 403) unless $claimed_id; | |
$claimed_id->set_extension_args('http://openid.net/extensions/sreg/1.1', | |
{required => 'email', optional => 'fullname,nickname'}); | |
$claimed_id->set_extension_args( | |
'http://openid.net/srv/ax/1.0', | |
{ | |
mode => 'fetch_request', | |
required => 'email,fullname,nickname,firstname,lastname', | |
'type.email' => "http://schema.openid.net/contact/email", | |
'type.fullname' => "http://axschema.org/namePerson", | |
'type.nickname' => "http://axschema.org/namePerson/friendly", | |
'type.firstname' => 'http://axschema.org/namePerson/first', | |
'type.lastname' => 'http://axschema.org/namePerson/last' | |
} | |
); | |
my $check_url = $claimed_id->check_url( | |
delayed_return => 1, | |
return_to => $self->url_for('response')->to_abs->to_string, | |
trust_root => $base | |
); | |
return $self->redirect_to($check_url) if $check_url; | |
$self->render(text => $csr->err, status => 403); | |
} | |
sub response { | |
my $self = shift; | |
my $params = $self->req->url->query->to_hash; | |
my $base = $self->req->url->base->to_string; | |
my $csr = Net::OpenID::Consumer->new( | |
ua => LWP::UserAgent->new, | |
required_root => $base, | |
consumer_secret => $self->csrf_token, | |
args => $params | |
); | |
my ($error, $login, $email, $fullname); | |
$csr->handle_server_response( | |
not_openid => sub { $error = 'Not an OpenID message' }, | |
setup_needed => sub { $error = 'Setup not supported' }, | |
cancelled => sub { $error = 'Authentication cancelled' }, | |
verified => sub { | |
my $vident = shift; | |
my $sreg = $vident->signed_extension_fields( | |
'http://openid.net/extensions/sreg/1.1'); | |
my $ax = $vident->signed_extension_fields('http://openid.net/srv/ax/1.0'); | |
$error = 'Missing username' | |
unless $login = $sreg->{nickname} || $ax->{'value.nickname'}; | |
$email = $sreg->{email} || $ax->{'value.email'}; | |
$fullname = $sreg->{fullname} || $ax->{'value.fullname'}; | |
}, | |
error => sub { | |
my ($err, $txt) = @_; | |
$error = "$err: $txt"; | |
}, | |
); | |
return $self->render(text => $error, status => 403) if $error; | |
# Create in DB | |
my $user = $self->users->find_or_create( | |
login => $login, | |
email => $email, | |
fullname => $fullname | |
); | |
$self->session(user => $user->{login}); | |
$self->redirect_to('dashboard'); | |
} | |
1; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package MyApp::Controller::Auth::Token; | |
use Mojo::Base 'Mojolicious::Controller'; | |
sub check { | |
my $self = shift; | |
my $tokens = $self->app->config('tokens'); | |
return 1 unless @$tokens; | |
$self->_denied and return undef | |
unless my $auth = $self->req->headers->authorization; | |
$self->_denied and return undef unless $auth =~ /^Token\ (\S+)$/; | |
my $token = $1; | |
$self->_denied and return undef unless grep { $token eq $_ } @$tokens; | |
return 1; | |
} | |
sub _denied { | |
my $self = shift; | |
$self->render('permissions', status => 403); | |
} | |
1; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment