| Line 0
          
      
      
        Link Here | 
            
              |  |  | 1 | #!/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 under the | 
            
              | 8 | # terms of the GNU General Public License as published by the Free Software | 
            
              | 9 | # Foundation; either version 3 of the License, or (at your option) any later | 
            
              | 10 | # version. | 
            
              | 11 | #  | 
            
              | 12 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | 
            
              | 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | 
            
              | 14 | # A PARTICULAR PURPOSE.  See the GNU General Public License for more details. | 
            
              | 15 | #  | 
            
              | 16 | # You should have received a copy of the GNU General Public License along | 
            
              | 17 | # with Koha; if not, write to the Free Software Foundation, Inc., | 
            
              | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
            
              | 19 | # | 
            
              | 20 | # | 
            
              | 21 | # Basic OAuth2 authentication for google goes like this | 
            
              | 22 | # First: get your clientid, clientsecret from google. At this stage, tell google that | 
            
              | 23 | # your redirect url is /cgi-bin/koha/svc/oauthlogin | 
            
              | 24 | # | 
            
              | 25 | # The first thing that happens when this script is called is that one gets redirected | 
            
              | 26 | # to an authentication url from google | 
            
              | 27 | # | 
            
              | 28 | # If successful, that then redirects back to this script, setting a CODE parameter | 
            
              | 29 | # which we use to look up a json authentication token. This token includes an encrypted  | 
            
              | 30 | # json id_token, which we round-trip back to google to decrypt. Finally, we can extract | 
            
              | 31 | # the email address from this. | 
            
              | 32 | # | 
            
              | 33 | # There is some room for improvement here. | 
            
              | 34 | # In particular, Google recommends verifying and decrypting the id_token locally, which means caching | 
            
              | 35 | # some information and updating it daily. But that would make things a lot faster | 
            
              | 36 |  | 
            
              | 37 | use strict; | 
            
              | 38 | use warnings; | 
            
              | 39 | use CGI qw/escape/; | 
            
              | 40 | use C4::Auth; | 
            
              | 41 | use C4::Context; | 
            
              | 42 | use C4::Output; | 
            
              | 43 |  | 
            
              | 44 | use LWP::UserAgent;  | 
            
              | 45 | use HTTP::Request::Common qw{ POST }; | 
            
              | 46 | use JSON; | 
            
              | 47 |  | 
            
              | 48 | my $scope="openid email"; | 
            
              | 49 | my $host = C4::Context->preference('OPACBaseURL'); | 
            
              | 50 | my $restricttodomain = C4::Context->preference('GoogleOAuth2Domain'); | 
            
              | 51 | my $redirecturl = 'http://' . $host . '/cgi-bin/koha/svc/googleoauth2'; | 
            
              | 52 | my $issuer = 'accounts.google.com'; | 
            
              | 53 | my $clientid = C4::Context->preference('GoogleOAuth2ClientID'); | 
            
              | 54 | my $clientsecret = C4::Context->preference('GoogleOAuth2ClientSecret'); | 
            
              | 55 |  | 
            
              | 56 | my $query = new CGI; | 
            
              | 57 |  | 
            
              | 58 | sub loginfailed { | 
            
              | 59 |   my $query = shift; | 
            
              | 60 |   my $reason = shift; | 
            
              | 61 |   $query->delete('code'); | 
            
              | 62 |   $query->param('OAuth2Failed'=>$reason); | 
            
              | 63 |   my ( $template, $borrowernumber, $cookie ) = get_template_and_user( | 
            
              | 64 |     { | 
            
              | 65 |         template_name   => 'opac-user.tmpl', | 
            
              | 66 |         query           => $query, | 
            
              | 67 |         type            => 'opac', | 
            
              | 68 |         authnotrequired => 0, | 
            
              | 69 |         flagsrequired   => { borrow => 1 }, | 
            
              | 70 |     } | 
            
              | 71 |   ); | 
            
              | 72 |   $template->param( 'invalidOAuth2Login' => $reason ); | 
            
              | 73 |   $template->param( 'loginprompt' => 1); | 
            
              | 74 |   output_html_with_http_headers $query, $cookie, $template->output; | 
            
              | 75 | } | 
            
              | 76 | #die $query->param('code'); | 
            
              | 77 | if (defined $query->param('error')) { | 
            
              | 78 |        loginfailed($query, 'An authentication error occurred. (Error:' . $query->param('error') . ')'); | 
            
              | 79 | } elsif (defined $query->param('code')) { | 
            
              | 80 |        my $code=$query->param('code'); | 
            
              | 81 |        my $ua = LWP::UserAgent->new(); | 
            
              | 82 |        my $request = POST('https://accounts.google.com/o/oauth2/token', [ | 
            
              | 83 |                         code => $code, | 
            
              | 84 |                         client_id => $clientid, | 
            
              | 85 |                         client_secret => $clientsecret, | 
            
              | 86 |                        redirect_uri => $redirecturl, | 
            
              | 87 |                                grant_type => 'authorization_code', | 
            
              | 88 |                         $scope => $scope | 
            
              | 89 |     ]); | 
            
              | 90 |        my $response =  $ua->request($request)->decoded_content ; | 
            
              | 91 |        my $json = decode_json ( $response); | 
            
              | 92 |        if ( exists($json->{'id_token'}) ) { | 
            
              | 93 |                $request = POST('https://www.googleapis.com/oauth2/v1/tokeninfo', [ | 
            
              | 94 |                        id_token => $json->{'id_token'} | 
            
              | 95 |                ]); | 
            
              | 96 |                $response = $ua->request($request)->decoded_content; | 
            
              | 97 |                $json = decode_json ( $response); | 
            
              | 98 |                # Confirm (as google suggests) that the issuer and audience are what we expect them to be | 
            
              | 99 |                if (($json->{'issuer'} eq $issuer) && ($json->{'audience'} eq $clientid) && exists($json->{'email'})) { | 
            
              | 100 |                        my $email = $json->{'email'}; | 
            
              | 101 |                        my ( $userid, $cookie, $sessionID ) = | 
            
              | 102 |                             checkauth( $query, 1,  { borrow => 1 }, 'opac', $email ); | 
            
              | 103 |                        if ($userid) { # A valid user has logged in | 
            
              | 104 |                                print $query->redirect( -uri => '/cgi-bin/koha/opac-user.pl', -cookie => $cookie ); | 
            
              | 105 |                        } else { | 
            
              | 106 |                                loginfailed($query, 'The email address you are trying to use is not associated with a borrower in this library.'); | 
            
              | 107 |                        } | 
            
              | 108 |                } else { # something went wrong with getting appropriate credentials | 
            
              | 109 |                        loginfailed($query, 'Failed to get proper credentials from google.'); | 
            
              | 110 |                } | 
            
              | 111 |        } else { # Failed to get ID Token | 
            
              | 112 |                loginfailed($query, 'An authentication error occurred. (Error: No ID Token was supplied)'); | 
            
              | 113 |        }        | 
            
              | 114 |  | 
            
              | 115 | } else { | 
            
              | 116 |     my  $prompt = ''; | 
            
              | 117 |     $prompt = $query->param('reauthenticate') unless not (defined $query->param('reauthenticate')); | 
            
              | 118 |  | 
            
              | 119 |     my $authorisationurl =  'https://accounts.google.com/o/oauth2/auth?' . | 
            
              | 120 |                             'response_type=code&' . | 
            
              | 121 |                             'redirect_uri=' . escape ( $redirecturl ) . '&' . | 
            
              | 122 |                             'client_id=' . escape ( $clientid ) . '&' . | 
            
              | 123 |                             'scope=' . escape ( $scope ) . '&'; | 
            
              | 124 |    if ( $restricttodomain ne '' ) { | 
            
              | 125 |       $authorisationurl .= 'hd=' . $restricttodomain . '&'; | 
            
              | 126 |    } | 
            
              | 127 |     $authorisationurl .=    'prompt=' . escape ( $query->param('reauthenticate') ); | 
            
              | 128 |        print $query->redirect($authorisationurl); | 
            
              | 129 | } | 
            
              | 130 |  | 
            
              | 131 |  |