Catalyst-Plugin-OAuth2-ResourceServer
view release on metacpan or search on metacpan
t/server-verify.t view on Meta::CPAN
use v5.36;
use Test::More;
use Test::Fatal;
use Crypt::JWT qw/encode_jwt/;
my $class = 'Catalyst::Plugin::OAuth2::ResourceServer::Server';
require_ok($class);
my $KEY = 'resource-server-secret-key-0123456789';
sub engine (%over) {
return $class->new(
signing_key => $KEY,
resource => 'https://rs.example/mcp',
issuer => 'https://as.example',
%over,
);
}
# a helper to mint a token the way the AS would
sub token (%claims) {
return encode_jwt(
payload => {
sub => 'user-1',
aud => 'https://rs.example/mcp',
iss => 'https://as.example',
scope => 'example:read',
exp => time + 60,
%claims,
},
alg => 'HS256',
key => $KEY,
);
}
# BUILD validation
like( exception { engine( resource => [] ) }, qr/resource/, 'empty resource rejected' );
like( exception { engine( jwt_alg => 'RS256' ) }, qr/jwt_alg/, 'non-HS alg rejected' );
like(
exception {
$class->new( signing_key => 'short', resource => 'https://rs.example/mcp',
issuer => 'https://as.example' );
},
qr/signing_key must be at least/,
'short signing_key rejected'
);
# happy path -> claims
{
my $claims = engine->verify_token( token() );
is( $claims->{sub}, 'user-1', 'sub claim' );
is( $claims->{scope}, 'example:read', 'scope claim' );
}
# array aud containing our resource is accepted
{
my $jwt = token( aud => [ 'https://other', 'https://rs.example/mcp' ] );
ok( engine->verify_token($jwt), 'array aud incl. our resource accepted' );
}
my %fail = (
expired => token( exp => time - 10 ),
wrong_aud => token( aud => 'https://other/api' ),
( run in 1.079 second using v1.01-cache-2.11-cpan-54e63673c56 )