Bugzilla – Attachment 46473 Details for
Bug 10988
Allow login via Google OAuth2 (OpenID Connect)
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 10988 - Rewrite and renamed to OpenIDConnect
0001-Bug-10988-Fixes-for-comments-57-and-58.patch (text/plain), 19.38 KB, created by
Nicholas van Oudtshoorn
on 2016-01-11 08:52:21 UTC
(
hide
)
Description:
Bug 10988 - Rewrite and renamed to OpenIDConnect
Filename:
MIME Type:
Creator:
Nicholas van Oudtshoorn
Created:
2016-01-11 08:52:21 UTC
Size:
19.38 KB
patch
obsolete
>From 38fd802f8392062b43a8050b50e687d306aa0b1d Mon Sep 17 00:00:00 2001 >From: Nicholas van Oudtshoorn <vanoudt@gmail.com> >Date: Mon, 11 Jan 2016 16:47:41 +0800 >Subject: [PATCH] Bug 10988 - Fixes for comments 57 and 58 > > Test Plan (remains the same): > 0) Back up your database > 1) Apply all these patches > 2) In your mysql client use your Koha database and execute: > > DELETE FROM systempreferences; > > SOURCE ~/kohaclone/installer/data/mysql/sysprefs.sql; > -- Should be no errors. > > SELECT * FROM systempreferences LIKE 'GoogleO%'; > -- Should see 4 entries. > > QUIT; > 3) Restore your database > 4) Run ./installer/data/mysql/updatedatabase.pl; > 5) In your mysql client use your Koha database and execute: > > SELECT * FROM systempreferences LIKE 'GoogleO%'; > -- Should see the same 4 entries. > 6) Log into the staff client > 7) Home -> Koha administration -> Global system preferences > 8) -> OPAC > -- make sure your OPACBaseURL is set (e.g. https://opac.koha.ca) > 9) -> Administration > -- There should be a 'Google OAuth2' section with the ability > to set those 4 system preferences. > 10) In a new tab, go to https://console.developers.google.com/project > 11) Click 'Create Project' > 12) Type in a project name that won't freak users out, like your > library name (e.g. South Pole Library). > 13) Click the 'Create' button. > 14) Click the 'APIs & auth' in the left frame. > 15) Click 'Credentials' > 16) Click 'Create new Client ID' > 17) Select 'Web application' and click 'Configure consent screen'. > 18) Select the Email Address. > 19) Put it a meaningful string into the Product Name > (e.g. South Pole Library Authentication) > 20) Fill in the other fields as desired (or not) > 21) Click 'Save' > 22) Change the 'AUTHORIZED JAVASCRIPT ORIGINS' to your OPACBaseURL. > (http://library.yourDNS.org) > 23) Change the 'AUTHORIZED REDIRECT URIS' to point to the new > googleoauth2 script > (http://library.yourDNS.org/cgi-bin/koha/svc/auth/googleopenidconnect) > 24) Click 'Create Client ID' > 25) Copy and paste the 'CLIENT ID' into the GoogleOAuth2ClientID > system preference. > 26) Copy and paste the 'CLIENT SECRET' into the GoogleOAuth2ClientSecret > system preference. > 27) Change the GoogleOpenIDConnect preference to 'Use'. > 28) Click 'Save all Administration preferences' > 29) In the OPAC, click 'Log in to your account'. > -- You should get a confirmation request, if you are > already logged in, OR a login screen if you are not. > -- You need to have the primary email address set to one > authenticated by Google in order to log in. > 30) Run koha qa test tools >--- > C4/Auth.pm | 7 + > .../mysql/atomicupdate/googleopenidconnect.sql | 5 + > installer/data/mysql/sysprefs.sql | 4 + > .../prog/en/modules/admin/preferences/admin.pref | 18 ++ > .../opac-tmpl/bootstrap/en/includes/masthead.inc | 2 + > .../opac-tmpl/bootstrap/en/modules/opac-auth.tt | 11 + > opac/svc/auth/googleopenidconnect | 240 +++++++++++++++++++++ > 7 files changed, 287 insertions(+) > create mode 100644 installer/data/mysql/atomicupdate/googleopenidconnect.sql > create mode 100755 opac/svc/auth/googleopenidconnect > >diff --git a/C4/Auth.pm b/C4/Auth.pm >index d7be1d8..a81fd30 100644 >--- a/C4/Auth.pm >+++ b/C4/Auth.pm >@@ -1287,6 +1287,13 @@ sub checkauth { > ); > } > >+ if (C4::Context->preference('GoogleOpenIDConnect')) { >+ if ($query->param("OpenIDConnectFailed")) { >+ my $reason = $query->param('OpenIDConnectFailed'); >+ $template->param(invalidGoogleOpenIDConnectLogin => $reason); >+ } >+ } >+ > $template->param( > LibraryName => C4::Context->preference("LibraryName"), > ); >diff --git a/installer/data/mysql/atomicupdate/googleopenidconnect.sql b/installer/data/mysql/atomicupdate/googleopenidconnect.sql >new file mode 100644 >index 0000000..2704bd1 >--- /dev/null >+++ b/installer/data/mysql/atomicupdate/googleopenidconnect.sql >@@ -0,0 +1,5 @@ >+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES >+ ('GoogleOpenIDConnect', '0', NULL, 'if ON, allows the use of Google OpenID Connect 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'), >+ ('GoogleOpenIDConnectDomain', '', NULL, 'Restrict OpenID Connect to this domain (or subdomains of this domain). Leave blank for all Google domains', 'Free'); >diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql >index aa53139..67c2da2 100644 >--- a/installer/data/mysql/sysprefs.sql >+++ b/installer/data/mysql/sysprefs.sql >@@ -153,6 +153,10 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` > ('gist','0','','Default Goods and Services tax rate NOT in %, but in numeric form (0.12 for 12%), set to 0 to disable GST','Integer'), > ('GoogleIndicTransliteration','0','','GoogleIndicTransliteration on the OPAC.','YesNo'), > ('GoogleJackets','0',NULL,'if ON, displays jacket covers from Google Books API','YesNo'), >+('GoogleOpenIDConnect', '0', NULL, 'if ON, allows the use of Google OpenID Connect 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'), >+('GoogleOpenIDConnectDomain', '', NULL, 'Restrict Google OpenID Connect to this domain (or subdomains of this domain). Leave blank for all Google domains', 'Free'), > ('hidelostitems','0','','If ON, disables display of\"lost\" items in OPAC.','YesNo'), > ('HidePatronName','0','','If this is switched on, patron\'s cardnumber will be shown instead of their name on the holds and catalog screens','YesNo'), > ('hide_marc','0',NULL,'If ON, disables display of MARC fields, subfield codes & indicators (still shows data)','YesNo'), >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref >index 0f3b66c..fe88fde 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref >@@ -143,6 +143,24 @@ Administration: > Common Name: the Common Name > emailAddress: the emailAddress > - field for SSL client certificate authentication >+ Google OpenID Connect: >+ - >+ - pref: GoogleOpenIDConnect >+ choices: >+ yes: Use >+ no: "Don't Use" >+ - Google OpenID Connect 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/auth/googleopenidconnect . >+ - >+ - Google OAuth2 Client ID >+ - pref: GoogleOAuth2ClientID >+ - >+ - Google OAuth2 Client Secret >+ - pref: GoogleOAuth2ClientSecret >+ - >+ - Google OpenID Connect Restrict to domain (or subdomain of this domain) >+ - pref: GoogleOpenIDConnectDomain >+ - . Leave blank for all google domains > Mozilla Persona: > - > - pref: Persona >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc >index 5a2e011..02a1c15 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc >@@ -90,6 +90,8 @@ > [% IF Koha.Preference('casAuthentication') %] > [%# CAS authentication is too complicated for modal window %] > <li><a href="/cgi-bin/koha/opac-user.pl">Log in to your account</a></li> >+ [% ELSIF ( Koha.Preference('GoogleOpenIDConnect') == 1 ) %] >+ <li><a href="/cgi-bin/koha/svc/auth/googleopenidconnect" 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 %] >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt >index 3dc1c56..efc73eb 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt >@@ -137,6 +137,17 @@ > [% END %] > > [% END # / IF casAuthentication %] >+ [% IF ( Koha.Preference('GoogleOpenIDConnect') == 1 ) %] >+ [% IF ( invalidGoogleOpenIDConnectLogin ) %] >+ <h4>Google login</h4> >+ <p>Sorry, your Google login failed. <span class="error">[% invalidGoogleOpenIDConnectLogin %]</span></p> >+ <p>Please note that the Google login will only work if you are using the e-mail address registered with this library.</p> >+ <p>If you want to, you can try to <a href="/cgi-bin/koha/svc/auth/googleopenidconnect?reauthenticate=select_account">log in using a different account</a> >+ [% END %] >+ <a href="/cgi-bin/koha/svc/auth/googleopenidconnect" class="btn btn-primary" id="openid_connect">Log in with Google</a> >+ <p>If you don not have a Google account, but do have a local account, you can still l >+og in: </p> >+ [% END %] > > <form action="/cgi-bin/koha/opac-user.pl" name="auth" id="auth" method="post"> > <input type="hidden" name="koha_login_context" value="opac" /> >diff --git a/opac/svc/auth/googleopenidconnect b/opac/svc/auth/googleopenidconnect >new file mode 100755 >index 0000000..8b91c27 >--- /dev/null >+++ b/opac/svc/auth/googleopenidconnect >@@ -0,0 +1,240 @@ >+#!/usr/bin/perl >+# 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, see <http://www.gnu.org/licenses>. >+# >+# >+# Basic OAuth2/OpenID Connect 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 Modern::Perl; >+use CGI qw ( -utf8 escape ); >+use C4::Auth qw{ checkauth get_session get_template_and_user }; >+use C4::Context; >+use C4::Output; >+ >+use LWP::UserAgent; >+use HTTP::Request::Common qw{ POST }; >+use JSON; >+use MIME::Base64 qw{ decode_base64url }; >+ >+my $discoveryDocURL = >+ 'https://accounts.google.com/.well-known/openid-configuration'; >+my $authendpoint = ''; >+my $tokenendpoint = ''; >+my $scope = 'openid email profile'; >+my $host = C4::Context->preference('OPACBaseURL') // q{}; >+my $restricttodomain = C4::Context->preference('GoogleOpenIDConnectDomain') >+ // q{}; >+ >+# protocol is assumed in OPACBaseURL see bug 5010. >+my $redirecturl = $host . '/cgi-bin/koha/svc/auth/googleopenidconnect'; >+my $issuer = 'accounts.google.com'; >+my $clientid = C4::Context->preference('GoogleOAuth2ClientID'); >+my $clientsecret = C4::Context->preference('GoogleOAuth2ClientSecret'); >+ >+my $ua = LWP::UserAgent->new(); >+my $response = $ua->get($discoveryDocURL); >+if ( $response->is_success ) { >+ my $json = decode_json( $response->decoded_content ); >+ if ( exists( $json->{'authorization_endpoint'} ) ) { >+ $authendpoint = $json->{'authorization_endpoint'}; >+ } >+ if ( exists( $json->{'token_endpoint'} ) ) { >+ $tokenendpoint = $json->{'token_endpoint'}; >+ } >+} >+ >+my $query = CGI->new; >+ >+sub loginfailed { >+ my $cgi_query = shift; >+ my $reason = shift; >+ $cgi_query->delete('code'); >+ $cgi_query->param( 'OpenIDConnectFailed' => $reason ); >+ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( >+ { >+ template_name => 'opac-user.tt', >+ query => $cgi_query, >+ type => 'opac', >+ authnotrequired => 0, >+ } >+ ); >+ $template->param( 'invalidGoogleOpenIDConnectLogin' => $reason ); >+ $template->param( 'loginprompt' => 1 ); >+ output_html_with_http_headers $cgi_query, $cookie, $template->output; >+ return; >+} >+ >+if ( defined $query->param('error') ) { >+ loginfailed( $query, >+ 'An authentication error occurred. (Error:' >+ . $query->param('error') >+ . ')' ); >+} >+elsif ( defined $query->param('code') ) { >+ my $stateclaim = $query->param('state'); >+ my $session = get_session( $query->cookie('CGISESSID') ); >+ if ( $session->param('google-openid-state') ne $stateclaim ) { >+ $session->clear( ["google-openid-state"] ); >+ $session->flush(); >+ loginfailed( $query, >+ 'Authentication failed. Your session has an unexpected state.' ); >+ } >+ $session->clear( ["google-openid-state"] ); >+ $session->flush(); >+ >+ my $code = $query->param('code'); >+ my $ua = LWP::UserAgent->new(); >+ if ( $tokenendpoint eq q{} ) { >+ loginfailed( $query, 'Unable to discover token endpoint.' ); >+ } >+ my $request = POST( >+ $tokenendpoint, >+ [ >+ 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'} ) ) { >+ if ( lc( $json->{'token_type'} ) ne 'bearer' ) { >+ loginfailed( $query, >+ 'Authentication failed. Incorrect token type.' ); >+ } >+ my $idtoken = $json->{'id_token'}; >+ >+# Normally we'd have to validate the token - but google says not to worry here (Avoids another library!) >+# See https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo for rationale >+ my @segments = split( '\.', $idtoken ); >+ unless ( scalar(@segments) == 3 ) { >+ loginfailed( $query, >+ 'Login token broken: either too many or too few segments.' ); >+ } >+ my ( $header, $claims, $validation ) = @segments; >+ $claims = decode_base64url($claims); >+ my $claims_json = decode_json($claims); >+ if ( ( $claims_json->{'iss'} ne ( 'https://' . $issuer ) ) >+ && ( $claims_json->{'iss'} ne $issuer ) ) >+ { >+ loginfailed( $query, >+ "Authentication failed. Issuer of authentication isn't Google." >+ ); >+ } >+ if ( ref( $claims_json->{'aud'} ) eq 'ARRAY' ) { >+ warn "Audience is an array of size: " >+ . scalar( @$claims_json->{'aud'} ); >+ if ( scalar( @$claims_json->{'aud'} ) > 1 ) >+ { # We don't want any other audiences >+ loginfailed( $query, >+ "Authentication failed. Unexpected audience provided." ); >+ } >+ } >+ if ( ( $claims_json->{'aud'} ne $clientid ) >+ || ( $claims_json->{'azp'} ne $clientid ) ) >+ { >+ loginfailed( $query, >+ "Authentication failed. Unexpected audience." ); >+ } >+ if ( $claims_json->{'exp'} < time() ) { >+ loginfailed( $query, 'Sorry, your authentication has timed out.' ); >+ } >+ >+ if ( exists( $claims_json->{'email'} ) ) { >+ my $email = $claims_json->{'email'}; >+ if ( ( $restricttodomain ne q{} ) >+ && ( index( $email, $restricttodomain ) < 0 ) ) >+ { >+ loginfailed( $query, >+'The email you have used is not valid for this library. Email addresses should conclude with ' >+ . $restricttodomain >+ . ' .' ); >+ } >+ else { >+ my ( $userid, $cookie, $session_id ) = >+ checkauth( $query, 1, {}, 'opac', $email ); >+ if ($userid) { # A user with this email is registered in koha >+ 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 at this library.' >+ ); >+ } >+ } >+ } >+ else { >+ loginfailed( $query, >+'Unexpectedly, no email seems to be associated with that acccount.' >+ ); >+ } >+ } >+ else { >+ loginfailed( $query, 'Failed to get proper credentials from Google.' ); >+ } >+} >+else { >+ my $session = get_session( $query->cookie('CGISESSID') ); >+ my $openidstate = 'auth_'; >+ $openidstate .= sprintf( "%x", rand 16 ) for 1 .. 32; >+ $session->param( 'google-openid-state', $openidstate ); >+ $session->flush(); >+ >+ my $prompt = $query->param('reauthenticate') // q{}; >+ if ( $authendpoint eq q{} ) { >+ loginfailed( $query, 'Unable to discover authorisation endpoint.' ); >+ } >+ my $authorisationurl = >+ $authendpoint . '?' >+ . 'response_type=code&' >+ . 'redirect_uri=' >+ . escape($redirecturl) . q{&} >+ . 'client_id=' >+ . escape($clientid) . q{&} >+ . 'scope=' >+ . escape($scope) . q{&} >+ . 'state=' >+ . escape($openidstate); >+ if ( $prompt || ( defined $prompt && length $prompt > 0 ) ) { >+ $authorisationurl .= '&prompt=' . escape($prompt); >+ } >+ print $query->redirect($authorisationurl); >+} >-- >2.4.3 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 10988
:
21740
|
21936
|
21938
|
21939
|
25343
|
25570
|
25684
|
29594
|
29595
|
30014
|
30015
|
36354
|
36355
|
40087
|
40252
|
40442
|
40979
|
41663
|
43791
|
43792
|
43938
|
45667
|
45668
|
45895
|
46016
|
46473
|
46474
|
47780
|
47781
|
47782
|
49785
|
49786
|
49787