OIDC-Client
view release on metacpan or search on metacpan
'expected call to user agent');
};
subtest "get_token() authorization_code - client_secret_jwt auth method" => sub {
# Given
$test->mock_encode_jwt(); # encode_jwt() args are placed directly into 'client_assertion'
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
token_response_parser => $test->mocked_token_response_parser,
kid_keys => {},
config => {
provider => 'my_provider',
id => 'my_client_id',
secret => 'my_client_secret',
signin_redirect_uri => 'my_signin_redirect_uri',
client_auth_method => 'client_secret_jwt',
},
provider_metadata => { token_url => 'https://my-provider/token' },
);
# When
my $token_response = $client->get_token(
code => 'my_code',
);
# Then
is($token_response->access_token, 'my_access_token',
'expected access token');
my %expected_encode_jwt_args = (
alg => 'HS256',
key => 'my_client_secret',
payload => {
iss => 'my_client_id',
sub => 'my_client_id',
aud => 'https://my-provider/token',
jti => re('\w+'),
iat => re('\d+'),
exp => re('\d+'),
},
);
my %expected_args = (
grant_type => 'authorization_code',
code => 'my_code',
redirect_uri => 'my_signin_redirect_uri',
client_id => 'my_client_id',
client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion => \%expected_encode_jwt_args,
);
my %expected_headers = ();
my @user_agent_sended_args = $test->mocked_user_agent->next_call();
cmp_deeply(\@user_agent_sended_args,
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/token', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
my $client_assertion_sended_claims = $user_agent_sended_args[1][4]{client_assertion}{payload};
is($client_assertion_sended_claims->{exp}, $client_assertion_sended_claims->{iat} + 120,
'expected exp claim value');
};
subtest "get_token() authorization_code - private_key_jwt auth method" => sub {
# Given
$test->mock_encode_jwt(); # encode_jwt() args are placed directly into 'client_assertion'
my $private_jwk = { kty => 'FAKE' };
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
token_response_parser => $test->mocked_token_response_parser,
kid_keys => {},
config => {
provider => 'my_provider',
id => 'my_client_id',
private_jwk => $private_jwk,
signin_redirect_uri => 'my_signin_redirect_uri',
client_auth_method => 'private_key_jwt',
},
provider_metadata => { token_url => 'https://my-provider/token' },
);
# When
my $token_response = $client->get_token(
code => 'my_code',
);
# Then
is($token_response->access_token, 'my_access_token',
'expected access token');
my %expected_encode_jwt_args = (
alg => 'RS256',
key => $private_jwk,
payload => {
iss => 'my_client_id',
sub => 'my_client_id',
aud => 'https://my-provider/token',
jti => re('\w+'),
iat => re('\d+'),
exp => re('\d+'),
},
);
my %expected_args = (
grant_type => 'authorization_code',
code => 'my_code',
redirect_uri => 'my_signin_redirect_uri',
client_id => 'my_client_id',
client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion => \%expected_encode_jwt_args,
);
my %expected_headers = ();
my @user_agent_sended_args = $test->mocked_user_agent->next_call();
cmp_deeply(\@user_agent_sended_args,
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/token', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
my $client_assertion_sended_claims = $user_agent_sended_args[1][4]{client_assertion}{payload};
is($client_assertion_sended_claims->{exp}, $client_assertion_sended_claims->{iat} + 120,
'expected exp claim value');
};
}
sub test_get_token_client_credentials {
# Prepare
$test->mock_user_agent(
to_mock => {
post => { access_token => 'my_access_token' },
}
);
$test->mock_token_response_parser();
subtest "get_token() client_credentials grant type" => sub {
# Given
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
token_response_parser => $test->mocked_token_response_parser,
);
# Then
is($token_response->access_token, 'my_access_token',
'expected access token');
my %expected_args = (
grant_type => 'refresh_token',
client_id => 'my_client_id',
client_secret => 'my_client_secret',
refresh_token => 'my_refresh_token',
scope => 'my_refresh_scope',
);
cmp_deeply([ $test->mocked_user_agent->next_call() ],
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/token', {}, 'form', \%expected_args ] ],
'expected call to user agent');
};
subtest "get_token() refresh_token grant type with client_secret_basic auth" => sub {
# Given
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
token_response_parser => $test->mocked_token_response_parser,
kid_keys => {},
config => {
provider => 'my_provider',
id => 'my_client_id',
secret => 'my_client_secret',
token_endpoint_grant_type => 'client_credentials',
token_endpoint_auth_method => 'client_secret_basic',
scope => 'my_scope',
audience => 'my_audience',
},
provider_metadata => { token_url => 'https://my-provider/token' },
);
# When
my $token_response = $client->get_token(
grant_type => 'refresh_token',
refresh_token => 'my_refresh_token',
);
# Then
is($token_response->access_token, 'my_access_token',
'expected access token');
my %expected_args = (
grant_type => 'refresh_token',
refresh_token => 'my_refresh_token',
);
my %expected_headers = (
Authorization => 'Basic bXlfY2xpZW50X2lkOm15X2NsaWVudF9zZWNyZXQ=',
);
cmp_deeply([ $test->mocked_user_agent->next_call() ],
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/token', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
};
subtest "get_token() refresh_token grant type with private_key_jwt auth method" => sub {
# Given
$test->mock_encode_jwt(); # encode_jwt() args are placed directly into 'client_assertion'
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
token_response_parser => $test->mocked_token_response_parser,
kid_keys => {},
config => {
provider => 'my_provider',
id => 'my_client_id',
private_key_file => "$Bin/resources/client.key",
token_endpoint_grant_type => 'client_credentials',
token_endpoint_auth_method => 'private_key_jwt',
scope => 'my_scope',
},
provider_metadata => { token_url => 'https://my-provider/token' },
);
# When
my $token_response = $client->get_token(
grant_type => 'refresh_token',
refresh_token => 'my_refresh_token',
);
# Then
is($token_response->access_token, 'my_access_token',
'expected access token');
my $expected_private_key = "FAKE PRIVATE KEY\n";
my %expected_encode_jwt_args = (
alg => 'RS256',
key => \$expected_private_key,
payload => {
iss => 'my_client_id',
sub => 'my_client_id',
aud => 'https://my-provider/token',
jti => re('\w+'),
iat => re('\d+'),
exp => re('\d+'),
},
);
my %expected_args = (
grant_type => 'refresh_token',
refresh_token => 'my_refresh_token',
client_id => 'my_client_id',
client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion => \%expected_encode_jwt_args,
);
my %expected_headers = ();
cmp_deeply([ $test->mocked_user_agent->next_call() ],
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/token', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
};
}
sub test_verify_jwt_token {
# Prepare
my $client = $class->new(
log => $log,
kid_keys => {},
config => {
provider => 'my_provider',
id => 'my_client_id',
secret => 'my_client_secret',
},
provider_metadata => { issuer => 'my_issuer' },
);
subtest "verify_jwt_token() no 'iss' claim" => sub {
# Given
$test->mock_decode_jwt(
claims => {}
);
# When - Then
throws_ok {
$client->verify_jwt_token(
token => 'my_token',
);
} qr/OIDC: 'iss' claim is missing/,
'exception is thrown';
isa_ok($@, 'OIDC::Client::Error::TokenValidation');
};
subtest "verify_jwt_token() 'iss' is different from the expected issuer" => sub {
# Given
$test->mock_decode_jwt(
claims => {
iss => 'other_issuer',
# Given
my %returned_claims = (
active => 1,
);
$test->mock_user_agent(to_mock => { post => \%returned_claims });
$test->mock_response_parser();
$test->mock_encode_jwt(); # encode_jwt() args are placed directly into 'client_assertion'
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
response_parser => $test->mocked_response_parser,
config => {
provider => 'my_provider',
id => 'my_client_id',
secret => 'my_client_secret',
client_auth_method => 'client_secret_jwt',
client_assertion_lifetime => 40,
},
provider_metadata => { issuer => 'my_issuer',
token_url => 'https://my-provider/token',
introspection_url => 'https://my-provider/introspect' },
);
# When
my $claims = $client->introspect_token(
token => 'opaque_token',
);
# Then
cmp_deeply($claims, \%returned_claims,
'expected claims');
my %expected_encode_jwt_args = (
alg => 'HS256',
key => 'my_client_secret',
payload => {
iss => 'my_client_id',
sub => 'my_client_id',
aud => 'https://my-provider/introspect',
jti => re('\w+'),
iat => re('\d+'),
exp => re('\d+'),
},
);
my %expected_args = (
token => 'opaque_token',
client_id => 'my_client_id',
client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion => \%expected_encode_jwt_args,
);
my %expected_headers = ();
my @user_agent_sended_args = $test->mocked_user_agent->next_call();
cmp_deeply(\@user_agent_sended_args,
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/introspect', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
my $client_assertion_sended_claims = $user_agent_sended_args[1][4]{client_assertion}{payload};
is($client_assertion_sended_claims->{exp}, $client_assertion_sended_claims->{iat} + 40,
'expected exp claim value');
};
subtest "introspect_token() - 'private_key_jwt' auth method" => sub {
# Given
my %returned_claims = (
active => 1,
);
$test->mock_user_agent(to_mock => { post => \%returned_claims });
$test->mock_response_parser();
$test->mock_encode_jwt(); # encode_jwt() args are placed directly into 'client_assertion'
my $private_key = 'FAKE_PRIVATE_KEY';
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
response_parser => $test->mocked_response_parser,
config => {
provider => 'my_provider',
id => 'my_client_id',
private_key => $private_key,
client_auth_method => 'private_key_jwt',
},
provider_metadata => { issuer => 'my_issuer',
token_url => 'https://my-provider/token',
introspection_url => 'https://my-provider/introspect' },
);
# When
my $claims = $client->introspect_token(
token => 'opaque_token',
);
# Then
cmp_deeply($claims, \%returned_claims,
'expected claims');
my %expected_encode_jwt_args = (
alg => 'RS256',
key => \$private_key,
payload => {
iss => 'my_client_id',
sub => 'my_client_id',
aud => 'https://my-provider/introspect',
jti => re('\w+'),
iat => re('\d+'),
exp => re('\d+'),
},
);
my %expected_args = (
token => 'opaque_token',
client_id => 'my_client_id',
client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion => \%expected_encode_jwt_args,
);
my %expected_headers = ();
my @user_agent_sended_args = $test->mocked_user_agent->next_call();
cmp_deeply(\@user_agent_sended_args,
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/introspect', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
};
subtest "introspect_token() - 'none' auth method - inactive token" => sub {
# Given
my %returned_claims = (
active => 0,
);
$test->mock_user_agent(to_mock => { post => \%returned_claims });
$test->mock_response_parser();
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
response_parser => $test->mocked_response_parser,
config => {
provider => 'my_provider',
id => 'my_client_id',
client_auth_method => 'none',
},
provider_metadata => { issuer => 'my_issuer',
introspection_url => 'https://my-provider/introspect' },
);
# When - Then
throws_ok {
$client->introspect_token(token => 'opaque_token');
} qr/OIDC: inactive token/,
'exception is thrown';
isa_ok($@, 'OIDC::Client::Error::TokenValidation');
my %expected_args = (
token => 'opaque_token',
client_id => 'my_client_id',
);
my %expected_headers = ();
cmp_deeply([ $test->mocked_user_agent->next_call() ],
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/introspect', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
};
subtest "introspect_token() - 'iss' is different from the expected issuer" => sub {
subtest "exchange_token() - client_secret_jwt auth method" => sub {
# Given
$test->mock_encode_jwt(); # encode_jwt() args are placed directly into 'client_assertion'
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
token_response_parser => $test->mocked_token_response_parser,
config => {
provider => 'my_provider',
id => 'my_client_id',
secret => 'my_client_secret',
client_auth_method => 'client_secret_jwt',
client_assertion_audience => 'my_client_assertion_audience',
audience_alias => {
my_alias => {
audience => 'my_audience',
},
},
},
provider_metadata => { token_url => 'https://my-provider/token' },
);
# When
my $exchanged_token = $client->exchange_token(
token => 'my_token',
audience => 'my_audience',
);
# Then
is($exchanged_token->access_token, 'my_access_token',
'expected access token');
my %expected_encode_jwt_args = (
alg => 'HS256',
key => 'my_client_secret',
payload => {
iss => 'my_client_id',
sub => 'my_client_id',
aud => 'my_client_assertion_audience',
jti => re('\w+'),
iat => re('\d+'),
exp => re('\d+'),
},
);
my %expected_args = (
audience => 'my_audience',
grant_type => 'urn:ietf:params:oauth:grant-type:token-exchange',
subject_token => 'my_token',
subject_token_type => 'urn:ietf:params:oauth:token-type:access_token',
client_id => 'my_client_id',
client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion => \%expected_encode_jwt_args,
);
my %expected_headers = ();
my @user_agent_sended_args = $test->mocked_user_agent->next_call();
cmp_deeply(\@user_agent_sended_args,
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/token', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
};
subtest "exchange_token() - private_key_jwt auth method" => sub {
# Given
$test->mock_encode_jwt(); # encode_jwt() args are placed directly into 'client_assertion'
my $client = $class->new(
log => $log,
user_agent => $test->mocked_user_agent,
token_response_parser => $test->mocked_token_response_parser,
config => {
provider => 'my_provider',
id => 'my_client_id',
private_jwk_file => "$Bin/resources/client.jwk",
client_auth_method => 'private_key_jwt',
client_assertion_audience => 'my_client_assertion_audience',
audience_alias => {
my_alias => {
audience => 'my_audience',
},
},
},
provider_metadata => { token_url => 'https://my-provider/token' },
);
# When
my $exchanged_token = $client->exchange_token(
token => 'my_token',
audience => 'my_audience',
);
# Then
is($exchanged_token->access_token, 'my_access_token',
'expected access token');
my %expected_encode_jwt_args = (
alg => 'RS256',
key => {kty => 'FAKE'},
payload => {
iss => 'my_client_id',
sub => 'my_client_id',
aud => 'my_client_assertion_audience',
jti => re('\w+'),
iat => re('\d+'),
exp => re('\d+'),
},
);
my %expected_args = (
audience => 'my_audience',
grant_type => 'urn:ietf:params:oauth:grant-type:token-exchange',
subject_token => 'my_token',
subject_token_type => 'urn:ietf:params:oauth:token-type:access_token',
client_id => 'my_client_id',
client_assertion_type => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
client_assertion => \%expected_encode_jwt_args,
);
my %expected_headers = ();
my @user_agent_sended_args = $test->mocked_user_agent->next_call();
cmp_deeply(\@user_agent_sended_args,
[ 'post', [ $test->mocked_user_agent, 'https://my-provider/token', \%expected_headers, 'form', \%expected_args ] ],
'expected call to user agent');
};
}
sub test_build_api_useragent {
subtest "build_api_useragent() with token parameter" => sub {
# Given
my $client = $class->new(
log => $log,
config => {
provider => 'my_provider',
id => 'my_client_id',
secret => 'my_client_secret',
},
);
( run in 3.828 seconds using v1.01-cache-2.11-cpan-75ffa21a3d4 )