File Coverage

blib/lib/Mojo/JWT/Google.pm
Criterion Covered Total %
statement 54 54 100.0
branch 22 24 91.6
condition 2 3 66.6
subroutine 9 9 100.0
pod 3 3 100.0
total 90 93 96.7


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