View | Details | Raw Unified | Return to bug 10988
Collapse All | Expand All

(-)a/C4/Auth.pm (-1 / +10 lines)
Lines 37-43 use List::MoreUtils qw/ any /; Link Here
37
use Encode qw( encode is_utf8);
37
use Encode qw( encode is_utf8);
38
38
39
# use utf8;
39
# use utf8;
40
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout $shib $shib_login);
40
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $googleoauth2 $ldap $cas $caslogout $shib $shib_login);
41
41
42
BEGIN {
42
BEGIN {
43
    sub psgi_env { any { /^psgi\./ } keys %ENV }
43
    sub psgi_env { any { /^psgi\./ } keys %ENV }
Lines 55-60 BEGIN { Link Here
55
      &get_all_subpermissions &get_user_subpermissions
55
      &get_all_subpermissions &get_user_subpermissions
56
    );
56
    );
57
    %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] );
57
    %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] );
58
    $googleoauth2 = C4::Context->preference('GoogleOAuth2');
58
    $ldap      = C4::Context->config('useldapserver') || 0;
59
    $ldap      = C4::Context->config('useldapserver') || 0;
59
    $cas       = C4::Context->preference('casAuthentication');
60
    $cas       = C4::Context->preference('casAuthentication');
60
    $shib      = C4::Context->config('useshibboleth') || 0;
61
    $shib      = C4::Context->config('useshibboleth') || 0;
Lines 465-470 sub get_template_and_user { Link Here
465
            BranchesLoop                          => GetBranchesLoop($opac_name),
466
            BranchesLoop                          => GetBranchesLoop($opac_name),
466
            BranchCategoriesLoop                  => GetBranchCategories( 'searchdomain', 1, $opac_name ),
467
            BranchCategoriesLoop                  => GetBranchCategories( 'searchdomain', 1, $opac_name ),
467
            CalendarFirstDayOfWeek                => ( C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday" ) ? 0 : 1,
468
            CalendarFirstDayOfWeek                => ( C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday" ) ? 0 : 1,
469
            GoogleOAuth2                          => (C4::Context->preference("GoogleOAuth2")),
468
            LibraryName                           => "" . C4::Context->preference("LibraryName"),
470
            LibraryName                           => "" . C4::Context->preference("LibraryName"),
469
            LibraryNameTitle                      => "" . $LibraryNameTitle,
471
            LibraryNameTitle                      => "" . $LibraryNameTitle,
470
            LoginBranchname                       => C4::Context->userenv ? C4::Context->userenv->{"branchname"} : "",
472
            LoginBranchname                       => C4::Context->userenv ? C4::Context->userenv->{"branchname"} : "",
Lines 1240-1245 sub checkauth { Link Here
1240
        );
1242
        );
1241
    }
1243
    }
1242
1244
1245
    if ($googleoauth2) {
1246
        if ($query->param("OAuth2Failed")) {
1247
            my $reason = $query->param('OAuth2Failed');
1248
            $template->param(invalidOAuth2Login => $reason);
1249
        }
1250
    }
1251
1243
    my $self_url = $query->url( -absolute => 1 );
1252
    my $self_url = $query->url( -absolute => 1 );
1244
    $template->param(
1253
    $template->param(
1245
        url         => $self_url,
1254
        url         => $self_url,
(-)a/installer/data/mysql/sysprefs.sql (+4 lines)
Lines 134-139 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
134
('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'),
134
('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'),
135
('GoogleIndicTransliteration','0','','GoogleIndicTransliteration on the OPAC.','YesNo'),
135
('GoogleIndicTransliteration','0','','GoogleIndicTransliteration on the OPAC.','YesNo'),
136
('GoogleJackets','0',NULL,'if ON, displays jacket covers from Google Books API','YesNo'),
136
('GoogleJackets','0',NULL,'if ON, displays jacket covers from Google Books API','YesNo'),
137
('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'),
138
('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'),
139
('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'),
140
('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain (or subdomains of this domain). Leave blank for all google domains', 'Free'),
137
('hidelostitems','0','','If ON, disables display of\"lost\" items in OPAC.','YesNo'),
141
('hidelostitems','0','','If ON, disables display of\"lost\" items in OPAC.','YesNo'),
138
('HidePatronName','0','','If this is switched on, patron\'s cardnumber will be shown instead of their name on the holds and catalog screens','YesNo'),
142
('HidePatronName','0','','If this is switched on, patron\'s cardnumber will be shown instead of their name on the holds and catalog screens','YesNo'),
139
('hide_marc','0',NULL,'If ON, disables display of MARC fields, subfield codes & indicators (still shows data)','YesNo'),
143
('hide_marc','0',NULL,'If ON, disables display of MARC fields, subfield codes & indicators (still shows data)','YesNo'),
(-)a/installer/data/mysql/updatedatabase.pl (+16 lines)
Lines 9793-9798 if(CheckVersion($DBversion)) { Link Here
9793
    SetVersion($DBversion);
9793
    SetVersion($DBversion);
9794
}
9794
}
9795
9795
9796
$DBversion = "XXX";
9797
if(CheckVersion($DBversion)) {
9798
    $dbh->do(q{
9799
        INSERT INTO systempreferences
9800
          (variable,value,explanation,options,type)
9801
        VALUES
9802
          ('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'),
9803
          ('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'),
9804
          ('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'),
9805
          ('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain (or subdomains of this domain). Leave blank for all google domains', 'Free')
9806
    });
9807
9808
    print "Upgrade to $DBversion done (Bug 10988: Allow login via Google OAuth2)\n";
9809
    SetVersion($DBversion);
9810
}
9811
9796
=head1 FUNCTIONS
9812
=head1 FUNCTIONS
9797
9813
9798
=head2 TableExists($table)
9814
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref (+18 lines)
Lines 114-119 Administration: Link Here
114
                  Common Name: the Common Name
114
                  Common Name: the Common Name
115
                  emailAddress: the emailAddress
115
                  emailAddress: the emailAddress
116
            - field for SSL client certificate authentication
116
            - field for SSL client certificate authentication
117
    Google OAuth2:
118
        -
119
            - pref: GoogleOAuth2
120
              choices:
121
                yes: Use
122
                no: "Don't Use"
123
            - Google OAuth2 login.
124
            - 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 .
125
        -
126
            - Google OAuth2 Client ID
127
            - pref: GoogleOAuth2ClientID
128
        -
129
            - Google OAuth2 Client Secret
130
            - pref: GoogleOAuth2ClientSecret
131
        -
132
            - Google OAuth2 Restrict to domain (or subdomain of this domain)
133
            - pref: GoogleOAuth2Domain
134
            - . Leave blank for all google domains
117
    Mozilla Persona:
135
    Mozilla Persona:
118
        -
136
        -
119
            - pref: Persona
137
            - pref: Persona
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc (+4 lines)
Lines 58-63 Link Here
58
                                                <li role="presentation"><a href="#" tabindex="-1" class="menu-inactive" role="menuitem">No private lists</a></li>
58
                                                <li role="presentation"><a href="#" tabindex="-1" class="menu-inactive" role="menuitem">No private lists</a></li>
59
                                                <li role="presentation"><a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a></li>
59
                                                <li role="presentation"><a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a></li>
60
                                            [% END %]
60
                                            [% END %]
61
                                        [% ELSIF ( GoogleOAuth2 ) %]
62
                                            <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>
61
                                        [% ELSE %]
63
                                        [% ELSE %]
62
                                            <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>
64
                                            <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>
63
                                        [% END # / IF loggedinusername %]
65
                                        [% END # / IF loggedinusername %]
Lines 76-81 Link Here
76
                                        [% IF Koha.Preference('casAuthentication') %]
78
                                        [% IF Koha.Preference('casAuthentication') %]
77
                                            [%# CAS authentication is too complicated for modal window %]
79
                                            [%# CAS authentication is too complicated for modal window %]
78
                                            <li><a href="/cgi-bin/koha/opac-user.pl">Log in to your account</a></li>
80
                                            <li><a href="/cgi-bin/koha/opac-user.pl">Log in to your account</a></li>
81
                                        [% ELSIF ( GoogleOAuth2 ) %]
82
                                            <li><a href="/cgi-bin/koha/svc/googleoauth2" role="button" data-toggle="modal">Log in to your account</a></li>
79
                                        [% ELSE %]
83
                                        [% ELSE %]
80
                                            <li><a href="/cgi-bin/koha/opac-user.pl" class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a></li>
84
                                            <li><a href="/cgi-bin/koha/opac-user.pl" class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a></li>
81
                                        [% END %]
85
                                        [% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt (+9 lines)
Lines 138-143 Link Here
138
138
139
                            [% END # / IF casAuthentication %]
139
                            [% END # / IF casAuthentication %]
140
140
141
                            [% IF ( invalidOAuth2Login ) %]
142
                                <h4>Automatic Login</h4>
143
                                <p>Sorry, your automatic login failed. <span class="error">[% invalidOAuth2Login %]</span></p>
144
                                <p>Please note that automatic login will only work if you are using the emal address registered with this library.</p>
145
                                <p>If you want to, you can try to <a href="/cgi-bin/koha/svc/googleoauth2?reauthenticate=select_account">login using a different account</a>
146
                                <h4>Local login</h4>
147
                                <p>If you can't login automatically, you can still login manually: </p>
148
                            [% END %]
149
141
                            <form action="[% url %]" name="auth" id="auth" method="post">
150
                            <form action="[% url %]" name="auth" id="auth" method="post">
142
                                <input type="hidden" name="koha_login_context" value="opac" />
151
                                <input type="hidden" name="koha_login_context" value="opac" />
143
                                <fieldset class="brief">
152
                                <fieldset class="brief">
(-)a/opac/svc/googleoauth2 (-1 / +161 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl -w
2
# Copyright vanoudt@gmail.com 2014
3
# Based on persona code from chris@bigballofwax.co.nz 2013
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
#
20
#
21
# Basic OAuth2 authentication for google goes like this First:
22
# get your clientid, clientsecret from google. At this stage, tell
23
# google that your redirect url is /cgi-bin/koha/svc/oauthlogin
24
#
25
# The first thing that happens when this script is called is
26
# that one gets redirected to an authentication url from google
27
#
28
# If successful, that then redirects back to this script, setting
29
# a CODE parameter which we use to look up a json authentication
30
# token. This token includes an encrypted json id_token, which we
31
# round-trip back to google to decrypt. Finally, we can extract
32
# the email address from this.
33
#
34
# There is some room for improvement here.  In particular, Google
35
# recommends verifying and decrypting the id_token locally, which
36
# means caching some information and updating it daily. But that
37
# would make things a lot faster
38
39
use Modern::Perl;
40
use CGI qw/escape/;
41
use C4::Auth;
42
use C4::Context;
43
use C4::Output;
44
45
use LWP::UserAgent;
46
use HTTP::Request::Common qw{ POST };
47
use JSON;
48
49
my $scope            = "openid email";
50
my $host             = C4::Context->preference('OPACBaseURL') // '';
51
my $restricttodomain = C4::Context->preference('GoogleOAuth2Domain') // '';
52
# protocol is assumed in OPACBaseURL see bug 5010.
53
my $redirecturl      = $host . '/cgi-bin/koha/svc/googleoauth2';
54
my $issuer           = 'accounts.google.com';
55
my $clientid         = C4::Context->preference('GoogleOAuth2ClientID');
56
my $clientsecret     = C4::Context->preference('GoogleOAuth2ClientSecret');
57
58
my $query = new CGI;
59
60
sub loginfailed {
61
    my $query  = shift;
62
    my $reason = shift;
63
    $query->delete('code');
64
    $query->param( 'OAuth2Failed' => $reason );
65
    my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
66
        {
67
            template_name   => 'opac-user.tmpl',
68
            query           => $query,
69
            type            => 'opac',
70
            authnotrequired => 0,
71
            flagsrequired   => { borrow => 1 },
72
        }
73
    );
74
    $template->param( 'invalidOAuth2Login' => $reason );
75
    $template->param( 'loginprompt'        => 1 );
76
    output_html_with_http_headers $query, $cookie, $template->output;
77
}
78
79
#die $query->param('code');
80
if ( defined $query->param('error') ) {
81
    loginfailed( $query,
82
            'An authentication error occurred. (Error:'
83
          . $query->param('error')
84
          . ')' );
85
}
86
elsif ( defined $query->param('code') ) {
87
    my $code    = $query->param('code');
88
    my $ua      = LWP::UserAgent->new();
89
    my $request = POST(
90
        'https://accounts.google.com/o/oauth2/token',
91
        [
92
            code          => $code,
93
            client_id     => $clientid,
94
            client_secret => $clientsecret,
95
            redirect_uri  => $redirecturl,
96
            grant_type    => 'authorization_code',
97
            $scope        => $scope
98
        ]
99
    );
100
    my $response = $ua->request($request)->decoded_content;
101
    my $json     = decode_json($response);
102
    if ( exists( $json->{'id_token'} ) ) {
103
        $request = POST( 'https://www.googleapis.com/oauth2/v1/tokeninfo',
104
            [ id_token => $json->{'id_token'} ] );
105
        $response = $ua->request($request)->decoded_content;
106
        $json     = decode_json($response);
107
108
# Confirm (as google suggests) that the issuer and audience are what we expect them to be
109
        if (   ( $json->{'issuer'} eq $issuer )
110
            && ( $json->{'audience'} eq $clientid )
111
            && exists( $json->{'email'} ) )
112
        {
113
            my $email = $json->{'email'};
114
            my ( $userid, $cookie, $sessionID ) =
115
              checkauth( $query, 1, { borrow => 1 }, 'opac', $email );
116
            if ($userid) {    # A valid user has logged in
117
                print $query->redirect(
118
                    -uri    => '/cgi-bin/koha/opac-user.pl',
119
                    -cookie => $cookie
120
                );
121
            }
122
            else {
123
                loginfailed( $query,
124
'The email address you are trying to use is not associated with a borrower in this library.'
125
                );
126
            }
127
        }
128
        else {    # something went wrong with getting appropriate credentials
129
            loginfailed( $query,
130
                'Failed to get proper credentials from google.' );
131
        }
132
    }
133
    else {        # Failed to get ID Token
134
        loginfailed( $query,
135
'An authentication error occurred. (Error: No ID Token was supplied)'
136
        );
137
    }
138
139
}
140
else {
141
    my $prompt = '';
142
    $prompt = $query->param('reauthenticate')
143
      unless not( defined $query->param('reauthenticate') );
144
145
    my $authorisationurl =
146
        'https://accounts.google.com/o/oauth2/auth?'
147
      . 'response_type=code&'
148
      . 'redirect_uri='
149
      . escape($redirecturl) . '&'
150
      . 'client_id='
151
      . escape($clientid) . '&'
152
      . 'scope='
153
      . escape($scope) . '&';
154
    if ( $restricttodomain ne '' ) {
155
        $authorisationurl .= 'hd=' . $restricttodomain . '&';
156
    }
157
    if ( $query->param('reauthenticate') ) {
158
        $authorisationurl .= 'prompt=' . escape( $query->param('reauthenticate') );
159
    }
160
    print $query->redirect($authorisationurl);
161
}

Return to bug 10988