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;
43 my ($self, $c, $realm, $authinfo) =@_;
45 my $id_token = $authinfo->{id_token};
46 $id_token ||= $c->req->method eq 'GET' ?
47 $c->req->query_params->{id_token} : $c->req->body_params->{id_token};
50 $c->log->debug(Dumper $authinfo);
53 Catalyst::Exception->throw("id_token not specified.");
56 my $userinfo = $self->decode($id_token);
59 $c->log->debug(Dumper $userinfo);
61 my $sub = $userinfo->{sub};
62 my $openid = $userinfo->{openid_id};
65 $c->log->debug($openid);
67 if (!$sub || !$openid) {
68 Catalyst::Exception->throw(
69 'Could not retrieve sub and openid from token! Is the token
74 # Do we have a user with the google id already?
75 my $user = $realm->find_user({
83 # Do we have a user with the openid?
85 $user = $realm->find_user({
90 throw ("Could not find a user with that openid or sub!");
93 my $new_user = $realm->add_user({
95 password => $user->password,
97 active => $user->active,
100 foreach my $t (@{ $user->traditions }) {
101 $new_user->add_tradition($t);
104 warn ($new_user->id);
106 warn (scalar @{$user->traditions});
107 warn (scalar @{$new_user->traditions});
110 warn (Dumper($user->id));
112 $realm->delete_user({ username => $user->id });
120 =head2 retrieve_certs
122 Retrieves a pair of JSON-encoded certificates from the given URL (defaults to
123 Google's public cert url), and returns the decoded JSON object.
131 Optional. Location where certificates are located.
132 Defaults to https://www.googleapis.com/oauth2/v1/certs.
138 Decoded JSON object containing certificates.
143 my ($self, $url) = @_;
145 $url ||= 'https://www.googleapis.com/oauth2/v1/certs';
146 return decode_json(get($url));
149 =head2 get_key_from_cert
151 Given a pair of certificates $certs (defaults to L</retrieve_certs>),
152 this function returns the public key of the cert identified by $kid.
160 Required. Index of the certificate hash $hash where the cert we want is
165 Optional. A (hashref) pair of certificates.
166 It's retrieved using L</retrieve_certs> if not given,
167 or if the pair is expired.
173 Public key of certificate.
177 sub get_key_from_cert {
178 my ($self, $kid, $certs) = @_;
180 $certs ||= $self->retrieve_certs;
181 my $cert = $certs->{$kid};
182 my $x509 = Crypt::OpenSSL::X509->new_from_string($cert);
184 if ($self->is_cert_expired($x509)) {
185 # If we ended up here, we were given
186 # an old $certs string from the user.
187 # Let's force getting another.
188 return $self->get_key_from_cert($kid);
191 return $x509->pubkey;
194 =head2 is_cert_expired
196 Returns if a given L<Crypt::OpenSSL::X509> certificate is expired.
200 sub is_cert_expired {
201 my ($self, $x509) = @_;
203 my $expiry = str2time($x509->notAfter);
205 return time > $expiry;
210 Returns the decoded information contained in a user's token.
218 Required. The user's token from Google+.
222 Optional. A public key string with which to decode the token.
223 If not given, the public key will be retrieved from $certs.
227 Optional. A pair of public key certs retrieved from Google.
228 If not given, or if the certificates have expired, a new
229 pair of certificates is retrieved.
235 Decoded JSON object from the decrypted token.
240 my ($self, $token, $certs, $pubkey) = @_;
243 my $details = decode_json(
244 MIME::Base64::decode_base64(
245 substr( $token, 0, CORE::index($token, '.') )
249 my $kid = $details->{kid};
250 $pubkey = $self->get_key_from_cert($kid, $certs);
253 return JSON::WebToken->decode($token, $pubkey);
258 Errietta Kostala <e.kostala@shadowcat.co.uk>
262 This library is free software. You can redistribute it and/or modify
263 it under the same terms as Perl itself.