File Coverage

blib/lib/Plack/Middleware/Auth/JWT.pm
Criterion Covered Total %
statement 64 66 96.9
branch 38 42 90.4
condition 5 5 100.0
subroutine 11 11 100.0
pod 2 3 66.6
total 120 127 94.4


line stmt bran cond sub pod time code
1             package Plack::Middleware::Auth::JWT;
2              
3             # ABSTRACT: Token-based Auth (aka Bearer Token) using JSON Web Tokens (JWT)
4             our $VERSION = '0.906'; # VERSION
5              
6 2     2   3230 use 5.010;
  2         9  
7 2     2   8 use strict;
  2         4  
  2         33  
8 2     2   7 use warnings;
  2         4  
  2         44  
9 2     2   8 use parent qw(Plack::Middleware);
  2         3  
  2         11  
10 2     2   129 use Plack::Util;
  2         3  
  2         57  
11             use Plack::Util::Accessor
12 2     2   9 qw(decode_args decode_callback psgix_claims psgix_token token_required ignore_invalid_token token_header_name token_query_name);
  2         10  
  2         11  
13 2     2   1044 use Plack::Request;
  2         101900  
  2         66  
14 2     2   552 use Crypt::JWT 0.020 qw(decode_jwt);
  2         44865  
  2         897  
15              
16             sub prepare_app {
17 10     10 1 31383 my $self = shift;
18              
19             # some defaults
20 10 100       27 $self->psgix_claims('claims') unless $self->psgix_claims;
21 10 100       163 $self->psgix_token('token') unless $self->psgix_token;
22              
23 10 100       76 $self->token_header_name('bearer')
24             unless defined $self->token_header_name;
25 10 100       72 $self->token_header_name(undef) unless $self->token_header_name;
26 10 100       46 $self->token_query_name('token') unless defined $self->token_query_name;
27 10 100       68 $self->token_query_name(undef) unless $self->token_query_name;
28 10 100       53 $self->token_required(0) unless defined $self->token_required;
29 10 100       65 $self->ignore_invalid_token(0) unless defined $self->ignore_invalid_token;
30              
31             # either decode_args or decode_callback is required
32 10 100       81 if ( my $cb = $self->decode_callback ) {
    50          
33 5 50       37 die "decode_callback must be a code reference"
34             unless ref($cb) eq 'CODE';
35             }
36             elsif ( my $args = $self->decode_args ) {
37 5         41 $args->{decode_payload} = 1;
38 5         10 $args->{decode_header} = 0;
39 5 100       12 $args->{verify_exp} = 1 unless exists $args->{verify_exp};
40 5 50       14 $args->{leeway} = 5 unless exists $args->{leeway};
41             }
42             else {
43 0         0 die
44             "Either decode_callback or decode_args has to be defined when loading this Middleware";
45             }
46             }
47              
48             sub call {
49 30     30 1 127452 my ( $self, $env ) = @_;
50              
51 30         45 my $token;
52              
53 30 100 100     67 if ( $self->token_header_name && $env->{HTTP_AUTHORIZATION} ) {
    100          
54 15         121 my $name = $self->token_header_name;
55 15         55 my $auth = $env->{HTTP_AUTHORIZATION};
56 15 50       169 $token = $1 if $auth =~ /^\s*$name\s+(.+)/i;
57             }
58             elsif ( my $name = $self->token_query_name ) {
59 13         176 my $req = Plack::Request->new($env);
60 13         109 $token = $req->query_parameters->get($name);
61             }
62              
63 30 100       944 unless ($token) {
64 8 100       29 return $self->unauthorized if $self->token_required;
65              
66             # no token found, but non required, so just call the app
67 2         15 return $self->app->($env);
68             }
69              
70 22         31 my $claims = eval {
71 22 100       42 if ( my $cb = $self->decode_callback ) {
72 8         43 return $cb->( $token, $env );
73             }
74             else {
75 14         53 return decode_jwt( token => $token, %{ $self->decode_args } );
  14         23  
76             }
77             };
78 22 100       2550 if ($@) {
79 9 100       23 if ($self->ignore_invalid_token) {
80 1         6 return $self->app->($env);
81             }
82             else {
83             # TODO hm, if token cannot be decoded: 401 or 400?
84 8         49 return $self->unauthorized( 'Cannot decode JWT: ' . $@ );
85             }
86             }
87             else {
88 13         29 $env->{ 'psgix.' . $self->psgix_token } = $token;
89 13         65 $env->{ 'psgix.' . $self->psgix_claims } = $claims;
90 13         63 return $self->app->($env);
91             }
92              
93             # should never be reached, but just to make sure...
94 0         0 return $self->unauthorized;
95             }
96              
97             sub unauthorized {
98 14     14 0 45 my $self = shift;
99 14   100     34 my $body = shift || 'Authorization required';
100              
101             return [
102 14         101 401,
103             [ 'Content-Type' => 'text/plain',
104             'Content-Length' => length $body
105             ],
106             [$body]
107             ];
108             }
109              
110             1;
111              
112             __END__