OIDC-Client

 view release on metacpan or  search on metacpan

t/client.t  view on Meta::CPAN

               '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,

t/client.t  view on Meta::CPAN

    );

    # 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',

t/client.t  view on Meta::CPAN


    # 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 {

t/client.t  view on Meta::CPAN

  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 )