NAME
Yancy::Plugin::Auth::Password - A simple password-based auth
VERSION
version 1.080
SYNOPSIS
use Mojolicious::Lite;
plugin Yancy => {
backend => 'sqlite://myapp.db',
schema => {
users => {
'x-id-field' => 'username',
properties => {
username => { type => 'string' },
password => { type => 'string', format => 'password' },
},
},
},
};
app->yancy->plugin( 'Auth::Password' => {
schema => 'users',
username_field => 'username',
password_field => 'password',
password_digest => {
type => 'SHA-1',
},
} );
DESCRIPTION
Note: This module is EXPERIMENTAL
and its API may change before Yancy v2.000 is released.
This plugin provides a basic password-based authentication scheme for a site.
This module composes the Yancy::Auth::Plugin::Role::RequireUser role to provide the require_user authorization method.
Adding Users
To add the initial user (or any user), use the mojo eval
command:
./myapp.pl eval 'yancy->create( users => { username => "dbell", password => "123qwe" } )'
This plugin adds the "auth.digest" filter to the password
field, so the password passed to yancy->create
will be properly hashed in the database.
You can use this same technique to edit users from the command-line if someone gets locked out:
./myapp.pl eval 'yancy->update( users => "dbell", { password => "123qwe" } )'
Migrate from Auth::Basic
To migrate from the deprecated Yancy::Plugin::Auth::Basic module, you should set the migrate_digest
config setting to the password_digest
settings from your Auth::Basic configuration. If they are the same as your current password digest settings, you don't need to do anything at all.
# Migrate from Auth::Basic, which had SHA-1 passwords, to
# Auth::Password using SHA-256 passwords
app->yancy->plugin( 'Auth::Password' => {
schema => 'users',
username_field => 'username',
password_field => 'password',
migrate_digest => {
type => 'SHA-1',
},
password_digest => {
type => 'SHA-256',
},
} );
# Migrate from Auth::Basic, which had SHA-1 passwords, to
# Auth::Password using SHA-1 passwords
app->yancy->plugin( 'Auth::Password' => {
schema => 'users',
username_field => 'username',
password_field => 'password',
password_digest => {
type => 'SHA-1',
},
} );
Verifying Yancy Passwords in SQL (or other languages)
Passwords are stored as base64. The Perl Digest module's removes the trailing padding from a base64 string. This means that if you try to use another method to verify passwords, you must also remove any trailing =
from the base64 hash you generate.
Here are some examples for how to generate the same password hash in different databases/languages:
* Perl:
$ perl -MDigest -E'say Digest->new( "SHA-1" )->add( "password" )->b64digest'
W6ph5Mm5Pz8GgiULbPgzG37mj9g
* MySQL:
mysql> SELECT TRIM( TRAILING "=" FROM TO_BASE64( UNHEX( SHA1( "password" ) ) ) );
+--------------------------------------------------------------------+
| TRIM( TRAILING "=" FROM TO_BASE64( UNHEX( SHA1( "password" ) ) ) ) |
+--------------------------------------------------------------------+
| W6ph5Mm5Pz8GgiULbPgzG37mj9g |
+--------------------------------------------------------------------+
* Postgres:
yancy=# CREATE EXTENSION pgcrypto;
CREATE EXTENSION
yancy=# SELECT TRIM( TRAILING '=' FROM encode( digest( 'password', 'sha1' ), 'base64' ) );
rtrim
-----------------------------
W6ph5Mm5Pz8GgiULbPgzG37mj9g
(1 row)
CONFIGURATION
This plugin has the following configuration options.
schema
The name of the Yancy schema that holds users. Required.
username_field
The name of the field in the schema which is the user's identifier. This can be a user name, ID, or e-mail address, and is provided by the user during login.
This field is optional. If not specified, the schema's ID field will be used. For example, if the schema uses the username
field as a unique identifier, we don't need to provide a username_field
.
plugin Yancy => {
schema => {
users => {
'x-id-field' => 'username',
properties => {
username => { type => 'string' },
password => { type => 'string' },
},
},
},
};
app->yancy->plugin( 'Auth::Password' => {
schema => 'users',
password_digest => { type => 'SHA-1' },
} );
password_field
The name of the field to use for the password. Defaults to password
.
This field will automatically be set up to use the "auth.digest" filter to properly hash the password when updating it.
password_digest
This is the hashing mechanism that should be used for hashing passwords. This value should be a hash of digest configuration. The one required field is type
, and should be a type supported by the Digest module:
MD5 (part of core Perl)
SHA-1 (part of core Perl)
SHA-256 (part of core Perl)
SHA-512 (part of core Perl)
Bcrypt (recommended)
Additional fields are given as configuration to the Digest module. Not all Digest types require additional configuration.
There is no default: Perl core provides SHA-1 hashes, but those aren't good enough. We recommend installing Digest::Bcrypt for password hashing.
# Use Bcrypt for passwords
# Install the Digest::Bcrypt module first!
app->yancy->plugin( 'Auth::Basic' => {
password_digest => {
type => 'Bcrypt',
cost => 12,
salt => 'abcdefgh♥stuff',
},
} );
Digest information is stored with the password so that password digests can be updated transparently when necessary. Changing the digest configuration will result in a user's password being upgraded the next time they log in.
allow_register
If true, allow the visitor to register their own user account.
register_fields
An array of fields to show to the user when registering an account. By default, all required fields from the schema will be presented in the form to register.
Sessions
This module uses Mojolicious sessions to store the login information in a secure, signed cookie.
To configure the default expiration of a session, use Mojolicious::Sessions default_expiration.
use Mojolicious::Lite;
# Expire a session after 1 day of inactivity
app->sessions->default_expiration( 24 * 60 * 60 );
FILTERS
This module provides the following filters. See "Extended Field Configuration" in Yancy for how to use filters.
auth.digest
Run the field value through the configured password Digest object and store the Base64-encoded result instead.
HELPERS
This plugin has the following helpers.
yancy.auth.current_user
Get the current user from the session, if any. Returns undef
if no user was found in the session.
my $user = $c->yancy->auth->current_user
|| return $c->render( status => 401, text => 'Unauthorized' );
yancy.auth.require_user
Validate there is a logged-in user and optionally that the user data has certain values. See "require_user" in Yancy::Plugin::Auth::Role::RequireUser.
# Display the user dashboard, but only to logged-in users
my $auth_route = $app->routes->under( '/user', $app->yancy->auth->require_user );
$auth_route->get( '' )->to( 'user#dashboard' );
yancy.auth.login_form
Return an HTML string containing the rendered login form.
%# Display a login form to an unauthenticated visitor
% if ( !$c->yancy->auth->current_user ) {
%= $c->yancy->auth->login_form
% }
The login form will create a return_to
hidden field to try to bring the user back to what they were doing when they were asked to log in. This field defaults to the value of the return_to
parameter or the value of the return_to
flash value. If neither is set, it defaults to the HTTP referrer if the form is displayed on the login page (under the URL /yancy/auth/password
) or the current page.
# Redirect user to login page, and return them here
under sub( $c ) {
return 1 if $c->yancy->auth->current_user;
$c->flash({ return_to => $c->req->url });
$c->redirect_to('yancy.auth.password.login');
return undef;
}
# Build a link to log in and then redirect to the dashboard
$c->url_for( 'yancy.auth.password.login' )
->query({ return_to => $c->url_for( 'dashboard' ) });
yancy.auth.logout
Log out any current account. Use this in your own controller actions to perform a logout.
ROUTES
This plugin creates the following named routes. Use named routes with helpers like url_for, link_to, and form_for.
yancy.auth.password.login_form
Display the login form using the "yancy/auth/password/login_form.html.ep" template. This route handles GET
requests and can be used with the redirect_to, url_for, and link_to helpers.
%= link_to Login => 'yancy.auth.password.login_form'
<%= link_to 'yancy.auth.password.login_form', begin %>Login<% end %>
<p>Login here: <%= url_for 'yancy.auth.password.login_form' %></p>
yancy.auth.password.login
Handle login by checking the user's username and password. This route handles POST
requests and can be used with the url_for and form_for helpers.
%= form_for 'yancy.auth.password.login' => begin
%= text_field 'username', placeholder => 'Username'
%= text_field 'password', placeholder => 'Password'
%= submit_button
% end
yancy.auth.password.logout
Clear the current login and allow the user to log in again. This route handles GET
requests and can be used with the redirect_to, url_for, and link_to helpers.
%= link_to Logout => 'yancy.auth.password.logout'
<%= link_to 'yancy.auth.password.logout', begin %>Logout<% end %>
<p>Logout here: <%= url_for 'yancy.auth.password.logout' %></p>
yancy.auth.password.register_form
Display the form to register a new user, if registration is enabled. This route handles GET
requests and can be used with the redirect_to, url_for, and link_to helpers.
%= link_to Register => 'yancy.auth.password.register_form'
<%= link_to 'yancy.auth.password.register_form', begin %>Register<% end %>
<p>Register here: <%= url_for 'yancy.auth.password.register_form' %></p>
yancy.auth.password.register
Register a new user, if registration is enabled. This route handles POST
requests and can be used with the url_for and form_for helpers.
%= form_for 'yancy.auth.password.register' => begin
%= text_field 'username', placeholder => 'Username'
%= text_field 'password', placeholder => 'Password'
%= text_field 'password-verify', placeholder => 'Password (again)'
%# ... Display other fields required for registration
%= submit_button
% end
TEMPLATES
To override these templates, add your own at the designated path inside your app's templates/
directory.
yancy/auth/password/login_form.html.ep
The form to log in.
yancy/auth/password/login_page.html.ep
The page containing the form to log in. Uses the login_form.html.ep
template for the form itself.
yancy/auth/unauthorized.html.ep
This template displays an error message that the user is not authorized to view this page. This most-often appears when the user is not logged in.
yancy/auth/unauthorized.json.ep
This template renders a JSON object with an "errors" array explaining the error.
layouts/yancy/auth.html.ep
The layout that Yancy uses when displaying the login form, the unauthorized error message, and other auth-related pages.
yancy/auth/password/register.html.ep
The page containing the form to register a new user. Will display all of the "register_fields".
SEE ALSO
AUTHOR
Doug Bell <preaction@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2021 by Doug Bell.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.