1 package stemmaweb::Authentication::Credential::Google;
3 use Crypt::OpenSSL::X509;
8 use LWP::Simple qw(get);
9 use Date::Parse qw(str2time);
17 stemmaweb::Authentication::Google - JSON Web Token handler for Google tokens.
21 Retrieves Google's public certificates, and then retrieves the key from the
22 cert using L<Crypt::OpenSSL::X509>. Finally, uses the pubkey to decrypt a
23 Google token using L<JSON::WebToken>.
28 my ($class, $config, $app, $realm) = @_;
29 $class = ref $class || $class;
41 my ($self, $c, $realm, $authinfo) =@_;
43 my $id_token = $authinfo->{id_token};
44 $id_token ||= $c->req->method eq 'GET' ?
45 $c->req->query_params->{id_token} : $c->req->body_params->{id_token};
48 Catalyst::Exception->throw("id_token not specified.");
51 my $email = $authinfo->{email};
52 $email ||= $c->req->method eq 'GET' ? $c->req->query_params->{email} :
53 $c->req->body_params->{email};
55 my $userinfo = $self->decode($id_token);
57 my $sub = $userinfo->{sub};
58 my $openid = $userinfo->{openid_id};
60 $userinfo->{email} = $email if $email;
62 if (!$sub || !$openid) {
63 Catalyst::Exception->throw(
64 'Could not retrieve sub and openid from token! Is the token
69 return $realm->find_user($userinfo, $c);
76 Retrieves a pair of JSON-encoded certificates from the given URL (defaults to
77 Google's public cert url), and returns the decoded JSON object.
85 Optional. Location where certificates are located.
86 Defaults to https://www.googleapis.com/oauth2/v1/certs.
92 Decoded JSON object containing certificates.
97 my ($self, $url) = @_;
99 my $c = $self->{_app};
104 $url ||= ( $c->config->{'Authentication::Credential::Google'}->{public_cert_url} || 'https://www.googleapis.com/oauth2/v1/certs' );
106 if ( ($c->registered_plugins('Catalyst::Plugin::Cache')) && ($cache = $c->cache) ) {
107 if ($certs = $cache->get('certs')) {
108 $certs = decode_json($certs);
110 foreach my $key (keys %$certs) {
111 my $cert = $certs->{$key};
112 my $x509 = Crypt::OpenSSL::X509->new_from_string($cert);
114 if ($self->is_cert_expired($x509)) {
125 my $certs_encoded = get($url);
128 $cache->set('certs', $certs_encoded);
131 $certs = decode_json($certs_encoded);
137 =head2 get_key_from_cert
139 Given a pair of certificates $certs (defaults to L</retrieve_certs>),
140 this function returns the public key of the cert identified by $kid.
148 Required. Index of the certificate hash $hash where the cert we want is
153 Optional. A (hashref) pair of certificates.
154 It's retrieved using L</retrieve_certs> if not given,
155 or if the pair is expired.
161 Public key of certificate.
165 sub get_key_from_cert {
166 my ($self, $kid, $certs) = @_;
168 $certs ||= $self->retrieve_certs;
169 my $cert = $certs->{$kid};
170 my $x509 = Crypt::OpenSSL::X509->new_from_string($cert);
172 if ($self->is_cert_expired($x509)) {
173 # If we ended up here, we were given
174 # an old $certs string from the user.
175 # Let's force getting another.
176 return $self->get_key_from_cert($kid);
179 return $x509->pubkey;
182 =head2 is_cert_expired
184 Returns if a given L<Crypt::OpenSSL::X509> certificate is expired.
188 sub is_cert_expired {
189 my ($self, $x509) = @_;
191 my $expiry = str2time($x509->notAfter);
193 return time > $expiry;
198 Returns the decoded information contained in a user's token.
206 Required. The user's token from Google+.
210 Optional. A public key string with which to decode the token.
211 If not given, the public key will be retrieved from $certs.
215 Optional. A pair of public key certs retrieved from Google.
216 If not given, or if the certificates have expired, a new
217 pair of certificates is retrieved.
223 Decoded JSON object from the decrypted token.
228 my ($self, $token, $certs, $pubkey) = @_;
231 my $details = decode_json(
232 MIME::Base64::decode_base64(
233 substr( $token, 0, CORE::index($token, '.') )
237 my $kid = $details->{kid};
238 $pubkey = $self->get_key_from_cert($kid, $certs);
241 return JSON::WebToken->decode($token, $pubkey);
246 Errietta Kostala <e.kostala@shadowcat.co.uk>
250 This library is free software. You can redistribute it and/or modify
251 it under the same terms as Perl itself.