Alternative OPAC logins RFC

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
=== 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 .
===== 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');
===== Tell Koha about the new system preferences related to OAuth2 =====
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
 <a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a>
 [% END %]
 [% ELSE %]
 <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>
 [% END # / IF loggedinusername %]
 [% END # / IF opacuserlogin %]
to
 <a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a>
 [% END %]
 [% ELSE %]
 [% IF ( GoogleOAuth2 ) %]
 <a href="/cgi-bin/koha/svc/googleoauth2" tabindex="-1" class="menu-inactive" role="menuitem">Log in to create your own lists</a>
 [% ELSE %]
 <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>
 [% END %]
 <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>
 [% END # / IF loggedinusername %]
 [% END # / IF opacuserlogin %]
Edit the file includes/masthead.inc, from about line 71, changing
 
 
 [% UNLESS ( loggedinusername ) %]
 <a href="/cgi-bin/koha/opac-user.pl" class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a>
 [% END %]
 [% IF ( loggedinusername ) %]
 Welcome, <a href="/cgi-bin/koha/opac-user.pl">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</a>
to
 
 
 [% UNLESS ( loggedinusername ) %]
 [% IF ( GoogleOAuth2 ) %]
 <a href="/cgi-bin/koha/svc/googleoauth2" role="button" data-toggle="modal">Log in to your account</a>
 [% ELSE %]
 <a href="/cgi-bin/koha/opac-user.pl" class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a>
 [% END %]
 [% END %]
 [% IF ( loggedinusername ) %]
 Welcome, <a href="/cgi-bin/koha/opac-user.pl">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</a>
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 ) %]
 Automatic Login
 Sorry, your automatic login failed. [% invalidOAuth2Login %]
 Please note that automatic login will only work if you are using the email address registered with this library.
 If you want to, you can try to <a href="/cgi-bin/koha/svc/googleoauth2?reauthenticate=select_account">login using a differen account</a>
 Local login
 If you can't login automatically, you can still login in manually: 
 [% 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===
* WebIDs and the WebID Protocol
** FOAF+SSL
==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
Category:OPAC_RFCs
Category:RFCs_not_targeted_at_a_particular_Koha_version