File Coverage

blib/lib/Web/Request/Role/JWT.pm
Criterion Covered Total %
statement 38 43 88.3
branch 10 12 83.3
condition 4 6 66.6
subroutine 11 12 91.6
pod 8 8 100.0
total 71 81 87.6


line stmt bran cond sub pod time code
1             package Web::Request::Role::JWT;
2              
3             # ABSTRACT: Accessors for JSON Web Token (JWT) stored in psgix
4              
5             our $VERSION = '1.002'; # VERSION
6              
7 3     3   12656 use 5.010;
  3         18  
8 3     3   18 use Moose::Role;
  3         5  
  3         33  
9 3     3   19226 use HTTP::Throwable::Factory qw(http_throw);
  3         29336  
  3         25  
10 3     3   1212 use Log::Any qw($log);
  3         8612  
  3         27  
11              
12              
13             sub get_jwt {
14 6     6 1 72579 my $self = shift;
15              
16 6         152 return $self->env->{'psgix.token'};
17             }
18              
19              
20             sub get_jwt_claims {
21 15     15 1 22820 my $self = shift;
22              
23 15         354 return $self->env->{'psgix.claims'};
24             }
25              
26              
27             sub get_jwt_claim_sub {
28 6     6 1 20889 my $self = shift;
29              
30 6         21 my $claims = $self->get_jwt_claims;
31 6 100 66     93 return unless $claims && ref($claims) eq 'HASH';
32 4         12 return $claims->{sub};
33             }
34              
35              
36             sub get_jwt_claim_aud {
37 3     3 1 20848 my $self = shift;
38              
39 3         12 my $claims = $self->get_jwt_claims;
40 3 100 66     42 return unless $claims && ref($claims) eq 'HASH';
41 2         8 return $claims->{aud};
42             }
43              
44              
45             sub requires_jwt {
46 3     3 1 20795 my $self = shift;
47              
48 3         20 my $token = $self->get_jwt;
49 3 100       33 return $token if $token;
50              
51 1         8 $log->error("No JWT found in request");
52 1         11 http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
53             }
54              
55              
56             sub requires_jwt_claims {
57 3     3 1 195392 my $self = shift;
58              
59 3         14 my $claims = $self->get_jwt_claims;
60 3 100       95 return $claims if $claims;
61              
62 1         6 $log->error("No claims found in JWT");
63 1         7 http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
64             }
65              
66              
67             sub requires_jwt_claim_sub {
68 3     3 1 31324 my $self = shift;
69              
70 3         14 my $sub = $self->get_jwt_claim_sub;
71              
72 3 100       23 return $sub if $sub;
73              
74 1         15 $log->error("Claim 'sub' not found in JWT");
75 1         8 http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
76             }
77              
78              
79             sub requires_jwt_claim_aud {
80 0     0 1   my $self = shift;
81              
82 0           my $aud = $self->get_jwt_claim_aud;
83              
84 0 0         return $aud if $aud;
85              
86 0           $log->error("Claim 'aud' not found in JWT");
87 0           http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
88             }
89              
90             1;
91              
92             __END__
93              
94             =pod
95              
96             =encoding UTF-8
97              
98             =head1 NAME
99              
100             Web::Request::Role::JWT - Accessors for JSON Web Token (JWT) stored in psgix
101              
102             =head1 VERSION
103              
104             version 1.002
105              
106             =head1 SYNOPSIS
107              
108             # Create a request handler
109             package My::App::Request;
110             use Moose;
111             extends 'Web::Request';
112             with 'Web::Request::Role::JWT';
113              
114             # Finally, in some controller action
115             sub action_that_needs_a_user_stored_in_jwt {
116             my ($self, $req) = @_;
117              
118             my $sub = $req->requires_jwt_claim_sub;
119              
120             my $data = $self->model->do_something( $sub );
121             return $self->json_response( $data );
122             }
123              
124             =head1 DESCRIPTION
125              
126             C<Web::Request::Role::JWT> provides a few accessor and helper methods
127             that make accessing JSON Web Tokens (JWT) stored in your PSGI C<$env>
128             easier.
129              
130             It works especially well when used with
131             L<Plack::Middleware::Auth::JWT>, which will validate the token and
132             extract the payload into the PSGI C<$env>.
133              
134             =head1 METHODS
135              
136             =head2 requires_* and logging
137              
138             If a C<requires_*> method fails, it will log an error via L<Log::Any>.
139              
140             =head2 get_jwt
141              
142             my $raw_token = $req->get_jwt;
143              
144             Returns the raw token, so you can inspect it, or maybe pass it along to some other endpoint.
145              
146             If you want to store your token somewhere else than the default C<<
147             $env->{'psgix.token'} >>, you have to provide another implementation
148             for this method.
149              
150             =head2 get_jwt_claims
151              
152             my $claims = $req->get_jwt_claims;
153              
154             Returns all the claims as a hashref.
155              
156             If you want to store your claims somewhere else than the default C<<
157             $env->{'psgix.claims'} >>, you have to provide another implementation
158             for this method.
159              
160             =head2 get_jwt_claim_sub
161              
162             my $sub = $req->get_jwt_claim_sub;
163              
164             Get the C<sub> claim: L<https://tools.ietf.org/html/rfc7519#section-4.1.2>
165              
166             =head2 get_jwt_claim_aud
167              
168             my $aud = $req->get_jwt_claim_aud;
169              
170             Get the C<aud> claim: L<https://tools.ietf.org/html/rfc7519#section-4.1.3>
171              
172             =head2 requires_jwt
173              
174             my $raw_token = $req->requires_jwt;
175              
176             Returns the raw token. If no token is available, throws a L<HTTP::Throwable::Role::Status::Unauthorized> exception (aka HTTP Status 401)
177              
178             =head2 requires_jwt_claims
179              
180             my $claims = $req->requires_jwt_claims;
181              
182             Returns all the claims as a hashref. If no claims are available, throws a L<HTTP::Throwable::Role::Status::Unauthorized> exception (aka HTTP Status 401)
183              
184             =head2 requires_jwt_claim_sub
185              
186             my $sub = $req->requires_jwt_claim_sub;
187              
188             Returns the C<sub> claim. If the C<sub> claim is missing, throws a L<HTTP::Throwable::Role::Status::Unauthorized> exception (aka HTTP Status 401)
189              
190             =head2 requires_jwt_claim_aud
191              
192             my $aud = $req->requires_jwt_claim_aud;
193              
194             Returns the C<aud> claim. If the C<aud> claim is missing, throws a L<HTTP::Throwable::Role::Status::Unauthorized> exception (aka HTTP Status 401)
195              
196             =head1 THANKS
197              
198             Thanks to
199              
200             =over
201              
202             =item *
203              
204             L<validad.com|https://www.validad.com/> for supporting Open Source.
205              
206             =back
207              
208             =head1 AUTHOR
209              
210             Thomas Klausner <domm@plix.at>
211              
212             =head1 COPYRIGHT AND LICENSE
213              
214             This software is copyright (c) 2017 - 2021 by Thomas Klausner.
215              
216             This is free software; you can redistribute it and/or modify it under
217             the same terms as the Perl 5 programming language system itself.
218              
219             =cut