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