TL;DR

A full example of using Mojolicious::Plugin::Authentication.

In previous post Mojolicious::Plugin::Authentication we took a look at… Mojolicious::Plugin::Authentication, a useful plugin for Mojolicious that can help us with managing authentication.

Here we take a look at a minimal and simplistic example for using it.

#!/usr/bin/env perl
use Mojolicious::Lite -signatures;

{
   my %db = (
      foo => {pass => 'FOO', name => 'Foo De Pois'},
      bar => {pass => 'BAZ', name => 'Bar Auangle'},
   );
   sub load_account ($u) { return $db{$u} // undef }
   sub validate ($u, $p) {
      warn "user<$u> pass<$p>\n";
      my $account = load_account($u) or return;
      return $account->{pass} eq $p;
   }
}

app->plugin(
   Authentication => {
      load_user     => sub ($app, $uid) { load_account($uid) },
      validate_user => sub ($c, $u, $p, $e) { validate($u, $p) ? $u : () },
   }
);

app->hook(
   before_render => sub ($c, $args) {
      my $user = $c->is_user_authenticated ? $c->current_user : undef;
      $c->stash(user => $user);
      return $c;
   }
);

get '/' => sub ($c) { $c->render(template => 'index') };

get '/login' => sub ($c) { $c->render(template => 'login') };

post '/login' => sub ($c) {
    my $username = $c->param('username');
    my $password = $c->param('password');
    if ($c->authenticate($username, $password)) {
       warn $c->is_user_authenticated ? 'YES' : 'NOT YET';
        $c->redirect_to('/private');
    }
    else {
        $c->redirect_to('login');
    }
    return;
};

get '/private' => sub ($c) {
    return $c->redirect_to('/login') unless $c->is_user_authenticated;
    return $c->render(template => 'private');
};

post '/logout' => sub ($c) {
    $c->logout if $c->is_user_authenticated;
    return $c->redirect_to('/');
};

app->start;

__DATA__
@@ layouts/layout.html.ep
<!DOCTYPE html>
<html lang="en">
  <head><title>Whatevah</title></head>
  <body>
    <%= content %>
    <hr>
%= t a => href => '/' => 'Home';
-
%= t a => href => '/login' => 'Login';
-
%= t a => href => '/private' => 'Private';
% if (defined $user) {
<form action="/logout" method="post"
   style="display: inline"
>
-
<button type="submit"
   style="background: none!important;
          border: none;
          padding: 0!important;
          text-decoration: underline;
          cursor: pointer;
          color: #069;">Logout <%= $user->{name} %></button>
</form>
% }
  </body>
</html>

@@ index.html.ep
% layout 'layout';
%= t h1 => 'Index - Free Access'

@@ private.html.ep
% layout 'layout';
%= t h1 => 'Private Stuff - Retricted Access'
Welcome <%= $user->{name} %>

@@ login.html.ep
% layout 'layout';
%= t h1 => 'login'
%= form_for '/login' => (method => 'post') => begin
username: <%= text_field 'username' %>
password: <%= password_field 'password' %>
%= submit_button 'log in' 
%= end

The example should not require much explanation, as it leverages on the concepts and functions described in the previous post.

One thing that can be elaborated a bit is this part here:

app->hook(
   before_render => sub ($c, $args) {
      my $user = $c->is_user_authenticated ? $c->current_user : undef;
      $c->stash(user => $user);
      return $c;
   }
);

In practice, we are always setting a user key in the stash, putting the information about the current user (via current_user()) if available, or nothing (undef) otherwise. In this way, our templates will have the data structure for the account at their disposal, e.g. to show the user’s name etc.