Web-Request-Role-JWT/lib/Web/Request/Role/JWT.pm
package Web::Request::Role::JWT;
# ABSTRACT: Accessors for JSON Web Token (JWT) stored in psgix
our $VERSION = '1.003'; # VERSION
use 5.010;
use Moose::Role;
use HTTP::Throwable::Factory qw(http_throw);
use Log::Any qw($log);
sub get_jwt {
my $self = shift;
return $self->env->{'psgix.token'};
}
sub get_jwt_claims {
my $self = shift;
return $self->env->{'psgix.claims'};
}
sub get_jwt_claim_sub {
my $self = shift;
my $claims = $self->get_jwt_claims;
return unless $claims && ref($claims) eq 'HASH';
return $claims->{sub};
}
sub get_jwt_claim_aud {
my $self = shift;
my $claims = $self->get_jwt_claims;
return unless $claims && ref($claims) eq 'HASH';
return $claims->{aud};
}
sub requires_jwt {
my $self = shift;
my $token = $self->get_jwt;
return $token if $token;
$log->error("No JWT found in request");
http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
}
sub requires_jwt_claims {
my $self = shift;
my $claims = $self->get_jwt_claims;
return $claims if $claims;
$log->error("No claims found in JWT");
http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
}
sub requires_jwt_claim_sub {
my $self = shift;
my $sub = $self->get_jwt_claim_sub;
return $sub if $sub;
$log->error("Claim 'sub' not found in JWT");
http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
}
sub requires_jwt_claim_aud {
my $self = shift;
my $aud = $self->get_jwt_claim_aud;
return $aud if $aud;
$log->error("Claim 'aud' not found in JWT");
http_throw( 'Unauthorized' => { www_authenticate => 'bearer' } );
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Web::Request::Role::JWT - Accessors for JSON Web Token (JWT) stored in psgix
=head1 VERSION
version 1.003
=head1 SYNOPSIS
# Create a request handler
package My::App::Request;
use Moose;
extends 'Web::Request';
with 'Web::Request::Role::JWT';
# Finally, in some controller action
sub action_that_needs_a_user_stored_in_jwt {
my ($self, $req) = @_;
my $sub = $req->requires_jwt_claim_sub;
my $data = $self->model->do_something( $sub );
return $self->json_response( $data );
}
=head1 DESCRIPTION
C<Web::Request::Role::JWT> provides a few accessor and helper methods
that make accessing JSON Web Tokens (JWT) stored in your PSGI C<$env>
easier.
It works especially well when used with
L<Plack::Middleware::Auth::JWT>, which will validate the token and
extract the payload into the PSGI C<$env>.
=head1 METHODS
=head2 requires_* and logging
If a C<requires_*> method fails, it will log an error via L<Log::Any>.
=head2 get_jwt
my $raw_token = $req->get_jwt;
Returns the raw token, so you can inspect it, or maybe pass it along to some other endpoint.
If you want to store your token somewhere else than the default C<<
$env->{'psgix.token'} >>, you have to provide another implementation
for this method.
=head2 get_jwt_claims
my $claims = $req->get_jwt_claims;
Returns all the claims as a hashref.
If you want to store your claims somewhere else than the default C<<
$env->{'psgix.claims'} >>, you have to provide another implementation
for this method.
=head2 get_jwt_claim_sub
my $sub = $req->get_jwt_claim_sub;
Get the C<sub> claim: L<https://tools.ietf.org/html/rfc7519#section-4.1.2>
=head2 get_jwt_claim_aud
my $aud = $req->get_jwt_claim_aud;
Get the C<aud> claim: L<https://tools.ietf.org/html/rfc7519#section-4.1.3>
=head2 requires_jwt
my $raw_token = $req->requires_jwt;
Returns the raw token. If no token is available, throws a L<HTTP::Throwable::Role::Status::Unauthorized> exception (aka HTTP Status 401)
=head2 requires_jwt_claims
my $claims = $req->requires_jwt_claims;
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)
=head2 requires_jwt_claim_sub
my $sub = $req->requires_jwt_claim_sub;
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)
=head2 requires_jwt_claim_aud
my $aud = $req->requires_jwt_claim_aud;
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)
=head1 THANKS
Thanks to
=over
=item *
L<validad.com|https://www.validad.com/> for supporting Open Source.
=back
=head1 AUTHOR
Thomas Klausner <domm@plix.at>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2017 - 2021 by Thomas Klausner.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut