As of version 0.06, this module is closed-source, following a delayed release scheme. OAuth authentication via Facebook, Google (OAuth2.0) and Twitter (OAuth1.0a) is supported since version 0.06. So the bad news is, that the tagline here on github is a bit over advertising what's puplicly available. The good news is, you can get a copy of the updated version with all the features. Simply contact me here.
Dancer::Plugin::Users - Adds user logic, routines and OpenID auth to Dancer apps
use Dancer::Plugin::Users;
This module adds one flavour of basic logic and routes it needs to enable users of your web application to register, log in and out and end their association.
As bonus, it also offers OpenID logins! Yay!
For all the Why & How, you might also want to check out the thread of the original pull-request this module is based on, in addition to the "background" section further down.
The plugin assumes that Dancer::Plugin::Database is in use and some other plugin is offering a session() store. Upon app startup, Dancer::Plugin::Users will connect to database table 'users', which is created if it doesn't exist.
In terms of password storage, we are on the safe side by relying on Dancer::Plugin::Passphrase which employs best practice by hashing and per-user-salting passwords, among others. When we process registrations, this module here also uses a simple regex to blacklist very common passwords but that is only considered as being a minimal fail-safe default. See the "Excourse" section below for some musings on how to improve that.
The module adds six routes to your application, hardcoded. Although you can change their URLs from the config files:
/login
/openid_login
/logout
/register
/openid_register
/end_membership
Each route relies on a template of the same name. The examples folder in this package should have some basic templates for you to start from.
In config files, you can set these variables:
plugins:
Users:
route_login: "/login"
route_openid_login: "/openid_login"
route_logout: "/logout"
route_register: "/register"
route_openid_register: "/openid_register"
route_end_membership: "/end_membership"
after_login: "sub { return '/user/'. $_[0]->{id}; }"
reserved_logins: "admin root superuser demo Anonymous test" # space separated, case-insensitive
reserved_passwords: undef # space separated, case-insensitive, appended to internal regex
db_table: "users"
The example also shows the defaults, so all this can be left out if these are okay for you.
The variable "after_login" is special in such regard that if it is set in one of the config files, the string is eval()'ed upon module-load to replace the default internal anon-code-ref pictured above. It is expected that it contains a sub that can process the passed $_[0], which is a hash-ref populated with at least user->{id} and $user->{nickname}.
In templates, for example:
<% IF session.user.id %>
Hello <a href="/user/<% session.user.id %>"><% session.user.nickname %></a> | <a href="/user/<% session.user.id %>">My profile</a> | <a href="/logout">logout</a>
<% ELSE %>
<a href="/login" rel="nofollow">login</a> . <a href="/register" rel="nofollow">register</a>
<% END %>
In your route's code:
# This is what's done after a sucessful login
session 'user' => {
id => $user->{id},
nickname => $user->{nickname},
openid => $verified_identity->{identity}, # only on openID logins
};
# add this somewhere to check if a visitor is logged-in
forward '/login' unless session('user');
I took inspiration from the audacity of Locale::Wolowitz to do something in 'yet another way'. The result is a wild blend of what would normally end up in more focused, separate modules, probably within the ::Auth or some user management namespace - only here, it's all packaged in one single (light) module that 'simply works'.
It's meant less to be a well thought out authentication module, or a complete user management add-on, and is far from being a framework. It's more a set of common practices, routines and tools I tend to throw-in when I build webapps that know about the concepts of "users" and "logins". As such, it doesn't come with ready-built templates, (as you'd have to customise them anyway), it makes a few assumtions about application flow and is not fully configurable. Sorry for that. It's just what looks sane to me. Anyway, I share it here as it may be of use for like-minded developers, and it's handy to have it up on GitHub. Yes, read that again, GitHub only for now, as reserving the Dancer::Plugin::Users namespace might be a bit too audacious.
Weak, common or colloquially 'bad' passwords are a security threat for your web applications. As such, this module currently requires passwords to be at least 6 characters long and matches against a regex with a very limited set of weak passwords. This can be considered as being an absolute minimum, but probably better than having no checks at all. And of course, these checks are only done within the local /register route, but many OpenId providers probably have a superior scheme implemented.
If you want to override this module's password checks (once the API to do that in a straightforward way materialises), here are some modules and things for you to consider:
You can impose different constraints upon passwords: require a minimum length, complexity or entropy, or do blacklisting (in whole or in parts) by using language dictionaries or (better) lists of passwords which are frequently used.
Some approaches are light on resources and/or easily cachable within your application, while others, usually more complete solutions, come at the cost of a certain IO and/or CPU overhead.
When using a dictionary, don't forget to add words that users usually see while they choose a password on your site/ registration page, like your webapp's name. Just look at lists of leaked passwords from major sites: top used passwords usually are or contain the site's name or other (trade)marks related to the respective service.
Data::Password::Check::JPassword provides a measurement of how strong a given password is - strings containing uppercase, lowercase, numbers, punctuation or other non-ascii stuff each increase the score.
Data::Password::Entropy calculates a similar score, but on a more abstract basis, taking order, distance of chars etc. into account.
Data::Password combines a number of constraints like length or order, and matches against spelling dictionaries usually available on *nix systems - albeit in simple 'loop-through-file'-manner which might contribute to the overall IO burden of your webapp.
Data::Password::BasicCheck looks for basics like minimum length and other more elaborate string permutations like roations, reorderings, etc. Well, and one other, less optimal thing, a 'maximum length', which might make some sense in... well, err, actually no sane scenario.
Data::Password::Common Dagolden's shot at it matches passwords against a (one) dictionary file. It comes bundled with over half a million passwords better to be rejected and relies on Search::Dict to loop in an optimized way though the dictionary (which has to be sorted for that to work, btw). Also, you can provide (roll) your own dictionary. A good (not language vocabulary only!) dictionary in combination with a minimum length constraint can be quite a complete solution.
Data::Password::Simple is a simple module that combines a minimum length constraint and a dictionary matcher that keeps the whole dictionary after module-load in memory as a hash. This module is probably quite exactly what you would 'reinvent' when you want to avoid disk access completely. But make sure you have that RAM to spare when your dict reaches a certain size.
Now it's on you to decide if you want to rely on clever calculations of a password's strength or on (in a reverse way) "brute-force" dictionary matching.
As I think the default configuration is the most practical, that's what I use and that's the proven one. As a result, changing settings is not that well tested.
The database currently holds only one OpenID peer. That might be a limitation if you want to offer your users multiple OpenID authorities to log into a single account on your webapp.
In case you found anything obvious to fix/change/improve, feel free to contribute on github.
For completeness, last time I looked, these authentication-related Dancer plugins were on CPAN: Dancer::Plugin::Auth::Extensible, Dancer::Plugin::Auth::RBAC, Dancer::Plugin::Auth::Twitter, Dancer::Plugin::Auth::Github, Dancer::Plugin::Authen::Simple, Dancer::Plugin::Facebook, Dancer::Auth::GoogleAuthenticator
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself