Catalyst-Plugin-OAuth2-ResourceServer
view release on metacpan or search on metacpan
t/catalyst-app.t view on Meta::CPAN
use v5.36;
use Test::More;
use lib 't/lib';
use HTTP::Request::Common qw/GET/;
use JSON::MaybeXS qw/decode_json/;
use Crypt::JWT qw/encode_jwt/;
use Catalyst::Test 'TestApp';
my $KEY = 'resource-server-secret-key-0123456789';
sub token (%claims) {
return encode_jwt(
payload => {
sub => 'user-1', aud => 'https://rs/mcp', iss => 'https://as',
scope => 'example:read', exp => time + 60, %claims,
},
alg => 'HS256', key => $KEY,
);
}
sub authget ( $path, $tok ) {
my @h = defined $tok ? ( Authorization => "Bearer $tok" ) : ();
return request( GET $path, @h );
}
# --- protected route: valid token passes, identity + scope available ---
{
my $res = authget( '/secure', token() );
is( $res->code, 200, 'valid bearer -> 200' );
is( $res->content, 'ok:user-1:example:read', 'identity + scope visible to action' );
}
# --- the Bearer scheme name is case-insensitive (RFC 7235) ---
{
my $res = request( GET '/secure', Authorization => 'bearer ' . token() );
is( $res->code, 200, 'lowercase "bearer" accepted' );
}
# --- missing token -> 401 plain challenge (no error=) + resource_metadata ---
{
my $res = authget( '/secure', undef );
is( $res->code, 401, 'missing token -> 401' );
my $wa = $res->header('WWW-Authenticate');
like( $wa, qr/^Bearer/, 'Bearer challenge' );
like( $wa, qr/resource_metadata="[^"]+"/, 'carries resource_metadata' );
# TestApp configures resource_metadata_url explicitly; the override is used
# verbatim rather than derived from the resource (which would have given
# https://rs/.well-known/oauth-protected-resource/mcp).
like( $wa, qr{resource_metadata="\Qhttps://rs/.well-known/oauth-protected-resource\E"},
'configured resource_metadata_url wins over derivation' );
unlike( $wa, qr/error=/, 'no error= for a missing token' );
is( $res->header('Cache-Control'), 'no-store', 'no-store' );
}
# --- bad token -> 401 with error="invalid_token" ---
{
my $res = authget( '/secure', 'garbage' );
is( $res->code, 401, 'bad token -> 401' );
like( $res->header('WWW-Authenticate'), qr/error="invalid_token"/, 'invalid_token' );
}
# --- resolver rejects (subject not active) -> 401 ---
{
my $res = authget( '/secure', token( sub => 'inactive' ) );
( run in 2.024 seconds using v1.01-cache-2.11-cpan-f0ff5d10edf )