File Coverage

blib/lib/Mojo/JWT/Google.pm
Criterion Covered Total %
statement 54 56 96.4
branch 22 24 91.6
condition 2 3 66.6
subroutine 9 10 90.0
pod 3 4 75.0
total 90 97 92.7


line stmt bran cond sub pod time code
1             package Mojo::JWT::Google;
2 1     1   229935 use utf8;
  1         8  
  1         6  
3 1     1   548 use Mojo::Base qw(Mojo::JWT);
  1         160496  
  1         6  
4 1     1   28020 use Mojo::File qw(path);
  1         33500  
  1         70  
5 1     1   9 use Mojo::JSON qw(decode_json);
  1         2  
  1         43  
6 1     1   8 use Carp;
  1         2  
  1         787  
7              
8             our $VERSION = '0.15';
9              
10             has client_email => undef;
11             has expires_in => 3600;
12             has issue_at => undef;
13             has scopes => sub { [] };
14             has target => q(https://www.googleapis.com/oauth2/v4/token);
15             has user_as => undef;
16             has audience => undef;
17              
18             sub new {
19 8     8 1 11725 my ($class, %options) = @_;
20 8         40 my $self = $class->SUPER::new(%options);
21 8 100       106 return $self if not defined $self->{from_json};
22              
23 2         9 my $result = $self->from_json($self->{from_json});
24              
25 1         5 return $self;
26             }
27              
28             sub claims {
29 7     7 1 5960 my ($self, $value) = @_;
30 7 100       21 if (defined $value) {
31 1         2 $self->{claims} = $value;
32 1         4 return $self;
33             }
34 6         15 my $claims = $self->_construct_claims;
35 6 100       17 unless (exists $claims->{exp}) {
36 3         28 $claims->{exp} = $self->now + $self->expires_in ;
37             }
38 6         45 return $claims;
39             }
40              
41             sub _construct_claims {
42 6     6   12 my $self = shift;
43 6         9 my $result = {};
44 6         34 $result->{iss} = $self->client_email;
45 6         45 $result->{aud} = $self->target;
46 6 100       33 $result->{sub} = $self->user_as if defined $self->user_as;
47 6         33 my @scopes = @{ $self->scopes };
  6         13  
48              
49 6 50 66     42 croak "Can't use both scopes and audience in the same token" if @scopes && $self->audience;
50 6 100       35 $result->{scope} = join ' ', @scopes if @scopes;
51 6 50       13 $result->{target_audience} = $self->audience if defined $self->audience;
52              
53 6 100       30 if ( not defined $self->issue_at ) {
54 3         14 $self->set_iat(1);
55             }
56             else {
57 3         24 $self->set_iat(0);
58 3         18 $result->{iat} = $self->issue_at;
59 3         13 $result->{exp} = $self->issue_at + $self->expires_in;
60             }
61 6         48 return $result;
62             }
63              
64             sub from_json {
65 6     6 1 2693 my ($self, $value) = @_;
66 6 100       138 croak 'You did not pass a filename to from_json' if not defined $value;
67 5 100       543 croak 'Cannot find file passed to from_json' if not -f $value;
68 3         19 my $json = decode_json( path($value)->slurp );
69 3 100       1770 croak 'private key was not found in file passed to from_json' unless $json->{private_key};
70 2 100       101 croak 'from_json only works with service accounts' if $json->{type} ne 'service_account';
71 1         15 $self->algorithm('RS256');
72 1         14 $self->secret($json->{private_key});
73 1         8 $self->client_email($json->{client_email});
74 1         8 return 1
75             }
76              
77             sub as_form_data {
78 0     0 0   my ($self) = @_;
79             return {
80 0           grant_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
81             assertion => $self->encode
82             }
83             }
84              
85             1;
86              
87              
88             =head1 NAME
89              
90             Mojo::JWT::Google - Service Account tokens
91              
92             =head1 VERSION
93              
94             version 0.15
95              
96             =head1 SYNOPSIS
97              
98             my $gjwt = Mojo::JWT::Google->new(secret => 's3cr3t',
99             scopes => [ '/my/scope/a', '/my/scope/b' ],
100             client_email => 'riche@cpan.org')->encode;
101              
102             # authenticating for apis as a service account
103             my $gjwt = Mojo::JWT::Google->new(
104             from_json => '/my/secret/project-b98ale897.json',
105             scopes => 'https://www.googleapis.com/auth/gmail.send',
106             user_as => 'some-email@your-org.com'); # if you have domain-wide delegation
107             my $ua = Mojo::UserAgent->new;
108             my $tx = $ua->post('https://www.googleapis.com/oauth2/v4/token', form => $gjwt->as_form_data);
109             $tx->res->json('/access_token') # will contain your access token
110            
111             # authenticating to use the Identity Aware Proxy
112             my $gjwt = Mojo::JWT::Google->new(
113             from_json => '/my/secret/project-b98ale897.json',
114             audience => 'the-client-id-from-your-IAP');
115             my $ua = Mojo::UserAgent->new;
116             my $tx = $ua->post('https://www.googleapis.com/oauth2/v4/token', form => $gjwt->as_form_data);
117             $tx->res->json('/id_token') # will contain your id token
118              
119             =head1 DESCRIPTION
120              
121             Like L, you can instantiate this class by using the same syntax,
122             except that this class constructs the claims for you.
123              
124             my $jwt = Mojo::JWT::Google->new(secret => 's3cr3t')->encode;
125              
126             And add any attribute defined in this class. The JWT is fairly useless unless
127             you define your scopes.
128              
129             my $gjwt = Mojo::JWT::Google->new(secret => 's3cr3t',
130             scopes => [ '/my/scope/a', '/my/scope/b' ],
131             client_email => 'riche@cpan.org')->encode;
132              
133             You can also get your information automatically from the .json you received
134             from Google. Your secret key is in that file, so it's best to keep it safe
135             somewhere. This will ease some busy work in configuring the object -- with
136             virtually the only things to do is determine the scopes and the user_as if you
137             need to impersonate.
138              
139             my $gjwt = Mojo::JWT::Google
140             ->new( from_json => '/my/secret.json',
141             scopes => [ '/my/scope/a', '/my/scope/b' ])->encode;
142              
143              
144             To authenticate, send a post request to https://www.googleapis.com/oauth2/v4/token,
145             with your Mojo::JWT::Google's as_form_data method as the payload.
146              
147             $ua->post('https://www.googleapis.com/oauth2/v4/token', form => $gjwt->as_form_data);
148              
149             =cut
150              
151             =head1 ATTRIBUTES
152              
153             L inherits all attributes from L and defines the
154             following new ones.
155              
156             =head2 claims
157              
158             Overrides the parent class and constructs a hashref representing Google's
159             required attribution.
160              
161              
162             =head2 client_email
163              
164             Get or set the Client ID email address.
165              
166             =head2 expires_in
167              
168             Defines the threshold for when the token expires. Defaults to 3600.
169              
170             =head2 issue_at
171              
172             Defines the time of issuance in epoch seconds. If not defined, the claims issue
173             at date defaults to the time when it is being encoded.
174              
175             =head2 scopes
176              
177             Get or set the Google scopes. If impersonating, these scopes must be set up by
178             your Google Business Administrator.
179              
180             =head2 target
181              
182             Get or set the target. At the time of writing, there is only one valid target:
183             https://www.googleapis.com/oauth2/v4/token. This is the default value; if you
184             have no need to customize this, then just fetch the default.
185              
186              
187             =head2 user_as
188              
189             Set the Google user to impersonate. Your Google Business Administrator must
190             have already set up your Client ID as a trusted app in order to use this
191             successfully.
192              
193             =cut
194              
195             =head1 METHODS
196              
197             Inherits all methods from L and defines the following new ones.
198              
199             =head2 from_json
200              
201             Loads the JSON file from Google with the client ID information in it and sets
202             the respective attributes.
203              
204             Dies on failure: file not found or value not defined
205              
206             $gjwt->from_json('/my/google/app/project/sa/json/file');
207              
208              
209             =head1 SEE ALSO
210              
211             L
212              
213             =head1 SOURCE REPOSITORY
214              
215             L
216              
217             =head1 AUTHOR
218              
219             Richard Elberger,
220              
221             =head1 CONTRIBUTORS
222              
223             Scott Wiersdorf,
224             Avishai Goldman,
225              
226             =head1 COPYRIGHT AND LICENSE
227              
228             Copyright (C) 2015 by Richard Elberger
229              
230             This library is free software; you can redistribute it and/or modify it
231             under the same terms as Perl itself.