From faea34e5c94922645b337bdeb5db32871eb1fde9 Mon Sep 17 00:00:00 2001 From: Matthias Benkard Date: Mon, 13 Apr 2015 14:16:43 +0000 Subject: Support Google OpenID Connect. --- www/authenticate.pl | 31 ++++++++++++++++++++----------- www/login.pl | 53 +++++++++++++++++++++++++++++++++++++++++++---------- www/provision.js | 2 +- 3 files changed, 64 insertions(+), 22 deletions(-) (limited to 'www') diff --git a/www/authenticate.pl b/www/authenticate.pl index ed0fb95..3c865ac 100755 --- a/www/authenticate.pl +++ b/www/authenticate.pl @@ -10,7 +10,8 @@ use JSON; use CGI; use CGI::Fast; -use Net::Google::FederatedLogin; +use OIDC::Lite; +use OIDC::Lite::Client::WebServer; do "common.pl"; @@ -21,23 +22,31 @@ while (my $cgi = new CGI::Fast) { my $realdomain = $::MULKONF->{real_domain}; my $claimed_email = $cgi->param('email'); - $claimed_email =~ s/\@$fakedomain/\@$realdomain/ if $fakedomain; + $claimed_email =~ s/\@$fakedomain/\@$realdomain/ if $fakedomain && $claimed_email; given (my $_ = $::MULKONF->{auth_type}) { when ('imap') { print $cgi->redirect(reluri($cgi, "authenticate-with-password.html?email=$claimed_email")); } when ('google') { - my $g = Net::Google::FederatedLogin->new( - claimed_id => $claimed_email, - return_to => reluri($cgi, 'login.pl'), - extensions => [{ns => 'ax', - uri => 'http://openid.net/srv/ax/1.0', - attributes => {mode => 'fetch_request', - required => 'email', - type => {email => 'http://axschema.org/contact/email'}}}] + my $oidc_client = OIDC::Lite::Client::WebServer->new( + id => $::MULKONF->{'google_oauth2_client_id'}, + secret => $::MULKONF->{'google_oauth2_client_secret'}, + authorize_uri => 'https://accounts.google.com/o/oauth2/auth', + access_token_uri => 'https://accounts.google.com/o/oauth2/token' ); - print $cgi->redirect($g->get_auth_url()); + # FIXME: Make `state` a unique, random session token! (Maybe a + # signed, timestamped web token, so stateless?) + print $cgi->redirect($oidc_client->uri_to_redirect( + redirect_uri => reluri($cgi, 'login.pl'), + scope => 'openid email', + state => '', + extra => { + access_type => 'online', + login_hint => $claimed_email, + response_type => 'code' + } + )); } default { die "Invalid auth_type! " . $::MULKONF->{auth_type}; diff --git a/www/login.pl b/www/login.pl index 363f243..fe1729d 100755 --- a/www/login.pl +++ b/www/login.pl @@ -16,9 +16,15 @@ use CGI::Fast; use CGI::Cookie; use Mail::IMAPTalk ; -use Net::Google::FederatedLogin; #use IO::Socket::SSL; +use OIDC::Lite; +use OIDC::Lite::Client::WebServer; +use OIDC::Lite::Model::IDToken; + +use LWP::UserAgent; +use HTTP::Request; + do "common.pl"; sub check_imap_password($$) { @@ -72,18 +78,45 @@ while (my $cgi = new CGI::Fast) { die "Could not authenticate."; } when ('google') { - my $g = Net::Google::FederatedLogin->new( - cgi => $cgi, - return_to => reluri($cgi, "login.pl") + my $code = $cgi->param('code') or die "Authorization code is missing."; + my $oidc_client = OIDC::Lite::Client::WebServer->new( + id => $::MULKONF->{'google_oauth2_client_id'}, + secret => $::MULKONF->{'google_oauth2_client_secret'}, + authorize_uri => 'https://accounts.google.com/o/oauth2/auth', + access_token_uri => 'https://accounts.google.com/o/oauth2/token' ); - $g->verify_auth or die "Could not verify the OpenID assertion!"; - my $ext = $g->get_extension('http://openid.net/srv/ax/1.0'); - my $verified_email = $ext->get_parameter('value.email'); - my $fakedomain = $::MULKONF->{fake_domain}; - my $realdomain = $::MULKONF->{real_domain}; + + # Acquire access token. + my $token = $oidc_client->get_access_token( + code => $code, + redirect_uri => reluri($cgi, 'login.pl') + ); + unless ($token) { + my $oidc_response = $oidc_client->last_response; + die "Could not acquire access token: " . $oidc_response->code . " (" . $oidc_response->content . ")"; + } + + # Extract ID token. + my $id_token = OIDC::Lite::Model::IDToken->load($token->id_token); + + # Acquire user information. + my $userinfo_request = HTTP::Request->new(GET => 'https://www.googleapis.com/oauth2/v3/userinfo'); + $userinfo_request->header(Authorization => "Bearer " . $token->access_token); + my $userinfo_response = LWP::UserAgent->new->request($userinfo_request); + unless ($userinfo_response->is_success) { + die "Could not acquire user information: " . $userinfo_response->code . " (" . $userinfo_response->content . ")"; + } + my $userinfo = decode_json($userinfo_response->content); + + # Verify e-mail and sign user's identity assertion. + unless ($userinfo->{'email_verified'}) { + die "User email is not verified." + } + my $verified_email = $userinfo->{'email'}; + my $fakedomain = $::MULKONF->{'fake_domain'}; + my $realdomain = $::MULKONF->{'real_domain'}; $verified_email =~ s/\@$realdomain/\@$fakedomain/ if $fakedomain; print $cgi->redirect(-cookie => cookie_for_user($verified_email), -url => reluri($cgi, 'successful-login.html')); - exit 0; } default { die "Invalid auth_type. Check MulkyID configuration!"; diff --git a/www/provision.js b/www/provision.js index 4113be0..ad6971e 100644 --- a/www/provision.js +++ b/www/provision.js @@ -36,7 +36,7 @@ jQuery(function($) { $.ajax({ type: 'POST', crossDomain: true, - url: '/browserid/sign.pl', + url: 'sign.pl', dataType: 'json', data: { email: email, pubkey: JSON.stringify(pubkey), duration: cert_duration }, success: function(data, status, xhr) { -- cgit v1.2.3