view release on metacpan or search on metacpan
* Fixed:
* Retroactively updated the change log...
1.30 2017-05-09 00:36:25+01:00 Europe/London
* New:
* The current user can be manually set by passing an argument to
current_user (preaction)
* Added a note in the documentation on possible namespace conflicts
* Fixed:
* Fixed an error that made the auto_validate key not behave as
documented (jjatria)
* Fixed the tests for Travis CI (preaction)
* Fixed some typos in sample code (moltar)
* Changes:
* Simplified some of the conditional code in the distribution
1.29 2015-10-28 09:35:22+01:00 Europe/Berlin
Implements new parameter to control response from routing via condition
1.28 2015-09-30 09:51:30+02:00 Europe/Paris
Fix for bug that'd consider a defined-but-false return value from load_user/validate_user
as a failed login/load attempt. (via carragom/github)
1.27 2015-06-16 21:28:06+07:00 Asia/Jakarta
Merged pull requests fixing the documentation (bridge -> under)
1.26 2013-08-27 10:02:37 Asia/Jakarta
Merged a pull request from kthakore that fixed the broken tests - thanks!
Added 'auto_validate' option to extra data in authentication sub
1.24 2012-08-02 01:36:17 Asia/Jakarta
Removed the example app until next release to resolve some RPM build issues, it will return in the next release
1.23 2012-07-03 13:56:38 Asia/Jakarta
Issue #5 closed; the signed and authenticated conditions only worked if you indicated 'required' to be true, this got fixed.
(Reported by SailingYYC)
1.22 2012-03-30 08:43:13 Asia/Jakarta
Ed W contributed some more functionality and renamed some options and methods to make things a bit more sane.
1.20 2011-12-15 15:00:24 Asia/Jakarta
Added "lazy_mode" and "signature_exists" along with some doc patches (meettya)
1.19 2011-09-25 21:26:38 Asia/Jakarta
Added an 'extra_data' hashref to the authenticate method that will be passed to your registered callback,
and merged in some documentation fixes courtesy of metaperl.
1.18 2011-07-27 01:15:43 Asia/Jakarta
Fixed a bug for newer versions of Mojolicious that no longer send the same set of parameters to a hook
Added the ability to pass extra data to the validate_user callback, it came up as a need in a project so ported it over
1.16 2011-06-04 18:29:01 Asia/Jakarta
POD fixes and cleanup
1.15 2011-05-02 02:32:12 Asia/Jakarta
Added a README.pod file for Github to display, since the "real" README is generated by Dist::Zilla
1.14 2011-04-30 00:39:11 Asia/Jakarta
Code cleanup (memowe)
1.13 2011-04-25 17:49:13 Asia/Jakarta
Added extra check on load_user and validate_user configuration options to make sure they're not only there,
but they are actually code refs
1.12 2011-04-25 16:00:21 Asia/Jakarta
A little bit of code cleanup, no major changes
Mojolicious::Plugin::Authentication - A plugin to make authentication a bit easier
# SYNOPSIS
use Mojolicious::Plugin::Authentication;
$self->plugin('Authentication' => {
autoload_user => 1,
session_key => 'wickedapp',
load_user_p => sub { ... },
validate_user_p => sub { ... },
});
# ...
$self->authenticate_p(
'username', 'password',
{ optional => 'extra data stuff' },
)->then(sub {
my ($authenticated) = @_;
if ($authenticated) {
# ...
}
});
# or, synchronous style
$self->plugin('Authentication' => {
autoload_user => 1,
session_key => 'wickedapp',
load_user => sub { ... },
validate_user => sub { ... },
current_user_fn => 'user', # compatibility with old code
});
my $authenticated = $self->authenticate(
'username', 'password',
{ optional => 'extra data stuff' },
);
if ($authenticated) {
...
}
# METHODS
Like other Mojolicious plugins, loading this plugin will import some function
helpers into the namespace of your application. This will not normally cause
any trouble, but be aware that if you define methods with the same names as
those below, you'll likely run into unexpected results.
## authenticate($username, $password, $extra\_data\_hashref)
Authenticate will use the supplied `load_user` and `validate_user`
subroutine refs to see whether a user exists with the given username and
password, and will set up the session accordingly. Returns true when the user
has been successfully authenticated, false otherwise. You can pass additional
data along in the `extra_data` hashref, it will be passed to your
`validate_user` subroutine as-is. If the extra data hash contains a key
`auto_validate`, the value of that key will be used as the UID, and
authenticate will not call your `validate_user` callback; this can be used
when working with OAuth tokens or other authentication mechanisms that do not
use a local username and password form.
## authenticate\_p($username, $password, $extra\_data\_hashref)
As above, but instead of returning a value, returns a promise of
same. Available even if only synchronous callbacks are provided as these
will be "promisified".
## is\_user\_authenticated
# CONFIGURATION
The following options can be set for the plugin, (but the "REQUIRED"
ones can be replaced with a promise-returning equivalent with `_p`
appended to the key):
- load\_user (REQUIRED)
A coderef for user loading (see ["USER LOADING"](#user-loading))
- validate\_user (REQUIRED)
A coderef for user validation (see ["USER VALIDATION"](#user-validation))
- session\_key (optional)
The name of the session key
- autoload\_user (optional)
Turn on/off automatic loading of user data - user data can be loaded only if
The coderef you pass to the load\_user configuration key has the following
signature:
sub {
my ($app, $uid) = @_;
...
return $user;
}
The uid is the value that was originally returned from the `validate_user`
coderef. You must return either a user object (it can be a hashref, arrayref,
or a blessed object) or undef.
# USER VALIDATION
User validation is what happens when we need to authenticate someone. The
coderef you pass to the `validate_user` configuration key has the following
signature:
sub {
my ($c, $username, $password, $extradata) = @_;
...
return $uid;
}
You must return either a user id or undef. The user id can be numerical or a
string. Do not return hashrefs, arrayrefs or objects, since the behaviour of
lib/Mojolicious/Plugin/Authentication.pm view on Meta::CPAN
our $VERSION = '1.39';
use Mojo::Base 'Mojolicious::Plugin';
use Mojo::Promise;
sub register {
my ($self, $app, $args) = @_;
$args = { %{ $args || {} } }; # copy as mutating
for my $cb_name (qw(load_user validate_user)) {
my $p_name = $cb_name."_p";
die __PACKAGE__, ": missing '$cb_name' subroutine ref in parameters\n"
unless grep ref eq 'CODE', @$args{$cb_name, $p_name};
$args->{$p_name} = sub {
my @r = eval { $args->{$cb_name}->(@_) };
$@ ? Mojo::Promise->reject($@) : Mojo::Promise->resolve(@r);
} if !$args->{$p_name};
}
if (defined $args->{lazy}) {
lib/Mojolicious/Plugin/Authentication.pm view on Meta::CPAN
"use 'autoload_user' instead\n";
$args->{autoload_user} = delete $args->{lazy};
}
my $autoload_user = $args->{autoload_user} // 0;
my $session_key = $args->{session_key} || 'auth_data';
my $our_stash_key = $args->{stash_key} || '__authentication__';
my $current_user_fn = $args->{current_user_fn} || 'current_user';
my $load_user_cb = $args->{load_user};
my $validate_user_cb = $args->{validate_user};
my $load_user_cb_p = $args->{load_user_p};
my $validate_user_cb_p= $args->{validate_user_p};
my $fail_render = ref $args->{fail_render} eq 'CODE'
? $args->{fail_render} : sub { $args->{fail_render} };
my $user_loader_sub = user_loader_closure(
$our_stash_key, $session_key, $load_user_cb,
);
my $user_loader_sub_p = user_loader_closure_p(
$our_stash_key, $session_key, $load_user_cb_p,
);
my $current_user = user_stash_extractor_closure(
$our_stash_key, $user_loader_sub,
);
my $current_user_p = user_stash_extractor_closure_p(
$our_stash_key, $user_loader_sub_p,
);
$app->helper(authenticate => authenticate_closure(
$our_stash_key, $session_key, $validate_user_cb, $current_user,
));
$app->helper(authenticate_p => authenticate_closure_p(
$our_stash_key, $session_key, $validate_user_cb_p, $current_user_p,
));
$app->hook(before_dispatch => $user_loader_sub_p) if $autoload_user;
$app->routes->add_condition(authenticated => sub {
my ($r, $c, $captures, $required) = @_;
my $res = (!$required or $c->is_user_authenticated);
unless ($res) {
my $fail = $fail_render->(@_);
lib/Mojolicious/Plugin/Authentication.pm view on Meta::CPAN
? Mojo::Promise->resolve
: $user_loader_sub_p->($c);
$promise->then(sub {
$stash = $c->stash($our_stash_key);
return $stash->{user};
});
};
}
sub authenticate_closure {
my ($our_stash_key, $session_key, $validate_user_cb, $current_user) = @_;
sub {
my ($c, $user, $pass, $extradata) = @_;
# if extradata contains "auto_validate", assume the passed username
# is in fact valid, and auto_validate contains the uid; used for
# OAuth and other stuff that does not work with usernames and
# passwords; use this with extreme care if you must
$extradata ||= {};
my $uid = $extradata->{auto_validate} //
$validate_user_cb->($c, $user, $pass, $extradata);
return undef if !defined $uid;
$c->session($session_key => $uid);
# Clear stash to force reload of any already loaded user object
delete $c->stash->{$our_stash_key};
return 1 if defined $current_user->($c);
return undef;
};
}
sub authenticate_closure_p {
my ($our_stash_key, $session_key, $validate_user_cb_p, $current_user_p) = @_;
sub {
my ($c, $user, $pass, $extradata) = @_;
$extradata ||= {};
my $promise = defined($extradata->{auto_validate})
? Mojo::Promise->resolve($extradata->{auto_validate})
: $validate_user_cb_p->($c, $user, $pass, $extradata);
$promise->then(sub {
my ($uid) = @_;
return undef if !defined $uid;
$c->session($session_key => $uid);
# Clear stash to force reload of any already loaded user object
delete $c->stash->{$our_stash_key};
$current_user_p->($c);
})->then(sub {
defined $_[0] ? 1 : undef;
});
lib/Mojolicious/Plugin/Authentication.pm view on Meta::CPAN
Mojolicious::Plugin::Authentication - A plugin to make authentication a bit easier
=head1 SYNOPSIS
use Mojolicious::Plugin::Authentication;
$self->plugin('Authentication' => {
autoload_user => 1,
session_key => 'wickedapp',
load_user_p => sub { ... },
validate_user_p => sub { ... },
});
# ...
$self->authenticate_p(
'username', 'password',
{ optional => 'extra data stuff' },
)->then(sub {
my ($authenticated) = @_;
if ($authenticated) {
# ...
}
});
# or, synchronous style
$self->plugin('Authentication' => {
autoload_user => 1,
session_key => 'wickedapp',
load_user => sub { ... },
validate_user => sub { ... },
current_user_fn => 'user', # compatibility with old code
});
my $authenticated = $self->authenticate(
'username', 'password',
{ optional => 'extra data stuff' },
);
if ($authenticated) {
...
}
=head1 METHODS
Like other Mojolicious plugins, loading this plugin will import some function
helpers into the namespace of your application. This will not normally cause
any trouble, but be aware that if you define methods with the same names as
those below, you'll likely run into unexpected results.
=head2 authenticate($username, $password, $extra_data_hashref)
Authenticate will use the supplied C<load_user> and C<validate_user>
subroutine refs to see whether a user exists with the given username and
password, and will set up the session accordingly. Returns true when the user
has been successfully authenticated, false otherwise. You can pass additional
data along in the C<extra_data> hashref, it will be passed to your
C<validate_user> subroutine as-is. If the extra data hash contains a key
C<auto_validate>, the value of that key will be used as the UID, and
authenticate will not call your C<validate_user> callback; this can be used
when working with OAuth tokens or other authentication mechanisms that do not
use a local username and password form.
=head2 authenticate_p($username, $password, $extra_data_hashref)
As above, but instead of returning a value, returns a promise of
same. Available even if only synchronous callbacks are provided as these
will be "promisified".
=head2 is_user_authenticated
lib/Mojolicious/Plugin/Authentication.pm view on Meta::CPAN
The following options can be set for the plugin, (but the "REQUIRED"
ones can be replaced with a promise-returning equivalent with C<_p>
appended to the key):
=over 4
=item load_user (REQUIRED)
A coderef for user loading (see L</"USER LOADING">)
=item validate_user (REQUIRED)
A coderef for user validation (see L</"USER VALIDATION">)
=item session_key (optional)
The name of the session key
=item autoload_user (optional)
Turn on/off automatic loading of user data - user data can be loaded only if
lib/Mojolicious/Plugin/Authentication.pm view on Meta::CPAN
The coderef you pass to the load_user configuration key has the following
signature:
sub {
my ($app, $uid) = @_;
...
return $user;
}
The uid is the value that was originally returned from the C<validate_user>
coderef. You must return either a user object (it can be a hashref, arrayref,
or a blessed object) or undef.
=head1 USER VALIDATION
User validation is what happens when we need to authenticate someone. The
coderef you pass to the C<validate_user> configuration key has the following
signature:
sub {
my ($c, $username, $password, $extradata) = @_;
...
return $uid;
}
You must return either a user id or undef. The user id can be numerical or a
string. Do not return hashrefs, arrayrefs or objects, since the behaviour of
t/01-functional.t view on Meta::CPAN
use lib path(qw( t lib ))->to_string;
use TestUtils;
package Local::App::Base {
use Mojolicious::Lite;
plugin Authentication => {
autoload_user => 1,
load_user => \&TestUtils::load_user_t,
validate_user => \&TestUtils::validate_user_t,
};
get '/' => sub {
shift->render(text => 'index page');
};
post '/login' => sub {
my $self = shift;
my $u = $self->req->param('u');
my $p = $self->req->param('p');
t/01-functional.t view on Meta::CPAN
};
};
package Local::App::Unauthorized {
use Mojolicious::Lite;
plugin Authentication => {
autoload_user => 1,
fail_render => { status => 401, json => { message => 'Unauthorized' } },
load_user => \&TestUtils::load_user_t,
validate_user => \&TestUtils::validate_user_t,
};
get '/condition/authonly' => ( authenticated => 1 ) => sub {
shift->render( text => 'authenticated condition' );
};
}
subtest 'Tests with fail_render' => sub {
my $t = Test::Mojo->new('Local::App::Unauthorized');
t/01-functional.t view on Meta::CPAN
->status_is(401)
->json_is('/message' => 'Unauthorized');
};
package Local::App::Promise {
use Mojolicious::Lite;
plugin Authentication => {
autoload_user => 1,
load_user_p => \&TestUtils::load_user_t_p,
validate_user_p => \&TestUtils::validate_user_t_p,
};
get '/condition/authonly' => ( authenticated => 1 ) => sub {
shift->render( text => 'authenticated condition' );
};
}
subtest 'Tests with async handlers' => sub {
my $t = Test::Mojo->new('Local::App::Promise');
t/02-functional_lazy.t view on Meta::CPAN
use lib path(qw( t lib ))->to_string;
use TestUtils;
package Local::App::Blocking {
use Mojolicious::Lite;
plugin Authentication => {
autoload_user => 0,
load_user => \&TestUtils::load_user_t,
validate_user => \&TestUtils::validate_user_t,
};
get '/' => sub {
shift->render( text => 'index page' );
};
post '/login' => sub {
my $self = shift;
my $u = $self->req->param('u');
my $p = $self->req->param('p');
t/02-functional_lazy.t view on Meta::CPAN
get '/condition/authonly/lazy' => ( signed => 1 ) => sub {
shift->render( text => 'signed authenticated condition' );
};
get '/logout' => sub {
my $self = shift;
$self->logout;
$self->render( text => 'logout' );
};
get '/auto_validate' => sub {
my $self = shift;
eval {
$self->authenticate( undef, undef, { auto_validate => 'userid' } );
1;
} or return $self->reply->exception('failed');
$self->render( text => 'ok' );
};
}
subtest 'Blocking tests' => sub {
my $t = Test::Mojo->new('Local::App::Blocking');
t/02-functional_lazy.t view on Meta::CPAN
$t->get_ok('/authonly')
->status_is(200)
->content_is('not authenticated');
$t->get_ok('/authonly/lazy')
->status_is(200)
->content_is('sign not authenticated');
};
subtest 'Auto-validate' => sub {
$t->get_ok('/auto_validate')
->status_is(200);
};
};
package Local::App::Async {
use Mojolicious::Lite;
plugin Authentication => {
autoload_user => 0,
load_user_p => \&TestUtils::load_user_t_p,
validate_user_p => \&TestUtils::validate_user_t_p,
};
post '/login' => sub {
my $self = shift;
my $u = $self->req->param('u');
my $p = $self->req->param('p');
$self->authenticate_p( $u, $p )->then( sub {
$self->render( text => $_[0] ? 'ok' : 'failed' );
});
t/03-current_user.t view on Meta::CPAN
use strict;
use warnings;
# Disable IPv6, epoll and kqueue
BEGIN { $ENV{MOJO_NO_IPV6} = $ENV{MOJO_POLL} = 1 }
use Test::More;
use Mojo::File qw(path);
use lib path(qw(t lib))."";
use TestUtils qw(load_user_t validate_user_t load_user_t_p validate_user_t_p);
# testing code starts here
use Mojolicious::Lite;
use Test::Mojo;
plugin 'Authentication', {
autoload_user => 1,
load_user => \&load_user_t,
validate_user => \&validate_user_t,
};
get '/other/endpoint' => sub {
my $self = shift;
$self->authenticate( 'foo', 'bar' );
$self->render( text => $self->current_user->{username} );
};
under '/api' => sub {
my $self = shift;
t/03-current_user.t view on Meta::CPAN
};
under '/'; # reset
my $t = Test::Mojo->new;
$t->get_ok( '/other/endpoint' )->status_is( 200 )->content_is( 'foo' );
$t->get_ok( '/api/endpoint' )->status_is( 200 )->content_is( 'custom' );
plugin 'Authentication', {
autoload_user => 1,
load_user_p => \&load_user_t_p,
validate_user_p => \&validate_user_t_p,
};
get '/other/endpoint_p' => sub {
my $c = shift;
$c->authenticate_p( 'foo', 'bar' )
->then(sub { $c->current_user_p })
->then(sub { $c->render( text => $_[0]->{username} ) });
};
under '/api_p' => sub {
my $c = shift;
$c->current_user_p( { username => 'custom' } )->then(sub { $c->continue });
t/lib/TestUtils.pm view on Meta::CPAN
package TestUtils;
use strict;
use warnings;
use Exporter 'import';
use Mojo::Promise;
our @EXPORT_OK = qw(
load_user_t validate_user_t
load_user_t_p validate_user_t_p
);
sub load_user_t {
my $self = shift;
my $uid = shift;
return {
'username' => 'foo',
'password' => 'bar',
'name' => 'Foo'
}
if ( $uid eq 'userid' ) || $uid eq 'useridwithextradata';
return undef;
}
sub validate_user_t {
my $self = shift;
my $username = shift || '';
my $password = shift || '';
my $extradata = shift || {};
return 'useridwithextradata' if($username eq 'foo' && $password eq 'bar' && ( $extradata->{'ohnoes'} || '' ) eq 'itsameme');
return 'userid' if($username eq 'foo' && $password eq 'bar');
return undef;
}
sub load_user_t_p { Mojo::Promise->resolve(load_user_t(@_)) }
sub validate_user_t_p { Mojo::Promise->resolve(validate_user_t(@_)) }