Auth-GoogleAuthenticator
view release on metacpan or search on metacpan
lib/Dancer/Auth/GoogleAuthenticator.pm view on Meta::CPAN
#(twofactor_active => session('twofactor')),
};
};
# Force authentication for all non-index pages
hook before => sub {
return
if request->path_info =~ m{^/(css|javascripts|400|500|favicon.ico|$)};
if (! session('user') && request->path_info !~ m{^/auth/login}) {
# We store the redirect target internally
# and give the user
# not an URL but an internal session as the redirect target
var requested_path => request->path_info;
request->path_info('/auth/login');
redirect '/auth/login';
}
};
post '/auth/setup' => sub {
my ($user) = session->{user};
my $action= params->{action} || '';
my $enable= $action eq 'regenerate';
my $have_otp= $users{ $user }->{otp_secret};
if( 'deactivate' eq $action ) {
warning "Erasing OTP secret for $user->{name}";
delete $user->{otp_secret};
} else {
warning "Generating random OTP secret for $user->{name}";
# Make up random OTP secret
# XX Should be configurable/callback
my @letters = ('a'..'z','0'..'9');
my $obj = Crypt::URandom::Token->new(
length => 10,
alphabet => \@letters,
);
$user->{otp_secret} = $obj->get;
};
redirect '/auth/setup';
};
get '/auth/setup' => sub {
my ($user) = session->{user};
my ($otp_secret) = $user->{otp_secret};
my $auth = get_otp_auth( $user );
warning "Have auth for $user->{name}"
if $auth;
# Display otp_secret if we have it
# XX Maybe this should be over SSL only
template 'setup_twofactor', {
auth => $auth,
user => $user,
otp_secret => $otp_secret
};
};
get '/auth/setup/qrcode.png' => sub {
my ($user) = session->{user};
my ($otp_secret) = $user->{otp_secret};
if( $otp_secret ) {
my $auth = get_otp_auth( $user );
if( $auth ) {
warning $auth->registration_url($user->{name});
my $qr = $auth->registration_qr_code( "$user->{name}" );
return send_file( \$qr, content_type => 'image/png' );
} else {
warning "No auth despite user?!";
};
} else {
warning "No OTP set up for '$user->{name}'";
};
};
get '/auth/login' => sub {
my $return = vars->{requested_path} || '';
# XX Should only store relative URLs here, or at least
# only site-local URLs
session->{return_url} = $return;
template 'login';
};
# Maybe also have "requires('password')"
# "requires('twofactor')"
post '/auth/login' => sub {
my ($user_id,$pass,$otp) = (params->{user}, params->{pass}, params->{otp});
my $return = vars->{requested_path} || session->{return_url} || '';
my $user= $users{ $user_id };
if( $user and $user->{pass}
and $pass and $pass eq $user->{pass} ) {
my $auth_twofactor= get_otp_auth( $user );
if( $auth_twofactor ) {
# Check OTP in addition to password
if( $auth_twofactor->verify( $otp )) {
session 'user' => $user;
session 'twofactor' => 1;
flash success => "User logged in with two-factor auth";
warning "User '$user_id' logged in with two-factor auth";
} else {
# Log the failure
# Increment a failure counter for that user
# Increment a failure counter for that IP
# Increment a failure counter overall, to detect clock skew
warning "Wrong OTP for user '$user_id'";
flash error => "User unknown or wrong password/OTP";
};
} else {
warning "User '$user_id' logged in with password";
session 'user' => $user;
session 'twofactor' => 0;
flash success => "User logged in with password auth";
};
} else {
warning "Wrong password for user '$user_id'";
flash error => "User unknown or wrong password/OTP";
};
return redirect $return
if( session('user') and $return );
template 'login';
};
get '/auth/logout' => sub {
session->destroy; # boom
( run in 1.244 second using v1.01-cache-2.11-cpan-788537b7465 )