Skip to content

Instantly share code, notes, and snippets.

@atatarn
Last active January 20, 2021 12:49
Show Gist options
  • Save atatarn/34f4095961e4b5391b88 to your computer and use it in GitHub Desktop.
Save atatarn/34f4095961e4b5391b88 to your computer and use it in GitHub Desktop.
Google OpenIDConnect (OAuth 2.0 for Login) example with Mojolicious
use Mojolicious::Lite;
use Mojo::Util qw(url_escape b64_decode);
use Mojo::JSON qw(from_json);
# Sample Google OAuth2 user email application using Mojolicious
# Based on the idea of https://gist.github.com/throughnothing/3726907
# For more info see:
# https://developers.google.com/accounts/docs/OpenIDConnect
# https://developers.google.com/wallet/digital/docs/jwtdecoder
#
# Run this sample app with:
# morbo oauth.pl --listen http://*:5555
my $config = plugin Config => { default => {
# Google OAuth API Key Values
# Get yours from: https://console.developers.google.com
client_id => '',
client_secret => '',
# Google OpenIDConnect Scope
scope => 'openid email',
# Google OAuth base uri
oauth_base => 'https://accounts.google.com/o/oauth2/auth',
oauth_token_service => 'https://www.googleapis.com/oauth2/v3/token',
# Application callback
cb => url_escape( 'http://mydomain.com:5555/oauth2callback' ),
cb_plain => 'http://mydomain.com:5555/oauth2callback',
}};
get '/' => sub { shift->render( 'home' ) };
get '/auth' => sub {
# Google Login Page URL construction
my $login_uri = "$config->{oauth_base}?client_id=$config->{client_id}&response_type=code&scope=$config->{scope}&redirect_uri=$config->{cb}";
# You can append 'hd' parameter to force only users from our Google Hosted Domain to log in
# See 'hd' in https://developers.google.com/accounts/docs/OpenIDConnect#authenticationuriparameters
# Comment this line to allow user to use any of his Google Accounts
$login_uri .= '&hd=mydomain.com';
shift->redirect_to($login_uri);
};
# OAuth2 callback from google
get '/oauth2callback' => sub {
my $self = shift;
my $ua = $self->app->ua->post($config->{oauth_token_service} => form => {
code => $self->param('code'),
client_id => $config->{client_id},
client_secret => $config->{client_secret},
redirect_uri => $config->{cb_plain},
grant_type => 'authorization_code'
});
return $self->render(json => { error => 'Error getting tokens', debug => $ua->res->json }) unless $ua->res->is_status_class(200);
# Google OpenIDConnect response is a JWT string
# JWT contains header, claims and signature sections separated by dot - .
# Users' info is in second section (claims), presented as a base64 string withoud '=' in the end
my @tokens = split(/\./, $ua->res->json->{id_token});
# Append extra characters to make original string base64 decodable
# See: https://github.com/googlewallet/jwt-decoder-python/blob/master/jwtdecoder.py
my $data = from_json(b64_decode($tokens[1].('=' x (4 - (length($tokens[1]) % 4)))));
# Resulting data: email, sub, hd (see: https://developers.google.com/accounts/docs/OpenIDConnect#obtainuserinfo )
$self->render(json => {
email => $data->{email},
email_verified => $data->{email_verified},
sub => $data->{sub},
hd => $data->{hd} ? $data->{hd} : 'Not in a Hosted Domain'
});
};
app->start;
__DATA__
@@ home.html.ep
<a href='/auth'>Click here</a> to authenticate with Google OAuth.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment