ETOOBUSY 🚀 minimal blogging for the impatient
Mojolicious::Plugin::Authentication example with under
TL;DR
An extension to the example of using Mojolicious::Plugin::Authentication.
In previous post Mojolicious::Plugin::Authentication example we took a look at an example for Mojolicious::Plugin::Authentication. I mean, it’s in the title - no big deal.
The example suggests that for every method that we want to put behind the authentication check we should do the following:
get '/private' => sub ($c) {
return $c->redirect_to('/login') unless $c->is_user_authenticated;
return $c->render(template => 'private');
};
i.e. do the check with is_user_authenticated()
and act accordingly
(e.g. by redirecting to the /login
page).
As you might have already thought at this point, this is both verbose and error prone. What if we forget to add that check?
Using a condition
If you just don’t like the verbosity and you’re fine with getting a
Not Found
error when the user tries to access a private page (i.e.
you don’t want to redirect to the /login
page), you can just use a
condition:
get '/private' => (authenticated => 1 ) => sub ($c) {
return $c->render(template => 'private');
};
From the application’s point of view, this route does not even exist
if the authentication condition is not fulfilled. For this reason, the
/private
endpoint will yield a Not Found
error to unauthenticated
users.
This still does not solve our problem with being error-prone though - we might forget to put the conditions. Or we might be lazy enough to not want to repeat it for all private endpoints.
Using under
It is possible to use under
for fun and profit to install an
intermediate checkpoint and then place all routes in a sub-tree.
Here is an example:
under '/authenticated' => sub ($c) {
return 1 if $c->is_user_authenticated;
$c->redirect_to('/login');
return 0;
};
get what => sub ($c) {
return $c->render(template => 'private', page => 'authenticated/what');
};
get ever => sub ($c) {
return $c->render(template => 'private', page => 'authenticated/ever');
};
The intermediate stop is at /authenticated
. Here, we check with
is_user_authenticated()
and return 1
if we are fine - this signals
to the system that we passed this intermediate stop and we can move
further in the route selection. Otherwise, we set the redirection to the
login page and return 0
, to signal that the route analysis should stop
here.
In [Mojolicious::Lite][] applications, all routes that come after the
under
are assumed to be… under it. So the get what => ...
route
is actually translated into a GET to /authenticated/what
and so on.
The route analysis will arrive here only if the intermediate stop above
was successful, so all routes after the under
are safe and will
trigger a redirection to the /login
page if the user is not
authenticated.
There is still a slight space for some information leak anyway. Trying
to hit a non-existent route (like /authenticated/whatever
) would not
yield a redirection to the /login
page, but a Not Found
error. This
somehow exposes our endpoints, and we might want to keep them…
private.
If this is a concern, you can add the following final route:
get '*' => sub ($c) { return $c->render(status => 404) };
In this way we are putting a catchall that will eventually complain with
a Not Found
error, but this time only to authenticated users.
The complete example
I’m planning to keep the complete example and possibly expand it if needed, here’s the current status: