Mojoli...ication(3pm)

πŸ“¦
version 1.38 Β· 2 versions
  • 1.38 Β· debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking (latest)
  • 1.37 Β· ubuntu jammy
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

✨ NAME ✨

Mojolicious::Plugin::Authentication β€” a plugin that makes authentication a bit easier. It's part of the User Contributed Perl Documentation, manual section 3pm, bestie. πŸŽ€


🌸 SYNOPSIS 🌸

okay so first things first, here's the async (promise-y) flavor:

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) {
        # ...
    }
});

and then the synchronous style, for when you're feeling old-school (respectfully):

# 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 πŸ’…

Just like every other Mojolicious plugin, loading this one imports some function helpers straight into your app's namespace. Normally that's totally fine, no drama πŸ’– β€” but heads up: if you go and define your own methods with the same names as the ones below, you'll very likely run into unexpected results. iykyk.

πŸ¦‹ authenticate($username, $password, $extra_data_hashref)

This one uses the load_user and validate_user sub refs you handed over to figure out whether a user actually exists with that username + password, and then it sets up the session accordingly. Returns true when the user got authenticated successfully, false otherwise.

You can also pass extra stuff along in the extra_data hashref β€” it gets handed to your validate_user subroutine as-is, no touching.

🫢 tiny but mighty detail: if the extra data hash contains a key auto_validate, the value of that key gets used as the UID, and authenticate will not call your validate_user callback. Super handy for OAuth tokens or other auth mechanisms that don't use a local username-and-password form. slay.

✨ authenticate_p($username, $password, $extra_data_hashref)

Same as above, but instead of returning a value, it returns a promise of same. And yes it's available even if you only gave it synchronous callbacks β€” those get "promisified" for you. iconic.

🌈 is_user_authenticated

Returns true if current_user() returns some valid object, false otherwise. that's it. that's the method.

πŸŽ€ is_user_authenticated_p

As above, but instead of returning a value, returns a promise of same.

πŸ’œ current_user

Returns the user object exactly as it was returned from the load_user subroutine ref you supplied.

You can change the current user by passing one in, but be careful: this bypasses the authentication. It's useful when you have multiple ways to authenticate users and you wanna re-use authorization checks that use current_user.

Also note: the name of this helper can be changed with the current_user_fn field during initialisation (peep below πŸ‘€).

🧚 current_user_p

As above, but instead of returning a value, returns a promise of same.

🌸 reload_user

Flushes the current user object and then returns current_user(). fresh start energy.

πŸ“ reload_user_p

As above, but instead of returning a value, returns a promise of same.

⭐ signature_exists

Returns true if a uid signature exists on the client side (in cookies), false otherwise.

⚠️ Warning: non-secure check! Only use this method for a "fast & dirty" lookup to see if the client has the proper cookies. It may be helpful in some cases β€” for example, counting "guest"/"logged users", or grabbing additional non-confidential information for "logged users" but not for "guest". Do not treat this as a real auth check, bestie. πŸ’€

πŸ‘‘ logout

Removes the session data for authentication, and effectively logs a user out. Returns a true value, so you can chain it. byeeeee πŸ¦‹


πŸŽ€ CONFIGURATION πŸŽ€

These are the options you can set for the plugin. (but the ones marked REQUIRED can be swapped out for a promise-returning equivalent by appending _p to the key ✨):

  • load_user (REQUIRED) β€” a coderef for user loading (see "USER LOADING")
  • validate_user (REQUIRED) β€” a coderef for user validation (see "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 only be loaded if it's used. May reduce site latency in some cases.
  • current_user_fn (optional) β€” sets the name for the current_user() helper function. _p gets appended for the asynchronous version.
  • fail_render (optional) β€” specify what is to be rendered when the authenticated condition is not met.

Set it to a coderef, which gets called with this signature:

sub {
    my ($routes, $controller, $captures, $required) = @_;
    ...
    return $hashref;
}

The return value of the subroutine gets ignored if it evaluates to false. If it returns a hash reference, that gets dereferenced and passed as-is to the controller's render function. If you return anything else… you are going to have a bad time. 😭

If you set it directly to a hash reference instead, that gets passed to render.

Wanna set the session expiry time? Do this in your startup routine:

$app->plugin('authentication', { ... });
$app->sessions->default_expiration(86400); # set expiry to 1 day
$app->sessions->default_expiration(3600); # set expiry to 1 hour

🌈 USER LOADING 🌈

The coderef you pass to the load_user configuration key has this 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 (and it can be a hashref, an arrayref, or a blessed object β€” we're flexible like that πŸ’…) or undef. no in-between, no vibes-based returns.


πŸ’– USER VALIDATION πŸ’–

User validation is what happens when we need to authenticate someone. The coderef you pass to the validate_user configuration key has this 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, because the behaviour of this plugin could get a little bit on the odd side of weird if you do that. πŸ™ˆ


✨ EXAMPLES ✨

For a code example using this, check out the t/01-functional.t and t/02-functional_lazy.t tests β€” they use Mojolicious::Lite and this plugin. go read them, they're right there. πŸ‘‘


πŸ¦‹ ROUTING VIA CONDITION πŸ¦‹

This plugin also exports a routing condition you can use to limit access to certain documents to only authenticated users:

$r->route('/foo')->requires(authenticated => 1)->to('mycontroller#foo');

my $authenticated_only = $r->route('/members')
    ->requires(authenticated => 1)
    ->to('members#index');

$authenticated_only->route('online')->to('members#online');

If someone isn't authenticated, these routes won't even be considered by the dispatcher, and unless you've set up a catch-all route, a 404 Not Found gets generated instead.

And here's another condition, for fast and unsecured checking of users who have a signature (without validating it). This method just checks client cookies for uid data existing:

$r->route('/foo')->requires(signed => 1)->to('mycontroller#foo');

This behavior is similar to the authenticated condition.

⭐ Prior to Mojolicious 9, use over instead of requires.

🧚 ROUTING VIA CALLBACK 🧚

If you wanna be able to send people to a login page, you'll have to do this:

my $members_only = $r->route('/members')->to(cb => sub {
    my $self = shift;

    $self->redirect_to('/login') and return 0
        unless($self->is_user_authenticated);

    return 1;
});

$members_only->route('online')->to('members#online');

Lazy and unsecured methods:

my $members_only = $r->route('/unimportant')->to(cb => sub {
    my $self = shift;

    $self->redirect_to('/login') and return 0
        unless($self->signature_exists);

    return 1;
});

$members_only->route('pages')->to('unimportant#pages');

πŸ’œ ROUTING VIA BRIDGE πŸ’œ

Same goal, different shape β€” if you wanna send people to a login page, you'll have to use this:

my $auth_bridge = $r->under('/members')->to('auth#check');
# only visible to logged in users
$auth_bridge->route('/list')->to('members#list');

And in your Auth controller you'd put:

sub check {
    my $self = shift;

    $self->redirect_to('/login') and return 0
        unless($self->is_user_authenticated);

    return 1;
};

Lazy and unsecured methods:

sub check {
    my $self = shift;

    $self->redirect_to('/login') and return 0
        unless($self->signature_exists);

    return 1;
};

🌟 SEE ALSO 🌟


🫢 AUTHOR 🫢

  • Ben van Staveren, "<madcat at cpan.org>"
  • JosΓ© JoaquΓ­n Atria, "<jjatria@cpan.org>"

πŸ› BUGS / CONTRIBUTING πŸ›

Please report any bugs or feature requests through the web interface at <https://github.com/benvanstaveren/mojolicious-plugin-authentication/issues>.


πŸ’Œ SUPPORT πŸ’Œ

You can find documentation for this module with the perldoc command:

perldoc Mojolicious::Plugin::Authentication

You can also look for information at:

  • AnnoCPAN: Annotated CPAN documentation

<http://annocpan.org/dist/Mojolicious-Plugin-Authentication>

  • CPAN Ratings

<http://cpanratings.perl.org/d/Mojolicious-Plugin-Authentication>

  • Search CPAN

<http://search.cpan.org/dist/Mojolicious-Plugin-Authentication/>

πŸ’– ACKNOWLEDGEMENTS πŸ’–

Okay bestie, this is the shoutout section and honestly? Deserved. Every single one of these icons helped make this module what it is, so let's give them their flowers fr fr πŸŒΈπŸ‘‘

  • Andrew Parker β€” for pointing out some bugs that crept in; a silent reminder not to code while sleepy πŸ˜΄πŸ’€ (real, we've all been there)
  • Mirko Westermeier (memowe) β€” for doing some (much needed) code cleanup 🧹✨ (much needed, she said, and she meant it)
  • Terrence Brannon (metaperl) β€” documentation patches πŸ“
  • Karpich Dmitry (meettya) β€” the "lazy_mode" and "signature_exists" functionality, including a test and documentation πŸ”₯ (shipped with tests AND docs, that's main character energy)
  • Ivo Welch β€” for donating his first ever Mojolicious application that shows an example of how to use this module 🎁 (donating your first ever app?? that's so sweet I can't)
  • Ed Wildgoose (ewildgoose) β€” adding the "current_user()" functionality, as well as some method renaming to make things a bit more sane πŸ§ πŸ’… (sanity is a group effort)
  • Colin Cyr (SailingYYC) β€” for reporting an issue with routing conditions; I really should not code while sleepy, brainfarts imminent! πŸ’€πŸ˜­ (the sleepy coding saga continues, iconic honestly)
  • Carlos Ramos (carragom) β€” for fixing the bug that'd consider an uid of 0 or "0" to be a problem πŸ›πŸ”¨ (yes, 0 is a real uid, leave her alone)
  • Doug Bell (preaction) β€” for improving the Travis CI integration and enabling arguments for current_user πŸ€–βœ¨
  • Roman F (moltar) β€” for fixing some pesky typos in sample code πŸ” (the typos were pesky, someone had to do it)
  • Hernan Lopes (hernan604) β€” for updating some deprecated method names in the documentation πŸ“š (deprecated names in docs?? yeah that had to go)

Okay legal time, bestie, don't scroll past this one 🫢

Copyright 2011-2021 Ben van Staveren.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.


And that's the whole page, bestie!! πŸŽ€βœ¨ You made it to the end, slay πŸ’… This version came from perl v5.34.0, dated 2022-06-09, straight out of the Mojoli...ication(3pm) manpage β€” go forth and authenticate responsibly πŸ¦‹πŸŒˆ

πŸ“¦ where this page comes from