Alternative OPAC logins RFC
Alternative OPAC logins
Status: | unknown | |
Sponsored by: | ||
Developed by: | ||
Expected for: | ||
Bug number: | Bug | |
Work in progress repository: | ||
Description: | Could it be possible to let patrons login to the OPAC with other credentials than the Username/password provided by Koha itself? OAuth and OpenID look like obvious candidates - what are their pros and cons, and are there other/better alternatives? |
This is more of an attempt at getting a little brainstorming going, rather than the description of any specific plans for a new development.
The problem: Patrons probably have too many usernames/passwords already, resulting in them forgetting them and/or re-using credentials from other sites, resulting in reduced security. Could we let users use credentials from outside Koha to log in?
Creating a website that uses OAuth/OpenID/etc for logging in is probably not rocket science. An added challenge for Koha will be connecting external credentials to an actual/physical borrower.
Technical alternatives
OAuth
- OAuth Community Site
- Code: The Code page mentions Net::OAuth
- Code: The Code page mentions Net::OAuth
Oauth2 (Google)
A basic oauth2 login for google is currently being worked on by Perth Bible College. If the email address associated with the google account is registered to a user in Koha, then that user is automatically logged in.
Developed by Nicholas van Oudtshoorn // Perth Bible College
This is a work in progress - though it is used in production on the pbc opac. See also Bug 10988.
Instructions for setting up OAuth2 (Google) using a patch file
These instructions were written for Koha 3.14.03; but should be easily translatable to other versions. But your mileage might vary! These instructions are based on the patches in [1].
Step 1: Patch your installation
Change to your koha directory. This is usually
/usr/share/koha
Download the installedinstance patch file from bug 10988:
wget "http://bugs.koha-community.org/bugzilla3/attachment.cgi?id=25570" -O googleoauth2_installedinstance.patch
Then run it:
patch -p1 < googleoauth2_installedinstance.patch
Step 2: Update your Koha database
Run the following SQL to update the Koha database. The easiest way to do this is by using phpMyAdmin, and selecting the Koha Database, then clicking on the SQL tab. Paste in and execute the following:
INSERT INTO `systempreferences` (`variable`, `value`, `options`, `explanation`, `type`) VALUES ('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'), ('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'), ('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'), ('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain. Leave blank for all google domains', 'Free');
Alternatively, you can run the
mysql -uKOHAUSER -pKOHAPASSWORD
command.
Then type
USE KOHADATABASENAME
then paste the following:
INSERT INTO `systempreferences` (`variable`, `value`, `options`, `explanation`, `type`) VALUES ('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'), ('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'), ('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'), ('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain. Leave blank for all google domains', 'Free');
Type
exit
.
Step 3: Restart your web server
As you normally would!
Step 4: Set up OAuth2 with Google
Create a web app in the google cloud console:
Go to https://cloud.google.com/console
Create a project, and give it some details
Open the Project by clicking on it
Under APIs & auth menu, open "Registered Apps" and click "Register App"
Give it a name and make sure you select "Web app", click ok
Under OAuth 2.0 Client ID:
under web origin, type <your_opac_address> in the redirect uri
enter <your_opac_address>/cgi-bin/koha/svc/googleoauth2
click Generate
Step 5: Adjust Koha OAuth2 Preferences
Set the GoogleOAuth2ClientID and GoogleOAuth2ClientSecret according to the values generated in step 4. If you want to restrict OAuth2 logins to a specifc domain, you can enter it into the GoogleOAuth2Domain preference.
Step 6: Enjoy!
If you can't use the patch file, follow these instructions instead of step 1
Patch C4/Auth.pm
Edit the file lib/C4/Auth.pm Change line 37 from
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout);
to
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $googleoauth2 $ldap $cas $caslogout);
at about line 54, replace
%EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); $ldap = C4::Context->config('useldapserver') || 0; $cas = C4::Context->preference('casAuthentication');
with
%EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); $googleoauth2= C4::Context->preference('GoogleOAuth2'); $ldap = C4::Context->config('useldapserver') || 0; $cas = C4::Context->preference('casAuthentication');
At about line 396, replace
BranchesLoop => GetBranchesLoop($opac_name), BranchCategoriesLoop => GetBranchCategories( 'searchdomain', 1, $opac_name ), CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1, LibraryName => "" . C4::Context->preference("LibraryName"), LibraryNameTitle => "" . $LibraryNameTitle,
with
BranchesLoop => GetBranchesLoop($opac_name), BranchCategoriesLoop => GetBranchCategories( 'searchdomain', 1, $opac_name ), CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1, GoogleOAuth2 => (C4::Context->preference("GoogleOAuth2")), LibraryName => "" . C4::Context->preference("LibraryName"), LibraryNameTitle => "" . $LibraryNameTitle,
At about line 1106, just before the code that begins:
my $self_url = $query->url( -absolute => 1 ); $template->param( url => $self_url,
insert the following
if ($googleoauth2) { if ($query->param("OAuth2Failed")) { my $reason = $query->param('OAuth2Failed'); $template->param(invalidOAuth2Login => $reason); } }
Update the Koha database to include the new system preferences
Run the following SQL to update the Koha database. You can do this either by using the
mysql -uKOHAUSER -pKOHAPASSWORD
command, or else from within something like phpMyAdmin.
INSERT INTO `systempreferences` (`variable`, `value`, `options`, `explanation`, `type`) VALUES ('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'), ('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'), ('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'), ('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain. Leave blank for all google domains', 'Free');
Edit the file
prog/en/modules/admin/preferences/admin.pref
At the end of the file, copy the following:
Google OAuth2: - - pref: GoogleOAuth2 choices: yes: Use no: "Don't Use" - Google OAuth2 login. - You will need to select OAuth2 when creating an app in the google cloud console, and set the web origin to your_opac_url and the redirect url to your_opac_url/cgi-bin/koha/svc/oauthlogin . - - Google OAuth2 Client ID - pref: GoogleOAuth2ClientID - - Google OAuth2 Client Secret - pref: GoogleOAuth2ClientSecret - - Google OAuth2 Restrict to domain - pref: GoogleOAuth2Domain
Install the actual code to do OAuth2 (Google) authentication
Create a new file called
opac-tmpl/svc/googleoauth2
with the following content:
#!/bin/perl -w
# Copyright vanoudt@gmail.com 2014
# Based on persona code from chris@bigballofwax.co.nz 2013
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# Basic OAuth2 authentication for google goes like this
# First: get your clientid, clientsecret from google. At this stage, tell google that
# your redirect url is /cgi-bin/koha/svc/oauthlogin
#
# The first thing that happens when this script is called is that one gets redirected
# to an authentication url from google
#
# If successful, that then redirects back to this script, setting a CODE parameter
# which we use to look up a json authentication token. This token includes an encrypted
# json id_token, which we round-trip back to google to decrypt. Finally, we can extract
# the email address from this.
#
# There is some room for improvement here.
# In particular, Google recommends verifying and decrypting the id_token locally, which means caching
# some information and updating it daily. But that would make things a lot faster
use strict;
use warnings;
use CGI qw/escape/;
use C4::Auth;
use C4::Context;
use C4::Output;
use LWP::UserAgent;
use HTTP::Request::Common qw{ POST };
use JSON;
my $scope="openid email";
my $host = C4::Context->preference('OPACBaseURL');
my $restricttodomain = C4::Context->preference('GoogleOAuth2Domain');
my $redirecturl = 'http://' . $host . '/cgi-bin/koha/svc/googleoauth2';
my $issuer = 'accounts.google.com';
my $clientid = C4::Context->preference('GoogleOAuth2ClientID');
my $clientsecret = C4::Context->preference('GoogleOAuth2ClientSecret');
my $query = new CGI;
sub loginfailed {
my $query = shift;
my $reason = shift;
$query->delete('code');
$query->param('OAuth2Failed'=>$reason);
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
{
template_name => 'opac-user.tmpl',
query => $query,
type => 'opac',
authnotrequired => 0,
flagsrequired => { borrow => 1 },
}
);
$template->param( 'invalidOAuth2Login' => $reason );
$template->param( 'loginprompt' => 1);
output_html_with_http_headers $query, $cookie, $template->output;
}
#die $query->param('code');
if (defined $query->param('error')) {
loginfailed($query, 'An authentication error occurred. (Error:' . $query->param('error') . ')');
} elsif (defined $query->param('code')) {
my $code=$query->param('code');
my $ua = LWP::UserAgent->new();
my $request = POST('https://accounts.google.com/o/oauth2/token', [
code => $code,
client_id => $clientid,
client_secret => $clientsecret,
redirect_uri => $redirecturl,
grant_type => 'authorization_code',
$scope => $scope
]);
my $response = $ua->request($request)->decoded_content ;
my $json = decode_json ( $response);
if ( exists($json->{'id_token'}) ) {
$request = POST('https://www.googleapis.com/oauth2/v1/tokeninfo', [
id_token => $json->{'id_token'}
]);
$response = $ua->request($request)->decoded_content;
$json = decode_json ( $response);
# Confirm (as google suggests) that the issuer and audience are what we expect them to be
if (($json->{'issuer'} eq $issuer) && ($json->{'audience'} eq $clientid) && exists($json->{'email'})) {
my $email = $json->{'email'};
my ( $userid, $cookie, $sessionID ) =
checkauth( $query, 1, { borrow => 1 }, 'opac', $email );
if ($userid) { # A valid user has logged in
print $query->redirect( -uri => '/cgi-bin/koha/opac-user.pl', -cookie => $cookie );
} else {
loginfailed($query, 'The email address you are trying to use is not associated with a borrower in this library.');
}
} else { # something went wrong with getting appropriate credentials
loginfailed($query, 'Failed to get proper credentials from google.');
}
} else { # Failed to get ID Token
loginfailed($query, 'An authentication error occurred. (Error: No ID Token was supplied)');
}
} else {
my $prompt = '';
$prompt = $query->param('reauthenticate') unless not (defined $query->param('reauthenticate'));
my $authorisationurl = 'https://accounts.google.com/o/oauth2/auth?' .
'response_type=code&' .
'redirect_uri=' . escape ( $redirecturl ) . '&' .
'client_id=' . escape ( $clientid ) . '&' .
'scope=' . escape ( $scope ) . '&';
if ( $restricttodomain ne '' ) {
$authorisationurl .= 'hd=' . $restricttodomain . '&';
}
$authorisationurl .= 'prompt=' . escape ( $query->param('reauthenticate') );
print $query->redirect($authorisationurl);
}
Change your templates to log in using OAuth2 (Google)
The following instructions are for the Bootstrap theme. If you are using prog or ccsr, simply change all references to
/cgi-bin/koha/opac-user.pl
with
/cgi-bin/koha/svc/googleoauth2
Edit the file
includes/masthead.inc
, from about line 58, changing
<li role="presentation"><a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a></li> [% END %] [% ELSE %] <li role="presentation"><a href="/cgi-bin/koha/opac-user.pl" tabindex="-1" class="menu-inactive loginModal-trigger" role="menuitem">Log in to create your own lists</a></li> [% END # / IF loggedinusername %] [% END # / IF opacuserlogin %]
to
<li role="presentation"><a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a></li> [% END %] [% ELSE %] [% IF ( GoogleOAuth2 ) %] <li role="presentation"><a href="/cgi-bin/koha/svc/googleoauth2" tabindex="-1" class="menu-inactive" role="menuitem">Log in to create your own lists</a></li> [% ELSE %] <li role="presentation"><a href="/cgi-bin/koha/opac-user.pl" tabindex="-1" class="menu-inactive loginModal-trigger" role="menuitem">Log in to create your own lists</a></li> [% END %] <li role="presentation"><a href="/cgi-bin/koha/opac-user.pl" tabindex="-1" class="menu-inactive loginModal-trigger" role="menuitem">Log in to create your own lists</a></li> [% END # / IF loggedinusername %] [% END # / IF opacuserlogin %]
Edit the file
includes/masthead.inc
, from about line 71, changing
<div id="members"> <ul class="nav pull-right"> [% UNLESS ( loggedinusername ) %] <li><a href="/cgi-bin/koha/opac-user.pl" class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a></li> [% END %] [% IF ( loggedinusername ) %] <li><p class="members navbar-text">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></p></li>
to
<div id="members"> <ul class="nav pull-right"> [% UNLESS ( loggedinusername ) %] [% IF ( GoogleOAuth2 ) %] <li><a href="/cgi-bin/koha/svc/googleoauth2" role="button" data-toggle="modal">Log in to your account</a></li> [% ELSE %] <li><a href="/cgi-bin/koha/opac-user.pl" class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a></li> [% END %] [% END %] [% IF ( loggedinusername ) %] <li><p class="members navbar-text">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></p></li>
Edit the file
modules/opac-auth.tt
, from about line 84, changing
[% END # / IF casAuthentication %] <form action="[% url %]" name="auth" id="auth" method="post"> <input type="hidden" name="koha_login_context" value="opac" /> <fieldset class="brief">
to
[% END # / IF casAuthentication %] [% IF ( invalidOAuth2Login ) %] <h4>Automatic Login</h4> <p>Sorry, your automatic login failed. <span class="error">[% invalidOAuth2Login %]</span></p> <p>Please note that automatic login will only work if you are using the email address registered with this library.</p> <p>If you want to, you can try to <a href="/cgi-bin/koha/svc/googleoauth2?reauthenticate=select_account">login using a differen account</a> <h4>Local login</h4> <p>If you can't login automatically, you can still login in manually: </p> [% END %] <form action="[% url %]" name="auth" id="auth" method="post"> <input type="hidden" name="koha_login_context" value="opac" /> <fieldset class="brief">
Useful Userscripts for OAuth2 (Google) on public terminals
When using Google OAuth2 in a public computer, there are some nice javascripts that can be injected to make this a whole lot more secure. Basically, the following userscripts disable the google account chooser, and log a person out of their google accounts when they log out of Koha. This is not very useful or polite outside of a public terminal, hence my thinking to do it with userscripts.
Just putting it here as a reference for anyone thinking to use this.
-------------------------------------------------------------------------------- // ==UserScript== // @id accounts.google.com-forcenocookie@scriptish // @name Disable persistent google login // @version 1.0 // @namespace // @author // @description // @include https://accounts.google.com/* // @run-at document-end // ==/UserScript== document.getElementsByName("PersistentCookie")[0].value='no';
-------------------------------------------------------------------------------- // ==UserScript== // @id koha-full-oauth2-logout@scriptish // @name Force Full Logout // @version 1.0 // @namespace // @author // @description // @include http://OPACURLHERE/* // @run-at document-end // ==/UserScript== document.getElementById('logoutmenu').href="https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue="+document.getElementById('logoutmenu').href; document.getElementById('logout').href="https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue="+document.getElementById('logoutmenu').href;
OpenID
- Bug 3237 - OpenID patron authentication
- OpenID Foundation website
- Get an OpenID - list of OpenID providers
- Code: Perl libraries
WedID
Connecting external credentials to an actual/physical borrower
How do we know that a user logging in with /OpenID/OAuth/etc should be given access to any given account in Koha? Do they have to turn up at the library and tell us their OpenID? Are there other ways?
Inspiration
- Thomas Brevik: The library card is a barrier to library use