add test to check that traditions are taken away from the old Google OpenID user
[scpubgit/stemmaweb.git] / lib / Google / JWT.pm
CommitLineData
927341ac 1package Google::JWT;
2
3use Crypt::OpenSSL::X509;
4use JSON::WebToken;
5use IO::All;
6use JSON::MaybeXS;
7use MIME::Base64;
8use LWP::Simple qw(get);
9use Date::Parse qw(str2time);
10
11use warnings;
12use strict;
13use strictures 1;
14
15=head1 NAME
16
17Google::JWT - JSON Web Token handler for Google tokens.
18
19=head1 SYNOPSIS
20
21my $tok = Google->JWT->decode($token);
22
23=head1 DESCRIPTION
24
25Retrieves Google's public certificates, and then retrieves the key from the
26cert using L<Crypt::OpenSSL::X509>. Finally, uses the pubkey to decrypt a
27Google token using L<JSON::WebToken>.
28
29=cut
30
31=head1 METHODS
32
33=head2 retrieve_certs
34
35Retrieves a pair of JSON-encoded certificates from the given URL (defaults to
36Google's public cert url), and returns the decoded JSON object.
37
38=head3 ARGUMENTS
39
40=over
41
42=item url
43
44Optional. Location where certificates are located.
45Defaults to https://www.googleapis.com/oauth2/v1/certs.
46
47=back
48
49=head3 RETURNS
50
51Decoded JSON object containing certificates.
52
53=cut
54
55sub retrieve_certs {
56 my ($self, $url) = @_;
57
58 $url ||= 'https://www.googleapis.com/oauth2/v1/certs';
59 return decode_json(get($url));
60}
61
62=head2 get_key_from_cert
63
64Given a pair of certificates $certs (defaults to L</retrieve_certs>),
65this function returns the public key of the cert identified by $kid.
66
67=head3 ARGUMENTS
68
69=over
70
71=item $kid
72
73Required. Index of the certificate hash $hash where the cert we want is
74located.
75
76=item $certs
77
78Optional. A (hashref) pair of certificates.
79It's retrieved using L</retrieve_certs> if not given,
80or if the pair is expired.
81
82=back
83
84=head3 RETURNS
85
86Public key of certificate.
87
88=cut
89
90sub get_key_from_cert {
91 my ($self, $kid, $certs) = @_;
92
93 $certs ||= $self->retrieve_certs;
94 my $cert = $certs->{$kid};
95 my $x509 = Crypt::OpenSSL::X509->new_from_string($cert);
96
97 if ($self->is_cert_expired($x509)) {
98 # If we ended up here, we were given
99 # an old $certs string from the user.
100 # Let's force getting another.
101 return $self->get_key_from_cert($kid);
102 }
103
104 return $x509->pubkey;
105}
106
107=head2 is_cert_expired
108
109Returns if a given L<Crypt::OpenSSL::X509> certificate is expired.
110
111=cut
112
113sub is_cert_expired {
114 my ($self, $x509) = @_;
115
116 my $expiry = str2time($x509->notAfter);
117
118 return time > $expiry;
119}
120
121=head2 decode
122
123Returns the decoded information contained in a user's token.
124
125=head3 ARGUMENTS
126
127=over
128
129=item $token
130
131Required. The user's token from Google+.
132
133=item $pubkey
134
135Optional. A public key string with which to decode the token.
136If not given, the public key will be retrieved from $certs.
137
138=item $certs
139
140Optional. A pair of public key certs retrieved from Google.
141If not given, or if the certificates have expired, a new
142pair of certificates is retrieved.
143
144=back
145
146=head2 RETURNS
147
148Decoded JSON object from the decrypted token.
149
150=cut
151
152sub decode {
153 my ($self, $token, $certs, $pubkey) = @_;
154
155 if (!$pubkey) {
156 my $details = decode_json(
157 MIME::Base64::decode_base64(
158 substr( $token, 0, CORE::index($token, '.') )
159 )
160 );
161
162 my $kid = $details->{kid};
163 $pubkey = $self->get_key_from_cert($kid, $certs);
164 }
165
166 return JSON::WebToken->decode($token, $pubkey);
167}
168
169=head1 AUTHOR
170
171Errietta Kostala <e.kostala@shadowcat.co.uk>
172
173=head1 LICENSE
174
175This library is free software. You can redistribute it and/or modify
176it under the same terms as Perl itself.
177
178=cut
179
1801;