From 235ce06f558d008e919361592698167752cd19e1 Mon Sep 17 00:00:00 2001 From: Jason Vanderhoof Date: Thu, 8 Dec 2022 13:17:48 -0700 Subject: [PATCH] Adds PKCE Support This commit adds support for PKCE to improve the security posture of OIDC Authentication. It also includes small improvements to make the Authentication Handler and Authenticator Repository more generic and durable. This feature is behind a feature flag as the UI requires changes to support this improved workflow. --- CHANGELOG.md | 6 + app/db/repository/authenticator_repository.rb | 15 +- .../authn_oidc/pkce_support_feature/client.rb | 117 ++++++ .../data_objects/authenticator.rb | 60 +++ .../pkce_support_feature/resolve_identity.rb | 19 + .../pkce_support_feature/strategy.rb | 42 +++ .../views/provider_context.rb | 60 +++ .../authentication/authn_oidc/v2/client.rb | 2 +- .../authentication/authn_oidc/v2/status.rb | 13 + .../authentication/authn_oidc/v2/strategy.rb | 1 - .../handler/authentication_handler.rb | 14 +- .../authentication/util/namespace_selector.rb | 14 +- app/domain/errors.rb | 31 +- config/initializers/feature_flags.rb | 10 +- .../authenticator_repository_spec.rb | 312 ++++++++-------- .../pkce_support_feature/client_spec.rb | 195 ++++++++++ .../data_objects/authenticator.rb | 74 ++++ .../resolve_identity_spec.rb | 96 +++++ .../pkce_support_feature/strategy_rspec.rb | 80 ++++ .../views/providor_context_spec.rb | 74 ++++ .../util/namespace_selector_spec.rb | 26 +- ...ck-expired_code-valid_oidc_credentials.yml | 100 +++++ .../client_callback-invalid_code_verifier.yml | 86 +++++ ...lback-used_code-valid_oidc_credentials.yml | 100 +++++ ...client_callback-valid_oidc_credentials.yml | 170 +++++++++ .../client_initialization.yml | 348 ++++++++++++++++++ .../pkce_support_feature/client_load.yml | 72 ++++ ...covery_endpoint-valid_oidc_credentials.yml | 137 +++++++ 28 files changed, 2090 insertions(+), 184 deletions(-) create mode 100644 app/domain/authentication/authn_oidc/pkce_support_feature/client.rb create mode 100644 app/domain/authentication/authn_oidc/pkce_support_feature/data_objects/authenticator.rb create mode 100644 app/domain/authentication/authn_oidc/pkce_support_feature/resolve_identity.rb create mode 100644 app/domain/authentication/authn_oidc/pkce_support_feature/strategy.rb create mode 100644 app/domain/authentication/authn_oidc/pkce_support_feature/views/provider_context.rb create mode 100644 app/domain/authentication/authn_oidc/v2/status.rb create mode 100644 spec/app/domain/authentication/authn-oidc/pkce_support_feature/client_spec.rb create mode 100644 spec/app/domain/authentication/authn-oidc/pkce_support_feature/data_objects/authenticator.rb create mode 100644 spec/app/domain/authentication/authn-oidc/pkce_support_feature/resolve_identity_spec.rb create mode 100644 spec/app/domain/authentication/authn-oidc/pkce_support_feature/strategy_rspec.rb create mode 100644 spec/app/domain/authentication/authn-oidc/pkce_support_feature/views/providor_context_spec.rb create mode 100644 spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-expired_code-valid_oidc_credentials.yml create mode 100644 spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-invalid_code_verifier.yml create mode 100644 spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-used_code-valid_oidc_credentials.yml create mode 100644 spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-valid_oidc_credentials.yml create mode 100644 spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_initialization.yml create mode 100644 spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_load.yml create mode 100644 spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/discovery_endpoint-valid_oidc_credentials.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index c201380864..c997e2f8f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. the OIDC provider endpoint would include duplicate OIDC authenticators. This change resolves ONYX-25530. +### Added +- Provides support for PKCE in the OIDC Authenticator code redirect workflow. + This is disabled by default, but is available under the + `CONJUR_FEATURE_PKCE_SUPPORT_ENABLED` feature flag. + [cyberark/conjur#2678](https://github.com/cyberark/conjur/pull/2678) + ## [1.19.1] - 2022-12-08 ### Security diff --git a/app/db/repository/authenticator_repository.rb b/app/db/repository/authenticator_repository.rb index 58d0d2af04..2ea9a53a6d 100644 --- a/app/db/repository/authenticator_repository.rb +++ b/app/db/repository/authenticator_repository.rb @@ -1,10 +1,16 @@ module DB module Repository class AuthenticatorRepository - def initialize(data_object:, resource_repository: ::Resource, logger: Rails.logger) + def initialize( + data_object:, + resource_repository: ::Resource, + logger: Rails.logger, + pkce_support_enabled: Rails.configuration.feature_flags.enabled?(:pkce_support) + ) @resource_repository = resource_repository @data_object = data_object @logger = logger + @pkce_support_enabled = pkce_support_enabled end def find_all(type:, account:) @@ -65,7 +71,14 @@ def load_authenticator(type:, account:, service_id:) args[variable.resource_id.split('/')[-1].underscore.to_sym] = variable.secret.value end end + begin + if @pkce_support_enabled + allowed_args = %i[account service_id] + + @data_object.const_get(:REQUIRED_VARIABLES) + + @data_object.const_get(:OPTIONAL_VARIABLES) + args_list = args_list.select{ |key, _| allowed_args.include?(key) } + end @data_object.new(**args_list) rescue ArgumentError => e @logger.debug("DB::Repository::AuthenticatorRepository.load_authenticator - exception: #{e}") diff --git a/app/domain/authentication/authn_oidc/pkce_support_feature/client.rb b/app/domain/authentication/authn_oidc/pkce_support_feature/client.rb new file mode 100644 index 0000000000..8ca0e996c4 --- /dev/null +++ b/app/domain/authentication/authn_oidc/pkce_support_feature/client.rb @@ -0,0 +1,117 @@ +module Authentication + module AuthnOidc + module PkceSupportFeature + class Client + def initialize( + authenticator:, + client: ::OpenIDConnect::Client, + oidc_id_token: ::OpenIDConnect::ResponseObject::IdToken, + discovery_configuration: ::OpenIDConnect::Discovery::Provider::Config, + cache: Rails.cache, + logger: Rails.logger + ) + @authenticator = authenticator + @client = client + @oidc_id_token = oidc_id_token + @discovery_configuration = discovery_configuration + @cache = cache + @logger = logger + end + + def oidc_client + @oidc_client ||= begin + issuer_uri = URI(@authenticator.provider_uri) + @client.new( + identifier: @authenticator.client_id, + secret: @authenticator.client_secret, + redirect_uri: @authenticator.redirect_uri, + scheme: issuer_uri.scheme, + host: issuer_uri.host, + port: issuer_uri.port, + authorization_endpoint: URI(discovery_information.authorization_endpoint).path, + token_endpoint: URI(discovery_information.token_endpoint).path, + userinfo_endpoint: URI(discovery_information.userinfo_endpoint).path, + jwks_uri: URI(discovery_information.jwks_uri).path, + end_session_endpoint: URI(discovery_information.end_session_endpoint).path + ) + end + end + + def callback(code:, nonce:, code_verifier:) + oidc_client.authorization_code = code + begin + bearer_token = oidc_client.access_token!( + scope: true, + client_auth_method: :basic, + code_verifier: code_verifier + ) + rescue Rack::OAuth2::Client::Error => e + # Only handle the expected errors related to access token retrieval. + case e.message + when /PKCE verification failed/ + raise Errors::Authentication::AuthnOidc::TokenRetrievalFailed, + 'PKCE verification failed' + when /The authorization code is invalid or has expired/ + raise Errors::Authentication::AuthnOidc::TokenRetrievalFailed, + 'Authorization code is invalid or has expired' + when /Code not valid/ + raise Errors::Authentication::AuthnOidc::TokenRetrievalFailed, + 'Authorization code is invalid' + end + raise e + end + id_token = bearer_token.id_token || bearer_token.access_token + + begin + attempts ||= 0 + decoded_id_token = @oidc_id_token.decode( + id_token, + discovery_information.jwks + ) + rescue StandardError => e + attempts += 1 + raise e if attempts > 1 + + # If the JWKS verification fails, blow away the existing cache and + # try again. This is intended to handle the case where the OIDC certificate + # changes, and we want to cache the new certificate without decode failing. + discovery_information(invalidate: true) + retry + end + + begin + decoded_id_token.verify!( + issuer: @authenticator.provider_uri, + client_id: @authenticator.client_id, + nonce: nonce + ) + rescue OpenIDConnect::ResponseObject::IdToken::InvalidNonce + raise Errors::Authentication::AuthnOidc::TokenVerificationFailed, + 'Provided nonce does not match the nonce in the JWT' + rescue OpenIDConnect::ResponseObject::IdToken::ExpiredToken + raise Errors::Authentication::AuthnOidc::TokenVerificationFailed, + 'JWT has expired' + rescue OpenIDConnect::ValidationFailed => e + raise Errors::Authentication::AuthnOidc::TokenVerificationFailed, + e.message + end + decoded_id_token + end + + def discovery_information(invalidate: false) + @cache.fetch( + "#{@authenticator.account}/#{@authenticator.service_id}/#{URI::Parser.new.escape(@authenticator.provider_uri)}", + force: invalidate, + skip_nil: true + ) do + @discovery_configuration.discover!(@authenticator.provider_uri) + rescue HTTPClient::ConnectTimeoutError, Errno::ETIMEDOUT => e + raise Errors::Authentication::OAuth::ProviderDiscoveryTimeout.new(@authenticator.provider_uri, e.message) + rescue => e + raise Errors::Authentication::OAuth::ProviderDiscoveryFailed.new(@authenticator.provider_uri, e.message) + end + end + end + end + end +end diff --git a/app/domain/authentication/authn_oidc/pkce_support_feature/data_objects/authenticator.rb b/app/domain/authentication/authn_oidc/pkce_support_feature/data_objects/authenticator.rb new file mode 100644 index 0000000000..7cac9b7ffe --- /dev/null +++ b/app/domain/authentication/authn_oidc/pkce_support_feature/data_objects/authenticator.rb @@ -0,0 +1,60 @@ +module Authentication + module AuthnOidc + module PkceSupportFeature + module DataObjects + class Authenticator + + REQUIRED_VARIABLES = %i[provider_uri client_id client_secret claim_mapping].freeze + OPTIONAL_VARIABLES = %i[redirect_uri response_type provider_scope name].freeze + + attr_reader( + :provider_uri, + :client_id, + :client_secret, + :claim_mapping, + :account, + :service_id, + :redirect_uri, + :response_type + ) + + def initialize( + provider_uri:, + client_id:, + client_secret:, + claim_mapping:, + account:, + service_id:, + redirect_uri: nil, + name: nil, + response_type: 'code', + provider_scope: nil + ) + @account = account + @provider_uri = provider_uri + @client_id = client_id + @client_secret = client_secret + @claim_mapping = claim_mapping + @response_type = response_type + @service_id = service_id + @name = name + @provider_scope = provider_scope + @redirect_uri = redirect_uri + end + + def scope + (%w[openid email profile] + [*@provider_scope.to_s.split(' ')]).uniq.join(' ') + end + + def name + @name || @service_id.titleize + end + + def resource_id + "#{account}:webservice:conjur/authn-oidc/#{service_id}" + end + end + end + end + end +end diff --git a/app/domain/authentication/authn_oidc/pkce_support_feature/resolve_identity.rb b/app/domain/authentication/authn_oidc/pkce_support_feature/resolve_identity.rb new file mode 100644 index 0000000000..4ac21cb8b2 --- /dev/null +++ b/app/domain/authentication/authn_oidc/pkce_support_feature/resolve_identity.rb @@ -0,0 +1,19 @@ +module Authentication + module AuthnOidc + module PkceSupportFeature + class ResolveIdentity + def call(identity:, account:, allowed_roles:) + # make sure role has a resource (ex. user, host) + roles = allowed_roles.select(&:resource?) + + roles.each do |role| + role_account, _, role_id = role.id.split(':') + return role if role_account == account && identity == role_id + end + + raise(Errors::Authentication::Security::RoleNotFound, identity) + end + end + end + end +end diff --git a/app/domain/authentication/authn_oidc/pkce_support_feature/strategy.rb b/app/domain/authentication/authn_oidc/pkce_support_feature/strategy.rb new file mode 100644 index 0000000000..478c15b66b --- /dev/null +++ b/app/domain/authentication/authn_oidc/pkce_support_feature/strategy.rb @@ -0,0 +1,42 @@ +module Authentication + module AuthnOidc + module PkceSupportFeature + class Strategy + def initialize( + authenticator:, + client: Authentication::AuthnOidc::PkceSupportFeature::Client, + logger: Rails.logger + ) + @authenticator = authenticator + @client = client.new(authenticator: authenticator) + @logger = logger + end + + def callback(args) + %i[code nonce code_verifier].each do |param| + unless args[param].present? + raise Errors::Authentication::RequestBody::MissingRequestParam, param.to_s + end + end + + identity = resolve_identity( + jwt: @client.callback( + code: args[:code], + nonce: args[:nonce], + code_verifier: args[:code_verifier] + ) + ) + unless identity.present? + raise Errors::Authentication::AuthnOidc::IdTokenClaimNotFoundOrEmpty, + @authenticator.claim_mapping + end + identity + end + + def resolve_identity(jwt:) + jwt.raw_attributes.with_indifferent_access[@authenticator.claim_mapping] + end + end + end + end +end diff --git a/app/domain/authentication/authn_oidc/pkce_support_feature/views/provider_context.rb b/app/domain/authentication/authn_oidc/pkce_support_feature/views/provider_context.rb new file mode 100644 index 0000000000..1e2c244430 --- /dev/null +++ b/app/domain/authentication/authn_oidc/pkce_support_feature/views/provider_context.rb @@ -0,0 +1,60 @@ +require 'securerandom' +require 'digest' + +module Authentication + module AuthnOidc + module PkceSupportFeature + module Views + class ProviderContext + def initialize( + client: Authentication::AuthnOidc::V2::Client, + digest: Digest::SHA256, + random: SecureRandom, + logger: Rails.logger + ) + @client = client + @logger = logger + @digest = digest + @random = random + end + + def call(authenticators:) + authenticators.map do |authenticator| + nonce = @random.hex(25) + code_verifier = @random.hex(25) + code_challenge = @digest.base64digest(code_verifier).tr("+/", "-_").tr("=", "") + { + service_id: authenticator.service_id, + type: 'authn-oidc', + name: authenticator.name, + nonce: nonce, + code_verifier: code_verifier, + redirect_uri: generate_redirect_url( + client: @client.new(authenticator: authenticator), + authenticator: authenticator, + nonce: nonce, + code_challenge: code_challenge + ) + } + end + end + + def generate_redirect_url(client:, authenticator:, nonce:, code_challenge:) + params = { + client_id: authenticator.client_id, + response_type: authenticator.response_type, + scope: ERB::Util.url_encode(authenticator.scope), + nonce: nonce, + code_challenge: code_challenge, + code_challenge_method: 'S256', + redirect_uri: ERB::Util.url_encode(authenticator.redirect_uri) + } + formatted_params = params.map { |key, value| "#{key}=#{value}" }.join("&") + + "#{client.discovery_information.authorization_endpoint}?#{formatted_params}" + end + end + end + end + end +end diff --git a/app/domain/authentication/authn_oidc/v2/client.rb b/app/domain/authentication/authn_oidc/v2/client.rb index 86488ac368..cc62b873f3 100644 --- a/app/domain/authentication/authn_oidc/v2/client.rb +++ b/app/domain/authentication/authn_oidc/v2/client.rb @@ -56,7 +56,7 @@ def callback(code:) id_token, discovery_information.jwks ) - rescue Exception => e + rescue StandardError => e attempts += 1 raise e if attempts > 1 diff --git a/app/domain/authentication/authn_oidc/v2/status.rb b/app/domain/authentication/authn_oidc/v2/status.rb new file mode 100644 index 0000000000..27de1a96f1 --- /dev/null +++ b/app/domain/authentication/authn_oidc/v2/status.rb @@ -0,0 +1,13 @@ +module Authentication + module AuthnOidc + module V2 + class Status + def call(authenticator:) + { + + } + end + end + end + end +end diff --git a/app/domain/authentication/authn_oidc/v2/strategy.rb b/app/domain/authentication/authn_oidc/v2/strategy.rb index a76f6ca64d..003c9bf83d 100644 --- a/app/domain/authentication/authn_oidc/v2/strategy.rb +++ b/app/domain/authentication/authn_oidc/v2/strategy.rb @@ -13,7 +13,6 @@ def initialize( @logger = logger end - # Don't love this name... def callback(args) # TODO: Check that `code` and `state` attributes are present raise Errors::Authentication::AuthnOidc::StateMismatch unless args[:state] == @authenticator.state diff --git a/app/domain/authentication/handler/authentication_handler.rb b/app/domain/authentication/handler/authentication_handler.rb index 9731b6d1ee..763b85cb58 100644 --- a/app/domain/authentication/handler/authentication_handler.rb +++ b/app/domain/authentication/handler/authentication_handler.rb @@ -10,13 +10,15 @@ def initialize( authn_repo: DB::Repository::AuthenticatorRepository, namespace_selector: Authentication::Util::NamespaceSelector, logger: Rails.logger, - authentication_error: LogMessages::Authentication::AuthenticationError + authentication_error: LogMessages::Authentication::AuthenticationError, + pkce_support_enabled: Rails.configuration.feature_flags.enabled?(:pkce_support) ) @role = role @resource = resource @authenticator_type = authenticator_type @logger = logger @authentication_error = authentication_error + @pkce_support_enabled = pkce_support_enabled # Dynamically load authenticator specific classes namespace = namespace_selector.select( @@ -31,10 +33,12 @@ def initialize( end def call(parameters:, request_ip:) - required_parameters = %i[state code] - required_parameters.each do |parameter| - if !parameters.key?(parameter) || parameters[parameter].strip.empty? - raise Errors::Authentication::RequestBody::MissingRequestParam, parameter + unless @pkce_support_enabled + required_parameters = %i[state code] + required_parameters.each do |parameter| + if !parameters.key?(parameter) || parameters[parameter].strip.empty? + raise Errors::Authentication::RequestBody::MissingRequestParam, parameter + end end end diff --git a/app/domain/authentication/util/namespace_selector.rb b/app/domain/authentication/util/namespace_selector.rb index d168505d43..a877269b34 100644 --- a/app/domain/authentication/util/namespace_selector.rb +++ b/app/domain/authentication/util/namespace_selector.rb @@ -3,13 +3,17 @@ module Authentication module Util class NamespaceSelector - def self.select(authenticator_type:) + def self.select(authenticator_type:, pkce_support_enabled: Rails.configuration.feature_flags.enabled?(:pkce_support)) case authenticator_type when 'authn-oidc' - # 'V2' is a bit of a hack to handle the fact that - # the original OIDC authenticator is really a - # glorified JWT authenticator. - 'Authentication::AuthnOidc::V2' + if pkce_support_enabled + 'Authentication::AuthnOidc::PkceSupportFeature' + else + # 'V2' is a bit of a hack to handle the fact that + # the original OIDC authenticator is really a + # glorified JWT authenticator. + 'Authentication::AuthnOidc::V2' + end else raise "'#{authenticator_type}' is not a supported authenticator type" # TODO: make this dynamic based on authenticator type. diff --git a/app/domain/errors.rb b/app/domain/errors.rb index c77bf9ce78..5c20a65b12 100644 --- a/app/domain/errors.rb +++ b/app/domain/errors.rb @@ -219,7 +219,6 @@ module Jwt end module AuthnOidc - IdTokenClaimNotFoundOrEmpty = ::Util::TrackableErrorClass.new( msg: "Claim '{0-claim-name}' not found or empty in ID token. " \ "This claim is defined in the id-token-user-property variable.", @@ -231,16 +230,6 @@ module AuthnOidc code: "CONJ00075E" ) - StateMismatch = ::Util::TrackableErrorClass.new( - msg: "Conjur internal state doesn't match given state", - code: "CONJ00127E" - ) - - TokenVerificationFailed = ::Util::TrackableErrorClass.new( - msg: "Conjur internal state doesn't match given state", - code: "CONJ00128E" - ) - InvalidVariableValue = ::Util::TrackableErrorClass.new( msg: "Parameter {0} with value {1} invalid", code: "CONJ00129E" @@ -251,6 +240,26 @@ module AuthnOidc code: "CONJ00130E" ) + TokenVerificationFailed = ::Util::TrackableErrorClass.new( + msg: "JWT Token validation failed: '{0-error}'", + code: "CONJ00128E" + ) + + TokenRetrievalFailed = ::Util::TrackableErrorClass.new( + msg: "Access Token retrieval failure: '{0-error}'", + code: "CONJ00133E" + ) + + # TODO - this error comes from the current version of the OIDC + # authenticator. The second iteration (currently behind a flag) + # moves state generation and verification to the client. Once the + # 'pkce_support' feature flag functionality permanently replaces + # the initial implementation, this error message will no longer be + # used and should be removed. + StateMismatch = ::Util::TrackableErrorClass.new( + msg: "Conjur internal state doesn't match given state", + code: "CONJ00127E" + ) end module AuthnIam diff --git a/config/initializers/feature_flags.rb b/config/initializers/feature_flags.rb index 5f0f9f63c8..61e930f18e 100644 --- a/config/initializers/feature_flags.rb +++ b/config/initializers/feature_flags.rb @@ -27,7 +27,15 @@ # If enabled, the Roles API will emit callbacks to extensions for # before/after events when role memberships are added or removed # through the REST API. - roles_api_extensions: false + roles_api_extensions: false, + + # If enabled, the V2 OIDC authenticator will be updated to support PKCE by + # default. This change is backward breaking as it moves State generation to + # the client and requires Nonce and PKCE to be provided to the OIDC Authenticator. + # + # Once the change is made on the UI (which is the intended target), the behavior + # enabled by the flag should be made the default behavior. + pkce_support: false }.freeze config.feature_flags = Conjur::FeatureFlags::Features.new( diff --git a/spec/app/db/repository/authenticator_repository_spec.rb b/spec/app/db/repository/authenticator_repository_spec.rb index 1501a8f70c..1d88506f8f 100644 --- a/spec/app/db/repository/authenticator_repository_spec.rb +++ b/spec/app/db/repository/authenticator_repository_spec.rb @@ -3,65 +3,38 @@ require 'spec_helper' RSpec.describe('DB::Repository::AuthenticatorRepository') do - let(:resource_repository) { ::Resource } - - let(:repo) do - DB::Repository::AuthenticatorRepository.new( - resource_repository: resource_repository, - data_object: Authentication::AuthnOidc::V2::DataObjects::Authenticator - ) - end - - let(:arguments) { %i[provider_uri client_id client_secret claim_mapping nonce state] } - - describe('#find_all') do - context 'when webservice is not present' do - it { expect(repo.find_all(type: 'authn-oidc', account: 'rspec')).to eq([]) } - end - - context 'when webservices are presents' do - let(:services) { %i[foo bar] } - before(:each) do - services.each do |service| - ::Role.create( - role_id: "rspec:policy:conjur/authn-oidc/#{service}-abc123" - ) - ::Resource.create( - resource_id: "rspec:webservice:conjur/authn-oidc/#{service}-abc123", - owner_id: "rspec:policy:conjur/authn-oidc/#{service}-abc123" - ) + # Verify tests pass with flag enabled/disabled + %w[true false].each do |pkce_flag_enabled| + context "with flag #{pkce_flag_enabled}" do + let(:data_object) do + if pkce_flag_enabled + Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator + else + Authentication::AuthnOidc::V2::DataObjects::Authenticator end end - context 'variables are not loaded' do - it { expect(repo.find_all(type: 'authn-oidc', account: 'rspec')).to eq([]) } + let(:resource_repository) { ::Resource } + + let(:repo) do + DB::Repository::AuthenticatorRepository.new( + resource_repository: resource_repository, + data_object: data_object, + pkce_support_enabled: pkce_flag_enabled + ) end - context 'variables are loaded' do - before(:each) do - services.each do |service| - arguments.each do |variable| - ::Resource.create( - resource_id: "rspec:variable:conjur/authn-oidc/#{service}-abc123/#{variable}", - owner_id: "rspec:policy:conjur/authn-oidc/#{service}-abc123" - ) - end - end - end + let(:arguments) { %i[provider_uri client_id client_secret claim_mapping nonce state] } - context 'secrets are not set' do + describe('#find_all') do + context 'when webservice is not present' do it { expect(repo.find_all(type: 'authn-oidc', account: 'rspec')).to eq([]) } end - context 'secrets are set' do + context 'when webservices are presents' do + let(:services) { %w[foo bar] } before(:each) do services.each do |service| - arguments.each do |variable| - ::Secret.create( - resource_id: "rspec:variable:conjur/authn-oidc/#{service}-abc123/#{variable}", - value: "#{variable}-abc123" - ) - end ::Role.create( role_id: "rspec:policy:conjur/authn-oidc/#{service}" ) @@ -78,145 +51,178 @@ end end - let(:authenticators) { repo.find_all(type: 'authn-oidc', account: 'rspec') } - - it { expect(authenticators.length).to eq(2) } - it { expect(authenticators.first).to be_kind_of(Authentication::AuthnOidc::V2::DataObjects::Authenticator) } - it { expect(authenticators.last).to be_kind_of(Authentication::AuthnOidc::V2::DataObjects::Authenticator) } + context 'variables are not loaded' do + it { expect(repo.find_all(type: 'authn-oidc', account: 'rspec')).to eq([]) } + end - context 'filters invalid authenticators' do + context 'variables are loaded' do before(:each) do - ::Role.create( - role_id: "rspec:policy:conjur/authn-oidc/baz-abc123" - ) - ::Resource.create( - resource_id: "rspec:webservice:conjur/authn-oidc/baz-abc123", - owner_id: "rspec:policy:conjur/authn-oidc/baz-abc123" - ) + services.each do |service| + arguments.each do |variable| + ::Resource.create( + resource_id: "rspec:variable:conjur/authn-oidc/#{service}/#{variable}", + owner_id: "rspec:policy:conjur/authn-oidc/#{service}" + ) + end + end + end + + context 'secrets are not set' do + it { expect(repo.find_all(type: 'authn-oidc', account: 'rspec')).to eq([]) } end - it { expect(repo.find_all(type: 'authn-oidc', account: 'rspec').length).to eq(2) } + context 'secrets are set' do + before(:each) do + services.each do |service| + arguments.each do |variable| + ::Secret.create( + resource_id: "rspec:variable:conjur/authn-oidc/#{service}/#{variable}", + value: "#{variable}" + ) + end + end + end + + let(:authenticators) { repo.find_all(type: 'authn-oidc', account: 'rspec') } + + it { expect(authenticators.length).to eq(2) } + it { expect(authenticators.first).to be_kind_of(data_object) } + it { expect(authenticators.last).to be_kind_of(data_object) } + + context 'filters invalid authenticators' do + before(:each) do + ::Role.create( + role_id: "rspec:policy:conjur/authn-oidc/baz" + ) + ::Resource.create( + resource_id: "rspec:webservice:conjur/authn-oidc/baz", + owner_id: "rspec:policy:conjur/authn-oidc/baz" + ) + end + + it { expect(repo.find_all(type: 'authn-oidc', account: 'rspec').length).to eq(2) } + + after(:each) do + ::Resource['rspec:webservice:conjur/authn-oidc/baz'].destroy + ::Role['rspec:policy:conjur/authn-oidc/baz'].destroy + end + end + end after(:each) do - ::Resource['rspec:webservice:conjur/authn-oidc/baz-abc123'].destroy - ::Role['rspec:policy:conjur/authn-oidc/baz-abc123'].destroy + services.each do |service| + arguments.each do |variable| + ::Resource["rspec:variable:conjur/authn-oidc/#{service}/#{variable}"].destroy + end + end end end - end - after(:each) do - services.each do |service| - arguments.each do |variable| - ::Resource["rspec:variable:conjur/authn-oidc/#{service}-abc123/#{variable}"].destroy + after(:each) do + services.each do |service| + ::Resource["rspec:webservice:conjur/authn-oidc/#{service}"].destroy + ::Role["rspec:policy:conjur/authn-oidc/#{service}"].destroy end end end end - after(:each) do - services.each do |service| - ::Resource["rspec:webservice:conjur/authn-oidc/#{service}-abc123"].destroy - ::Role["rspec:policy:conjur/authn-oidc/#{service}-abc123"].destroy + describe('#find') do + context 'when webservice is not present' do + it { expect(repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be(nil) } end - end - - end - end - - describe('#find') do - context 'when webservice is not present' do - it { expect(repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be(nil) } - end - - context 'when webservice is present' do - before(:each) do - ::Role.create( - role_id: "rspec:policy:conjur/authn-oidc/abc123" - ) - ::Resource.create( - resource_id: "rspec:webservice:conjur/authn-oidc/abc123", - owner_id: "rspec:policy:conjur/authn-oidc/abc123" - ) - end - context 'when no variables are set' do - it { expect(repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be(nil) } - end - - context 'when all variables are present' do - before(:each) do - arguments.each do |variable| + context 'when webservice is present' do + before(:each) do + ::Role.create( + role_id: "rspec:policy:conjur/authn-oidc/abc123" + ) ::Resource.create( - resource_id: "rspec:variable:conjur/authn-oidc/abc123/#{variable}", + resource_id: "rspec:webservice:conjur/authn-oidc/abc123", owner_id: "rspec:policy:conjur/authn-oidc/abc123" ) end - end - context 'are empty' do - it { expect(repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be(nil) } - end + context 'when no variables are set' do + it { expect(repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be(nil) } + end - context 'are set' do - before(:each) do - arguments.each do |variable| - ::Secret.create( - resource_id: "rspec:variable:conjur/authn-oidc/abc123/#{variable}", - value: "#{variable}-abc123" - ) + context 'when all variables are present' do + before(:each) do + arguments.each do |variable| + ::Resource.create( + resource_id: "rspec:variable:conjur/authn-oidc/abc123/#{variable}", + owner_id: "rspec:policy:conjur/authn-oidc/abc123" + ) + end end - end - let(:authenticator) { repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123') } + context 'are empty' do + it { expect(repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be(nil) } + end - it { expect(authenticator).to be_truthy } - it { expect(authenticator).to be_kind_of(Authentication::AuthnOidc::V2::DataObjects::Authenticator) } - it { expect(authenticator.account).to eq('rspec') } - it { expect(authenticator.service_id).to eq('abc123') } - end + context 'are set' do + before(:each) do + arguments.each do |variable| + ::Secret.create( + resource_id: "rspec:variable:conjur/authn-oidc/abc123/#{variable}", + value: "#{variable}" + ) + end + end + + let(:authenticator) { repo.find(type: 'authn-oidc', account: 'rspec', service_id: 'abc123') } - after(:each) do - arguments.each do |variable| - ::Resource["rspec:variable:conjur/authn-oidc/abc123/#{variable}"].destroy + it { expect(authenticator).to be_truthy } + it { expect(authenticator).to be_kind_of(data_object) } + it { expect(authenticator.account).to eq('rspec') } + it { expect(authenticator.service_id).to eq('abc123') } + end + + after(:each) do + arguments.each do |variable| + ::Resource["rspec:variable:conjur/authn-oidc/abc123/#{variable}"].destroy + end + end + end + + after(:each) do + ::Resource['rspec:webservice:conjur/authn-oidc/abc123'].destroy + ::Role['rspec:policy:conjur/authn-oidc/abc123'].destroy end end end - after(:each) do - ::Resource['rspec:webservice:conjur/authn-oidc/abc123'].destroy - ::Role['rspec:policy:conjur/authn-oidc/abc123'].destroy - end - end - end + describe('#exists?') do + context 'when webservice is present' do + before(:context) do + ::Role.create( + role_id: "rspec:policy:conjur/authn-oidc/abc123" + ) + ::Resource.create( + resource_id: "rspec:webservice:conjur/authn-oidc/abc123", + owner_id: "rspec:policy:conjur/authn-oidc/abc123" + ) + end - describe('#exists?') do - context 'when webservice is present' do - before(:context) do - ::Role.create( - role_id: "rspec:policy:conjur/authn-oidc/abc123" - ) - ::Resource.create( - resource_id: "rspec:webservice:conjur/authn-oidc/abc123", - owner_id: "rspec:policy:conjur/authn-oidc/abc123" - ) - end + it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be_truthy } + it { expect(repo.exists?(type: nil, account: 'rspec', service_id: 'abc123')).to be_falsey } + it { expect(repo.exists?(type: 'authn-oidc', account: nil, service_id: 'abc123')).to be_falsey } + it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: nil)).to be_falsey } - it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be_truthy } - it { expect(repo.exists?(type: nil, account: 'rspec', service_id: 'abc123')).to be_falsey } - it { expect(repo.exists?(type: 'authn-oidc', account: nil, service_id: 'abc123')).to be_falsey } - it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: nil)).to be_falsey } + after(:context) do + ::Resource['rspec:webservice:conjur/authn-oidc/abc123'].destroy + ::Role['rspec:policy:conjur/authn-oidc/abc123'].destroy + end + end - after(:context) do - ::Resource['rspec:webservice:conjur/authn-oidc/abc123'].destroy - ::Role['rspec:policy:conjur/authn-oidc/abc123'].destroy + context 'when webservice is not present' do + it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be_falsey } + it { expect(repo.exists?(type: nil, account: 'rspec', service_id: 'abc123')).to be_falsey } + it { expect(repo.exists?(type: 'authn-oidc', account: nil, service_id: 'abc123')).to be_falsey } + it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: nil)).to be_falsey } + end end end - - context 'when webservice is not present' do - it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: 'abc123')).to be_falsey } - it { expect(repo.exists?(type: nil, account: 'rspec', service_id: 'abc123')).to be_falsey } - it { expect(repo.exists?(type: 'authn-oidc', account: nil, service_id: 'abc123')).to be_falsey } - it { expect(repo.exists?(type: 'authn-oidc', account: 'rspec', service_id: nil)).to be_falsey } - end end end diff --git a/spec/app/domain/authentication/authn-oidc/pkce_support_feature/client_spec.rb b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/client_spec.rb new file mode 100644 index 0000000000..4b3d528cfb --- /dev/null +++ b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/client_spec.rb @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe(Authentication::AuthnOidc::PkceSupportFeature::Client) do + let(:authenticator) do + Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator.new( + provider_uri: 'https://dev-92899796.okta.com/oauth2/default', + redirect_uri: 'http://localhost:3000/authn-oidc/okta-2/cucumber/authenticate', + client_id: '0oa3w3xig6rHiu9yT5d7', + client_secret: 'e349BMTTIpLO-rPuPqLLkLyH_pO-loUzhIVJCrHj', + claim_mapping: 'foo', + account: 'bar', + service_id: 'baz' + ) + end + + let(:client) do + VCR.use_cassette("authenticators/authn-oidc/pkce_support_feature/client_load") do + client = Authentication::AuthnOidc::PkceSupportFeature::Client.new( + authenticator: authenticator + ) + # The call `oidc_client` queries the OIDC endpoint. As such, + # we need to wrap this in a VCR call. Calling this before + # returning the client to allow this call to be more effectively + # mocked. + client.oidc_client + client + end + end + + describe '.callback' do + context 'when credentials are valid' do + it 'returns a valid JWT token', vcr: 'authenticators/authn-oidc/pkce_support_feature/client_callback-valid_oidc_credentials' do + # Because JWT tokens have an expiration timeframe, we need to hold + # time constant after caching the request. + travel_to(Time.parse("2022-09-30 17:02:17 +0000")) do + token = client.callback( + code: '-QGREc_SONbbJIKdbpyYudA13c9PZlgqdxowkf45LOw', + code_verifier: 'c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d', + nonce: '7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d' + ) + expect(token).to be_a_kind_of(OpenIDConnect::ResponseObject::IdToken) + expect(token.raw_attributes['nonce']).to eq('7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d') + expect(token.raw_attributes['preferred_username']).to eq('test.user3@mycompany.com') + expect(token.aud).to eq('0oa3w3xig6rHiu9yT5d7') + end + end + end + + context 'when code verifier does not match' do + it 'raises an error', vcr: 'authenticators/authn-oidc/pkce_support_feature/client_callback-invalid_code_verifier' do + travel_to(Time.parse("2022-10-17 17:23:30 +0000")) do + expect do + client.callback( + code: 'GV48_SF4a19ghvBhVbbSG3Lr8BuFl8PhWVPZSbokV2o', + code_verifier: 'bad-code-verifier', + nonce: '3e6bd5235e4692b37ca1f04cb01b6e0cb177aa20dcef19e89f' + ) + end.to raise_error( + Errors::Authentication::AuthnOidc::TokenRetrievalFailed, + "CONJ00133E Access Token retrieval failure: 'PKCE verification failed'" + ) + end + end + end + + context 'when nonce does not match' do + it 'raises an error', vcr: 'authenticators/authn-oidc/pkce_support_feature/client_callback-valid_oidc_credentials' do + travel_to(Time.parse("2022-09-30 17:02:17 +0000")) do + expect do + client.callback( + code: '-QGREc_SONbbJIKdbpyYudA13c9PZlgqdxowkf45LOw', + code_verifier: 'c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d', + nonce: 'bad-nonce' + ) + end.to raise_error( + Errors::Authentication::AuthnOidc::TokenVerificationFailed, + "CONJ00128E JWT Token validation failed: 'Provided nonce does not match the nonce in the JWT'" + ) + end + end + end + + context 'when JWT has expired' do + it 'raises an error', vcr: 'authenticators/authn-oidc/pkce_support_feature/client_callback-valid_oidc_credentials' do + travel_to(Time.parse("2022-10-01 17:02:17 +0000")) do + expect do + client.callback( + code: '-QGREc_SONbbJIKdbpyYudA13c9PZlgqdxowkf45LOw', + code_verifier: 'c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d', + nonce: '7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d' + ) + end.to raise_error( + Errors::Authentication::AuthnOidc::TokenVerificationFailed, + "CONJ00128E JWT Token validation failed: 'JWT has expired'" + ) + end + end + end + + context 'when code has previously been used' do + it 'raise an exception', vcr: 'authenticators/authn-oidc/pkce_support_feature/client_callback-used_code-valid_oidc_credentials' do + expect do + client.callback( + code: '-QGREc_SONbbJIKdbpyYudA13c9PZlgqdxowkf45LOw', + code_verifier: 'c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d', + nonce: '7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d' + ) + end.to raise_error( + Errors::Authentication::AuthnOidc::TokenRetrievalFailed, + "CONJ00133E Access Token retrieval failure: 'Authorization code is invalid or has expired'" + ) + end + end + + context 'when code has expired', vcr: 'authenticators/authn-oidc/pkce_support_feature/client_callback-expired_code-valid_oidc_credentials' do + it 'raise an exception' do + expect do + client.callback( + code: 'SNSPeiQJ0-D6nUHTg-Ht9ZoDxIaaWBB80pnYuXY2VxU', + code_verifier: 'c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d', + nonce: '7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d' + ) + end.to raise_error( + Errors::Authentication::AuthnOidc::TokenRetrievalFailed, + "CONJ00133E Access Token retrieval failure: 'Authorization code is invalid or has expired'" + ) + end + end + end + + describe '.oidc_client' do + context 'when credentials are valid' do + it 'returns a valid oidc client', vcr: 'authenticators/authn-oidc/pkce_support_feature/client_initialization' do + oidc_client = client.oidc_client + + expect(oidc_client).to be_a_kind_of(OpenIDConnect::Client) + expect(oidc_client.identifier).to eq('0oa3w3xig6rHiu9yT5d7') + expect(oidc_client.secret).to eq('e349BMTTIpLO-rPuPqLLkLyH_pO-loUzhIVJCrHj') + expect(oidc_client.redirect_uri).to eq('http://localhost:3000/authn-oidc/okta-2/cucumber/authenticate') + expect(oidc_client.scheme).to eq('https') + expect(oidc_client.host).to eq('dev-92899796.okta.com') + expect(oidc_client.port).to eq(443) + expect(oidc_client.authorization_endpoint).to eq('/oauth2/default/v1/authorize') + expect(oidc_client.token_endpoint).to eq('/oauth2/default/v1/token') + expect(oidc_client.userinfo_endpoint).to eq('/oauth2/default/v1/userinfo') + end + end + end + + describe '.discovery_information', vcr: 'authenticators/authn-oidc/pkce_support_feature/discovery_endpoint-valid_oidc_credentials' do + context 'when credentials are valid' do + it 'endpoint returns valid data' do + discovery_information = client.discovery_information(invalidate: true) + + expect(discovery_information.authorization_endpoint).to eq( + 'https://dev-92899796.okta.com/oauth2/default/v1/authorize' + ) + expect(discovery_information.token_endpoint).to eq( + 'https://dev-92899796.okta.com/oauth2/default/v1/token' + ) + expect(discovery_information.userinfo_endpoint).to eq( + 'https://dev-92899796.okta.com/oauth2/default/v1/userinfo' + ) + expect(discovery_information.jwks_uri).to eq( + 'https://dev-92899796.okta.com/oauth2/default/v1/keys' + ) + expect(discovery_information.end_session_endpoint).to eq( + 'https://dev-92899796.okta.com/oauth2/default/v1/logout' + ) + end + end + + context 'when provider URI is invalid', vcr: 'authenticators/authn-oidc/pkce_support_feature/discovery_endpoint-invalid_oidc_provider' do + it 'returns an timeout error' do + client = Authentication::AuthnOidc::PkceSupportFeature::Client.new( + authenticator: Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator.new( + provider_uri: 'https://foo.bar1234321.com', + redirect_uri: 'http://localhost:3000/authn-oidc/okta-2/cucumber/authenticate', + client_id: '0oa3w3xig6rHiu9yT5d7', + client_secret: 'e349BMTTIpLO-rPuPqLLkLyH_pO-loUzhIVJCrHj', + claim_mapping: 'foo', + account: 'bar', + service_id: 'baz' + ) + ) + + expect{client.discovery_information(invalidate: true)}.to raise_error( + Errors::Authentication::OAuth::ProviderDiscoveryFailed + ) + end + end + end +end diff --git a/spec/app/domain/authentication/authn-oidc/pkce_support_feature/data_objects/authenticator.rb b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/data_objects/authenticator.rb new file mode 100644 index 0000000000..f499c588c3 --- /dev/null +++ b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/data_objects/authenticator.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe('Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator') do + let(:default_args) do + { + provider_uri: 'https://foo.bar.com/baz', + client_id: 'client-id-123', + client_secret: 'client-secret-123', + claim_mapping: 'email', + account: 'default', + service_id: 'my-authenticator' + } + end + let(:args) { default_args } + + let(:authenticator) { Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator.new(**args) } + + describe '.scope' do + context 'with default initializer' do + it { expect(authenticator.scope).to eq('openid email profile') } + end + + context 'when initialized with a string argument' do + let(:args) { default_args.merge({ provider_scope: 'foo' }) } + it { expect(authenticator.scope).to eq('openid email profile foo') } + end + + context 'when initialized with a non-string argument' do + let(:args) { default_args.merge({ provider_scope: 1 }) } + it { expect(authenticator.scope).to eq('openid email profile 1') } + end + + context 'when initialized with a duplicated argument' do + let(:args) { default_args.merge({ provider_scope: 'profile' }) } + it { expect(authenticator.scope).to eq('openid email profile') } + end + + context 'when initialized with an array argument' do + context 'single value array' do + let(:args) { default_args.merge({ provider_scope: 'foo' }) } + it { expect(authenticator.scope).to eq('openid email profile foo') } + end + + context 'multi-value array' do + let(:args) { default_args.merge({ provider_scope: 'foo bar' }) } + it { expect(authenticator.scope).to eq('openid email profile foo bar') } + end + end + end + + describe '.name' do + context 'when name is missing' do + it { expect(authenticator.name).to eq('My Authenticator') } + end + context 'when name is present' do + let(:args) { default_args.merge(name: 'foo') } + it { expect(authenticator.name).to eq('foo') } + end + end + + describe '.resource_id' do + context 'correctly renders' do + it { expect(authenticator.resource_id).to eq('default:webservice:conjur/authn-oidc/my-authenticator') } + end + end + + describe '.response_type' do + context 'with default initializer' do + it { expect(authenticator.response_type).to eq('code') } + end + end +end diff --git a/spec/app/domain/authentication/authn-oidc/pkce_support_feature/resolve_identity_spec.rb b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/resolve_identity_spec.rb new file mode 100644 index 0000000000..83943542fb --- /dev/null +++ b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/resolve_identity_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe(' Authentication::AuthnOidc::PkceSupportFeature::ResolveIdentity') do + let(:resolve_identity) do + Authentication::AuthnOidc::PkceSupportFeature::ResolveIdentity.new + end + + describe('#call') do + let(:valid_role) do + instance_double(::Role).tap do |double| + allow(double).to receive(:id).and_return('rspec:user:alice') + allow(double).to receive(:resource?).and_return(true) + end + end + + context 'when identity matches a role ID' do + it 'returns the matching role' do + expect( + resolve_identity.call( + account: 'rspec', + identity: 'alice', + allowed_roles: [ valid_role ] + ).id + ).to eq('rspec:user:alice') + end + + context 'when it includes roles without resources' do + it 'returns the matching role' do + expect( + resolve_identity.call( + account: 'rspec', + identity: 'alice', + allowed_roles: [ + instance_double(::Role).tap do |double| + allow(double).to receive(:id).and_return('rspec:user:alice') + allow(double).to receive(:resource?).and_return(false) + end, + valid_role + ] + ).id + ).to eq('rspec:user:alice') + end + end + + context 'when the accounts are different' do + it 'returns the matching role' do + expect( + resolve_identity.call( + account: 'rspec', + identity: 'alice', + allowed_roles: [ + instance_double(::Role).tap do |double| + allow(double).to receive(:id).and_return('foo:user:alice') + allow(double).to receive(:resource?).and_return(true) + end, + valid_role + ] + ).id + ).to eq('rspec:user:alice') + end + end + end + + context 'when the provided identity does not match a role or annotation' do + it 'raises the error RoleNotFound' do + expect { + resolve_identity.call( + account: 'rspec', + identity: 'alice', + allowed_roles: [ + instance_double(::Role).tap do |double| + allow(double).to receive(:id).and_return('rspec:user:bob') + allow(double).to receive(:resource?).and_return(true) + end, + instance_double(::Role).tap do |double| + allow(double).to receive(:id).and_return('rspec:user:chad') + allow(double).to receive(:resource?).and_return(true) + allow(double).to receive(:resource).and_return( + instance_double(::Resource).tap do |resource| + allow(resource).to receive(:annotation).with('authn-oidc/identity').and_return('chad.example') + end + ) + end + ] + ) + }.to raise_error( + Errors::Authentication::Security::RoleNotFound, + /CONJ00007E 'alice' not found/ + ) + end + end + + end +end diff --git a/spec/app/domain/authentication/authn-oidc/pkce_support_feature/strategy_rspec.rb b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/strategy_rspec.rb new file mode 100644 index 0000000000..62040a41bf --- /dev/null +++ b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/strategy_rspec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe(' Authentication::AuthnOidc::PkceSupportFeature::Strategy') do + + let(:jwt) { double(raw_attributes: { claim_mapping: "alice" }) } + + let(:authenticator) do + Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator.new( + account: "cucumber", + service_id: "foo", + redirect_uri: "http://conjur/authn-oidc/cucumber/authenticate", + provider_uri: "http://test", + name: "foo", + client_id: "ConjurClient", + client_secret: 'client_secret', + claim_mapping: mapping + ) + end + + let(:client) do + class_double(::Authentication::AuthnOidc::PkceSupportFeature::Client).tap do |double| + allow(double).to receive(:new).and_return(current_client) + end + end + + let(:current_client) do + instance_double(::Authentication::AuthnOidc::PkceSupportFeature::Client).tap do |double| + allow(double).to receive(:callback).and_return(jwt) + end + end + + let(:strategy) do + Authentication::AuthnOidc::PkceSupportFeature::Strategy.new( + authenticator: authenticator, + client: client + ) + end + + describe('#callback') do + context 'when a role_id matches the identity exist' do + let(:mapping) { "claim_mapping" } + it 'returns the role' do + expect(strategy.callback(nonce: 'nonce', code: 'code', code_verifier: 'foo')) + .to eq('alice') + end + end + + context "when the claiming matching in the token doesn't match the jwt" do + let(:mapping) { 'wrong_mapping' } + it 'raises a `IdTokenClaimNotFoundOrEmpty` error' do + expect { strategy.callback(code: 'code', nonce: 'nonce', code_verifier: 'foo') } + .to raise_error(Errors::Authentication::AuthnOidc::IdTokenClaimNotFoundOrEmpty) + end + end + + context 'when required parameters are missing' do + let(:mapping) { '' } + context 'when code is missing' do + it 'raises a `MissingRequestParam` error' do + expect { strategy.callback(nonce: 'nonce', code_verifier: 'foo') } + .to raise_error(Errors::Authentication::RequestBody::MissingRequestParam) + end + end + context 'when nonce is missing' do + it 'raises a `MissingRequestParam` error' do + expect { strategy.callback(code: 'code', code_verifier: 'foo') } + .to raise_error(Errors::Authentication::RequestBody::MissingRequestParam) + end + end + context 'when code_verifier is missing' do + it 'raises a `MissingRequestParam` error' do + expect { strategy.callback(nonce: 'nonce', code: 'code') } + .to raise_error(Errors::Authentication::RequestBody::MissingRequestParam) + end + end + end + end +end diff --git a/spec/app/domain/authentication/authn-oidc/pkce_support_feature/views/providor_context_spec.rb b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/views/providor_context_spec.rb new file mode 100644 index 0000000000..f499c588c3 --- /dev/null +++ b/spec/app/domain/authentication/authn-oidc/pkce_support_feature/views/providor_context_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe('Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator') do + let(:default_args) do + { + provider_uri: 'https://foo.bar.com/baz', + client_id: 'client-id-123', + client_secret: 'client-secret-123', + claim_mapping: 'email', + account: 'default', + service_id: 'my-authenticator' + } + end + let(:args) { default_args } + + let(:authenticator) { Authentication::AuthnOidc::PkceSupportFeature::DataObjects::Authenticator.new(**args) } + + describe '.scope' do + context 'with default initializer' do + it { expect(authenticator.scope).to eq('openid email profile') } + end + + context 'when initialized with a string argument' do + let(:args) { default_args.merge({ provider_scope: 'foo' }) } + it { expect(authenticator.scope).to eq('openid email profile foo') } + end + + context 'when initialized with a non-string argument' do + let(:args) { default_args.merge({ provider_scope: 1 }) } + it { expect(authenticator.scope).to eq('openid email profile 1') } + end + + context 'when initialized with a duplicated argument' do + let(:args) { default_args.merge({ provider_scope: 'profile' }) } + it { expect(authenticator.scope).to eq('openid email profile') } + end + + context 'when initialized with an array argument' do + context 'single value array' do + let(:args) { default_args.merge({ provider_scope: 'foo' }) } + it { expect(authenticator.scope).to eq('openid email profile foo') } + end + + context 'multi-value array' do + let(:args) { default_args.merge({ provider_scope: 'foo bar' }) } + it { expect(authenticator.scope).to eq('openid email profile foo bar') } + end + end + end + + describe '.name' do + context 'when name is missing' do + it { expect(authenticator.name).to eq('My Authenticator') } + end + context 'when name is present' do + let(:args) { default_args.merge(name: 'foo') } + it { expect(authenticator.name).to eq('foo') } + end + end + + describe '.resource_id' do + context 'correctly renders' do + it { expect(authenticator.resource_id).to eq('default:webservice:conjur/authn-oidc/my-authenticator') } + end + end + + describe '.response_type' do + context 'with default initializer' do + it { expect(authenticator.response_type).to eq('code') } + end + end +end diff --git a/spec/app/domain/authentication/util/namespace_selector_spec.rb b/spec/app/domain/authentication/util/namespace_selector_spec.rb index b001903438..a16c2b0e77 100644 --- a/spec/app/domain/authentication/util/namespace_selector_spec.rb +++ b/spec/app/domain/authentication/util/namespace_selector_spec.rb @@ -6,12 +6,26 @@ describe '#select' do context 'when type is a supported authenticator type' do context 'when type is `authn-oidc`' do - it 'returns the valid namespace' do - expect( - Authentication::Util::NamespaceSelector.select( - authenticator_type: 'authn-oidc' - ) - ).to eq('Authentication::AuthnOidc::V2') + context 'when pkce support feature flag is enabled' do + it 'returns the valid namespace' do + expect( + Authentication::Util::NamespaceSelector.select( + authenticator_type: 'authn-oidc', + pkce_support_enabled: true + ) + ).to eq('Authentication::AuthnOidc::PkceSupportFeature') + end + end + + context 'when pkce support feature flag is disabled' do + it 'returns the valid namespace' do + expect( + Authentication::Util::NamespaceSelector.select( + authenticator_type: 'authn-oidc', + pkce_support_enabled: false + ) + ).to eq('Authentication::AuthnOidc::V2') + end end end end diff --git a/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-expired_code-valid_oidc_credentials.yml b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-expired_code-valid_oidc_credentials.yml new file mode 100644 index 0000000000..68c08d72ab --- /dev/null +++ b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-expired_code-valid_oidc_credentials.yml @@ -0,0 +1,100 @@ +--- +http_interactions: +- request: + method: post + uri: https://dev-92899796.okta.com/oauth2/default/v1/token + body: + encoding: UTF-8 + string: grant_type=authorization_code&code=SNSPeiQJ0-D6nUHTg-Ht9ZoDxIaaWBB80pnYuXY2VxU&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauthn-oidc%2Fokta-2%2Fcucumber%2Fauthenticate&scope=true&nonce=7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d&code_verifier=c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d + headers: + User-Agent: + - Rack::OAuth2 (1.19.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Fri, 30 Sep 2022 17:19:21 GMT + Authorization: + - Basic MG9hM3czeGlnNnJIaXU5eVQ1ZDc6ZTM0OUJNVFRJcExPLXJQdVBxTExrTHlIX3BPLWxvVXpoSVZKQ3JIag== + Content-Type: + - application/x-www-form-urlencoded + response: + status: + code: 400 + message: Bad Request + headers: + Date: + - Fri, 30 Sep 2022 17:19:22 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Okta-Request-Id: + - YzclGT9F51eImbj6lTlN0AAACl4 + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy-Report-Only: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''nonce-8XxNsgGJ7tleqCeom6XuCfv4ASDp8xU7hm86MEgqU-0'' + ''unsafe-eval'' ''self'' dev-92899796.okta.com *.oktacdn.com; style-src ''unsafe-inline'' + ''self'' dev-92899796.okta.com *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + X-Rate-Limit-Limit: + - '300' + X-Rate-Limit-Remaining: + - '299' + X-Rate-Limit-Reset: + - '1664558421' + Cache-Control: + - no-cache, no-store + Pragma: + - no-cache + Expires: + - '0' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + Set-Cookie: + - JSESSIONID=8B02BEDFF41B92741E6EEBB6CA0B2D80; Path=/; Secure; HttpOnly + - autolaunch_triggered=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + - sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + body: + encoding: UTF-8 + string: '{"error":"invalid_grant","error_description":"The authorization code + is invalid or has expired."}' + recorded_at: Fri, 30 Sep 2022 17:19:22 GMT +recorded_with: VCR 6.1.0 diff --git a/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-invalid_code_verifier.yml b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-invalid_code_verifier.yml new file mode 100644 index 0000000000..779411772a --- /dev/null +++ b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-invalid_code_verifier.yml @@ -0,0 +1,86 @@ +--- +http_interactions: +- request: + method: post + uri: https://dev-92899796.okta.com/oauth2/default/v1/token + body: + encoding: UTF-8 + string: grant_type=authorization_code&code=GV48_SF4a19ghvBhVbbSG3Lr8BuFl8PhWVPZSbokV2o&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauthn-oidc%2Fokta-2%2Fcucumber%2Fauthenticate&scope=true&nonce=3e6bd5235e4692b37ca1f04cb01b6e0cb177aa20dcef19e89f&code_verifier=bad-code-verifier + headers: + User-Agent: + - Rack::OAuth2 (1.19.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Mon, 17 Oct 2022 17:23:30 GMT + Authorization: + - Basic MG9hM3czeGlnNnJIaXU5eVQ1ZDc6ZTM0OUJNVFRJcExPLXJQdVBxTExrTHlIX3BPLWxvVXpoSVZKQ3JIag== + Content-Type: + - application/x-www-form-urlencoded + response: + status: + code: 400 + message: Bad Request + headers: + Date: + - Mon, 17 Oct 2022 17:24:19 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Okta-Request-Id: + - Y02Pw8hcjYzIfnEoK4T9AAAACQU + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''; report-uri https://okta.report-uri.com/r/d/csp/enforce; + report-to csp' + X-Rate-Limit-Limit: + - '300' + X-Rate-Limit-Remaining: + - '299' + X-Rate-Limit-Reset: + - '1666027519' + Cache-Control: + - no-cache, no-store + Pragma: + - no-cache + Expires: + - '0' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + Set-Cookie: + - JSESSIONID=9ABDA66E3EA85BE59AB6233C8DC28B7F; Path=/; Secure; HttpOnly + - autolaunch_triggered=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + - sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + body: + encoding: UTF-8 + string: '{"error":"invalid_grant","error_description":"PKCE verification failed."}' + recorded_at: Mon, 17 Oct 2022 17:23:30 GMT +recorded_with: VCR 6.1.0 diff --git a/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-used_code-valid_oidc_credentials.yml b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-used_code-valid_oidc_credentials.yml new file mode 100644 index 0000000000..2a3cc5d4b2 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-used_code-valid_oidc_credentials.yml @@ -0,0 +1,100 @@ +--- +http_interactions: +- request: + method: post + uri: https://dev-92899796.okta.com/oauth2/default/v1/token + body: + encoding: UTF-8 + string: grant_type=authorization_code&code=-QGREc_SONbbJIKdbpyYudA13c9PZlgqdxowkf45LOw&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauthn-oidc%2Fokta-2%2Fcucumber%2Fauthenticate&scope=true&nonce=7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d&code_verifier=c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d + headers: + User-Agent: + - Rack::OAuth2 (1.19.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Fri, 30 Sep 2022 17:18:17 GMT + Authorization: + - Basic MG9hM3czeGlnNnJIaXU5eVQ1ZDc6ZTM0OUJNVFRJcExPLXJQdVBxTExrTHlIX3BPLWxvVXpoSVZKQ3JIag== + Content-Type: + - application/x-www-form-urlencoded + response: + status: + code: 400 + message: Bad Request + headers: + Date: + - Fri, 30 Sep 2022 17:18:18 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Okta-Request-Id: + - Yzck2nN7s2AQGePZY8c2gQAAB94 + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy-Report-Only: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''nonce-ZUSAcrGWJJO_LGxCGbys2QL4CSrTAzGBje02qK26sdc'' + ''unsafe-eval'' ''self'' dev-92899796.okta.com *.oktacdn.com; style-src ''unsafe-inline'' + ''self'' dev-92899796.okta.com *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + X-Rate-Limit-Limit: + - '300' + X-Rate-Limit-Remaining: + - '299' + X-Rate-Limit-Reset: + - '1664558358' + Cache-Control: + - no-cache, no-store + Pragma: + - no-cache + Expires: + - '0' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + Set-Cookie: + - JSESSIONID=69068D777A11001AB7E7539E934C9A20; Path=/; Secure; HttpOnly + - autolaunch_triggered=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + - sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + body: + encoding: UTF-8 + string: '{"error":"invalid_grant","error_description":"The authorization code + is invalid or has expired."}' + recorded_at: Fri, 30 Sep 2022 17:18:18 GMT +recorded_with: VCR 6.1.0 diff --git a/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-valid_oidc_credentials.yml b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-valid_oidc_credentials.yml new file mode 100644 index 0000000000..d0df4e7d8c --- /dev/null +++ b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_callback-valid_oidc_credentials.yml @@ -0,0 +1,170 @@ +--- +http_interactions: +- request: + method: post + uri: https://dev-92899796.okta.com/oauth2/default/v1/token + body: + encoding: UTF-8 + string: grant_type=authorization_code&code=-QGREc_SONbbJIKdbpyYudA13c9PZlgqdxowkf45LOw&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauthn-oidc%2Fokta-2%2Fcucumber%2Fauthenticate&scope=true&nonce=7efcbba36a9b96fdb5285a159665c3d382abd8b6b3288fcc8d&code_verifier=c1de7f1251849accd99d4839d79a637561b1181b909ed7dc1d + headers: + User-Agent: + - Rack::OAuth2 (1.19.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Fri, 30 Sep 2022 17:02:17 GMT + Authorization: + - Basic MG9hM3czeGlnNnJIaXU5eVQ1ZDc6ZTM0OUJNVFRJcExPLXJQdVBxTExrTHlIX3BPLWxvVXpoSVZKQ3JIag== + Content-Type: + - application/x-www-form-urlencoded + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 30 Sep 2022 17:14:38 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Okta-Request-Id: + - Yzcj_ieURr1wLPVErCrikAAAC84 + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy-Report-Only: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''nonce-ZFIOD_nd7I1XFja243oXrDS25KhzLla9BnlkbWhH1OU'' + ''unsafe-eval'' ''self'' dev-92899796.okta.com *.oktacdn.com; style-src ''unsafe-inline'' + ''self'' dev-92899796.okta.com *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + X-Rate-Limit-Limit: + - '300' + X-Rate-Limit-Remaining: + - '299' + X-Rate-Limit-Reset: + - '1664558138' + Cache-Control: + - no-cache, no-store + Pragma: + - no-cache + Expires: + - '0' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Robots-Tag: + - noindex,nofollow + Set-Cookie: + - JSESSIONID=392B5556071EB935B2684D3AF2D492E5; Path=/; Secure; HttpOnly + - autolaunch_triggered=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + - sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/ + body: + encoding: UTF-8 + string: '{"token_type":"Bearer","expires_in":3600,"access_token":"eyJraWQiOiI3NE5OR2R0RC1rQ2p1b2tZRFRVaG05VVQ3SXRjaUY5S1dEMVNEVktyZW80IiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULkNoVzZRVU1iYllmalZZdGhZT0MxT2FqR3gzNk52NUIteWFTcnNtdTBBM3ciLCJpc3MiOiJodHRwczovL2Rldi05Mjg5OTc5Ni5va3RhLmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE2NjQ1NTgwNzgsImV4cCI6MTY2NDU2MTY3OCwiY2lkIjoiMG9hM3czeGlnNnJIaXU5eVQ1ZDciLCJ1aWQiOiIwMHU1Z2tvNmRmNDRqMmZHcDVkNyIsInNjcCI6WyJlbWFpbCIsInByb2ZpbGUiLCJvcGVuaWQiXSwiYXV0aF90aW1lIjoxNjY0NTU3ODUzLCJzdWIiOiJ0ZXN0LnVzZXIzQG15Y29tcGFueS5jb20ifQ.hxsTC4_AO9M-KzYDmOac9KK2cqdvZVEXtB0jagLMEkfdkZ4XTm2h8UBcbdTlwKa1UWbM5y9JuwTeQ5_J3FJP-YMkzsfcrYvvYkAd0V7Dw39CjRh5SFU7y-_ReCWGUp5Ni__yLcfmRl0EUAufs-JauyPX3GiknYD4QPZsfjCl0qGfZ7QrPOFOO4IJu7DuKCNVb-fR9aTS4VGEdff62idOoTKexXAuIaWw9yicSTULGFpIxeyPiVMjRQtpuZAl15rdmdZi2fJqlMEY7fbMY5OzgRcT-KCITk6vzY8DsoJosOZbNBYSvwHBilbcxJYMz3o9xn39ADZIvGX69M-3llbKRQ","scope":"email + profile openid","id_token":"eyJraWQiOiI3NE5OR2R0RC1rQ2p1b2tZRFRVaG05VVQ3SXRjaUY5S1dEMVNEVktyZW80IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIwMHU1Z2tvNmRmNDRqMmZHcDVkNyIsIm5hbWUiOiJSb2JlcnQgV2lsbGlhbXMgSUlJIiwiZW1haWwiOiJ0ZXN0LnVzZXIzQG15Y29tcGFueS5jb20iLCJ2ZXIiOjEsImlzcyI6Imh0dHBzOi8vZGV2LTkyODk5Nzk2Lm9rdGEuY29tL29hdXRoMi9kZWZhdWx0IiwiYXVkIjoiMG9hM3czeGlnNnJIaXU5eVQ1ZDciLCJpYXQiOjE2NjQ1NTgwNzgsImV4cCI6MTY2NDU2MTY3OCwianRpIjoiSUQuMFE0Uy1xTEpCaW5fbi1PUHQySVBzYVhOaE8yMjRrLVpqVlFpLVNodUxNNCIsImFtciI6WyJwd2QiXSwiaWRwIjoiMDBvM3cxeTh0dXVhWWNDVDc1ZDciLCJub25jZSI6IjdlZmNiYmEzNmE5Yjk2ZmRiNTI4NWExNTk2NjVjM2QzODJhYmQ4YjZiMzI4OGZjYzhkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdC51c2VyM0BteWNvbXBhbnkuY29tIiwiYXV0aF90aW1lIjoxNjY0NTU3ODUzLCJhdF9oYXNoIjoiY3d4OTNFaTJXdVYzRzZ3MFRiYmJBZyJ9.SrRuxy27rvHveLtC_2lWFpTWXTSc1I4UCDs4jd1pUKD4iBldSLXk7fWEY7VNQFqtHGidOSBD4rOUiSCpphLKHGfLgFLyRlqjU2k4nG8BGqslaEuiN2YhsVDECAjjjctDUazSaDHKsYiPCt4f9w7DeCWShDkG7QLH-CHlUkYzzeBz3bXmxYm2h1LzhMstZldvFTbcB-pKtKauavzhYK2NIb8k86EpXr7tHQ4wbgOUzX6_m8lw-9gMhGZc32vxROiaqu776DmZ7doQwLx3UN3NqRIdhlCThCy4seF_3_YanE73PAhCRNRTiEBZ5svct7XGBw8Vl5VElV7b6lAC21Ni5w"}' + recorded_at: Fri, 30 Sep 2022 17:02:17 GMT +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/v1/keys + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - OpenIDConnect (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Fri, 30 Sep 2022 17:02:17 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 30 Sep 2022 17:14:39 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=7730945, must-revalidate + Expires: + - Thu, 29 Dec 2022 04:43:44 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Yzcj_2SzA_OGwA_yMGySgwAAAek + body: + encoding: UTF-8 + string: '{"keys":[{"kty":"RSA","alg":"RS256","kid":"74NNGdtD-kCjuokYDTUhm9UT7ItciF9KWD1SDVKreo4","use":"sig","e":"AQAB","n":"zhlW4oIEvu5wcGQ4ROfpumaqQWluAGi6FIV1Hi-gFuA9Z_Gxw4x3fQnZbpsXsuWlu2Ivfl-9RCiu2AGcpUiH7cX84BWTRtwdOuPXMvOx_g1EUVna1IdVbXSsvWzgi0gOHsv9ZGyoq9BPRccn4m69824SvoAsIi3m-Jr0W3RFe3dCojkGDmrDiH7h8mhR_CjG2IRaQVxc0OqhwOYGAIsivhKQB7YjxqR3fMyUYEpJg3285r5r0-05slJDcUfJ3kILUgAvBBx0fZdLdopHij7hWFaPPKWHW7gKvFlX8h2_IMFMkaGlCF0XQ9S35hiQa12Nqf9frEDa48qcBmUweW67Rw"},{"kty":"RSA","alg":"RS256","kid":"_I6KGcucpmmj1en6JMfUuFB7SDKergpmgfP5nOBucxI","use":"sig","e":"AQAB","n":"40IMCS23bTWYL04XSZ-lwwnKuxVpkULRZYFxUrsYSXt5O-Tob52kIfw_QV_I3qNTZqjbfzSmokifNGF8_O1sQVuENE0vDpxenh05eV8h497sunnhsuOLkl4Y0CqgLWcfH-4qgF8musOPsVBdQIZ7SQ4BNMupgAkgyxENynDDlM4GeAwhu7TYtmtA2eOsj4ut6GrLNk-6KjhHvllmVdH09RJox5cz5atwTNI_LkwK9AHRLCaaM27KNLX498kFDTLPfB27zGwwODLuOy0i6XHskf4fnzrL-ttXliVtt0zgfMKPBCPxkm8Ynk7FRZbKisZRwl82aSyDWfZo-gHU2-U8Bw"}]}' + recorded_at: Fri, 30 Sep 2022 17:02:17 GMT +recorded_with: VCR 6.1.0 diff --git a/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_initialization.yml b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_initialization.yml new file mode 100644 index 0000000000..3080a4f574 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_initialization.yml @@ -0,0 +1,348 @@ +--- +http_interactions: +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Thu, 06 Oct 2022 22:13:54 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 06 Oct 2022 22:13:55 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Fri, 07 Oct 2022 22:13:55 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Yz9TI_MTaB3_WDnGc1HzWAAADII + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Thu, 06 Oct 2022 22:13:55 GMT +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Thu, 06 Oct 2022 22:13:55 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 06 Oct 2022 22:13:56 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Fri, 07 Oct 2022 22:13:56 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Yz9TJACEb_A1PbEeXb-UlAAACpI + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Thu, 06 Oct 2022 22:13:56 GMT +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Thu, 06 Oct 2022 22:13:56 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 06 Oct 2022 22:13:56 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Fri, 07 Oct 2022 22:13:56 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Yz9TJJEklkO44lgEvnjWdwAABHI + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Thu, 06 Oct 2022 22:13:56 GMT +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Thu, 06 Oct 2022 22:13:56 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 06 Oct 2022 22:13:57 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Fri, 07 Oct 2022 22:13:55 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Yz9TJdnAqVMcaT4YlT5ioQAAADM + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Thu, 06 Oct 2022 22:13:57 GMT +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Thu, 06 Oct 2022 22:13:57 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 06 Oct 2022 22:13:57 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Fri, 07 Oct 2022 22:13:56 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Yz9TJf0JGaDLjG0YjGuzMQAADco + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Thu, 06 Oct 2022 22:13:57 GMT +recorded_with: VCR 6.1.0 diff --git a/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_load.yml b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_load.yml new file mode 100644 index 0000000000..dbf9497344 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/client_load.yml @@ -0,0 +1,72 @@ +--- +http_interactions: +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Sat, 01 Oct 2022 17:02:17 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Fri, 07 Oct 2022 21:10:51 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com https://oinmanager.okta.com + data:; script-src ''unsafe-inline'' ''unsafe-eval'' ''self'' dev-92899796.okta.com + *.oktacdn.com; style-src ''unsafe-inline'' ''self'' dev-92899796.okta.com + *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + pendo-static-5391521872216064.storage.googleapis.com; frame-src ''self'' dev-92899796.okta.com + dev-92899796-admin.okta.com login.okta.com; img-src ''self'' dev-92899796.okta.com + *.oktacdn.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io + pendo-static-5634101834153984.storage.googleapis.com pendo-static-5391521872216064.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Sat, 08 Oct 2022 21:10:51 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Y0CV2zAVa-KLmseiKVuYoQAABrc + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Sat, 01 Oct 2022 17:02:17 GMT +recorded_with: VCR 6.1.0 diff --git a/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/discovery_endpoint-valid_oidc_credentials.yml b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/discovery_endpoint-valid_oidc_credentials.yml new file mode 100644 index 0000000000..638c583e19 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/authenticators/authn-oidc/pkce_support_feature/discovery_endpoint-valid_oidc_credentials.yml @@ -0,0 +1,137 @@ +--- +http_interactions: +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Thu, 30 Jun 2022 20:53:10 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 30 Jun 2022 20:53:10 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + https://oinmanager.okta.com data:; script-src ''unsafe-inline'' ''unsafe-eval'' + ''self'' dev-92899796.okta.com *.oktacdn.com; style-src ''unsafe-inline'' + ''self'' dev-92899796.okta.com *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com; + frame-src ''self'' dev-92899796.okta.com dev-92899796-admin.okta.com login.okta.com; + img-src ''self'' dev-92899796.okta.com *.oktacdn.com *.tiles.mapbox.com *.mapbox.com + app.pendo.io data.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Tue, 12 Jul 2022 16:31:08 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - YsxQTEzo3y4Lo9fSGY_LawAABPE + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Thu, 30 Jun 2022 20:53:10 GMT +- request: + method: get + uri: https://dev-92899796.okta.com/oauth2/default/.well-known/openid-configuration + body: + encoding: UTF-8 + string: '' + headers: + User-Agent: + - SWD (1.3.0) (2.8.3, ruby 3.0.4 (2022-04-12)) + Accept: + - "*/*" + Date: + - Thu, 30 Jun 2022 20:53:10 GMT + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 30 Jun 2022 20:53:11 GMT + Content-Type: + - application/json + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - nginx + Public-Key-Pins-Report-Only: + - pin-sha256="r5EfzZxQVvQpKo3AgYRaT7X2bDO/kj3ACwmxfdT2zt8="; pin-sha256="MaqlcUgk2mvY/RFSGeSwBRkI+rZ6/dxe/DuQfBT/vnQ="; + pin-sha256="72G5IEvDEWn+EThf3qjR7/bQSWaS2ZSLqolhnO6iyJI="; pin-sha256="rrV6CLCCvqnk89gWibYT0JO6fNQ8cCit7GGoiVTjCOg="; + max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly" + X-Xss-Protection: + - '0' + P3p: + - CP="HONK" + Content-Security-Policy: + - 'default-src ''self'' dev-92899796.okta.com *.oktacdn.com; connect-src ''self'' + dev-92899796.okta.com dev-92899796-admin.okta.com *.oktacdn.com *.mixpanel.com + *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com + https://oinmanager.okta.com data:; script-src ''unsafe-inline'' ''unsafe-eval'' + ''self'' dev-92899796.okta.com *.oktacdn.com; style-src ''unsafe-inline'' + ''self'' dev-92899796.okta.com *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com; + frame-src ''self'' dev-92899796.okta.com dev-92899796-admin.okta.com login.okta.com; + img-src ''self'' dev-92899796.okta.com *.oktacdn.com *.tiles.mapbox.com *.mapbox.com + app.pendo.io data.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com + data: blob:; font-src ''self'' dev-92899796.okta.com data: *.oktacdn.com fonts.gstatic.com; + frame-ancestors ''self''' + Expect-Ct: + - report-uri="https://oktaexpectct.report-uri.com/r/t/ct/reportOnly", max-age=0 + Cache-Control: + - max-age=86400, must-revalidate + Expires: + - Fri, 01 Jul 2022 20:53:11 GMT + Vary: + - Origin + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=315360000; includeSubDomains + X-Okta-Request-Id: + - Yr4NN7wdOzHatG786KpGLQAACUY + body: + encoding: UTF-8 + string: '{"issuer":"https://dev-92899796.okta.com/oauth2/default","authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/authorize","token_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/token","userinfo_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/userinfo","registration_endpoint":"https://dev-92899796.okta.com/oauth2/v1/clients","jwks_uri":"https://dev-92899796.okta.com/oauth2/default/v1/keys","response_types_supported":["code","id_token","code + id_token","code token","id_token token","code id_token token"],"response_modes_supported":["query","fragment","form_post","okta_post_message"],"grant_types_supported":["authorization_code","implicit","refresh_token","password","urn:ietf:params:oauth:grant-type:device_code"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","profile","email","address","phone","offline_access","device_sso"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"claims_supported":["iss","ver","sub","aud","iat","exp","jti","auth_time","amr","idp","nonce","name","nickname","preferred_username","given_name","middle_name","family_name","email","email_verified","profile","zoneinfo","locale","address","phone_number","picture","website","gender","birthdate","updated_at","at_hash","c_hash"],"code_challenge_methods_supported":["S256"],"introspection_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/introspect","introspection_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"revocation_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/revoke","revocation_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","private_key_jwt","none"],"end_session_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/logout","request_parameter_supported":true,"request_object_signing_alg_values_supported":["HS256","HS384","HS512","RS256","RS384","RS512","ES256","ES384","ES512"],"device_authorization_endpoint":"https://dev-92899796.okta.com/oauth2/default/v1/device/authorize"}' + recorded_at: Thu, 30 Jun 2022 20:53:11 GMT +recorded_with: VCR 6.1.0