Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: OIDC refresh token support #2664

Closed
wants to merge 9 commits into from

Conversation

john-odonnell
Copy link
Contributor

@john-odonnell john-odonnell commented Oct 14, 2022

Desired Outcome

This PR implements refresh token support for Conjur's OIDC V2 authenticator.

API Updates

Endpoint Method Input Response Body Optional Response Header Notes
/authenticate GET
  • code
  • nonce
  • code_verifier
Conjur access token X-OIDC-Refresh-Token Updated, Backwards-compatible with Refresh-less flows
/authenticate POST
  • code
  • nonce
  • code_verifier
Conjur access token X-OIDC-Refresh-Token New
/authenticate POST
  • refresh_token
  • nonce
Conjur access token X-OIDC-Refresh-Token New
/logout POST
  • refresh_token
  • nonce
  • state
  • redirect_uri
URI to OIDC provider session management endpoint New

Implemented Changes

Connected Issue/Story

CyberArk internal issue link: [ONYX-26608]

Definition of Done

At least 1 todo must be completed in the sections below for the PR to be
merged.

Changelog

  • The CHANGELOG has been updated, or
  • This PR does not include user-facing changes and doesn't require a
    CHANGELOG update

Test coverage

  • This PR includes new unit and integration tests to go with the code
    changes, or
  • The changes in this PR do not require tests

Documentation

  • Docs (e.g. READMEs) were updated in this PR
  • A follow-up issue to update official docs has been filed here: insert issue ID
  • This PR does not require updating any documentation

Behavior

  • This PR changes product behavior and has been reviewed by a PO, or
  • These changes are part of a larger initiative that will be reviewed later, or
  • No behavior was changed with this PR

Security

  • Security architect has reviewed the changes in this PR,
  • These changes are part of a larger initiative with a separate security review, or
  • There are no security aspects to these changes

@john-odonnell john-odonnell changed the title Authn OIDC V2: Enable refresh token response for auth code exchange Authn OIDC V2: Update auth code exchange for refresh token response Oct 14, 2022
@john-odonnell john-odonnell marked this pull request as ready for review October 14, 2022 17:30
@john-odonnell john-odonnell requested a review from a team as a code owner October 14, 2022 17:30
@john-odonnell john-odonnell marked this pull request as draft October 14, 2022 17:32
@john-odonnell
Copy link
Contributor Author

Maybe this shouldn't be merged yet. Probably shouldn't start distributing refresh tokens until we can either accept or revoke them.

- AuthenticationHandler class now expects a given Strategy to respond
  with the authenticating identity and a hash of response headers.
  These are passed through to the AuthenticationController.
- AuthnOidc V2 Client and Strategy classes are updated to meet this
  expectation.
- Includes RSpec and Cucumber tests validating the updated behavior.
@john-odonnell john-odonnell force-pushed the oidc-code-with-refresh branch from e8ab69d to d5fff30 Compare October 19, 2022 22:45
@@ -248,6 +249,12 @@ def render_authn_token(authn_token)
render(content_type => authn_token)
end

def set_headers(headers_h)
headers_h.each { |key, value|
response.set_header(key, value.to_s) unless key.blank? || value.blank?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthenticateController#set_headers refers to 'value' more than self (maybe move it to another class?)

unless identity.present?
raise Errors::Authentication::AuthnOidc::IdTokenClaimNotFoundOrEmpty,
@authenticator.claim_mapping
end
identity

return [identity, {}] if refresh_token.nil?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authentication::AuthnOidc::V2::Strategy#callback performs a nil-check

@@ -248,6 +249,12 @@
render(content_type => authn_token)
end

def set_headers(headers_h)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not prefix writer method names with set_.

@@ -248,6 +249,12 @@
render(content_type => authn_token)
end

def set_headers(headers_h)
headers_h.each { |key, value|
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using {...} for multi-line blocks.

unless identity.present?
raise Errors::Authentication::AuthnOidc::IdTokenClaimNotFoundOrEmpty,
@authenticator.claim_mapping
end
identity

return [identity, {}] if refresh_token.nil?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add empty line after guard clause.

Comment on lines +12 to 17
auth_token, headers_h = Authentication::Handler::AuthenticationHandler.new(
authenticator_type: params[:authenticator]
).call(
parameters: params.to_hash.symbolize_keys,
request_ip: request.ip
)
Copy link
Contributor Author

@john-odonnell john-odonnell Oct 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reconsidering if this is the interface we want. Authn-OIDC V2 is our only auth scheme that needs to set a response header - this means each other auth scheme converted to use the AuthenticationHandler pattern will be returning an empty set of headers.

To avoid passing the response object everywhere, I experimented with passing a lambda as an optional parameter to the call method, so that while other auth schemes can do this:

auth_token = Authentication::Handler::AuthenticationHandler.new(
  authenticator_type: params[:authenticator]
).call(
  parameters: params.to_hash.symbolize_keys,
  request_ip: request.ip
)

... Authn-OIDC V2 could do this:

auth_token = Authentication::Handler::AuthenticationHandler.new(
  authenticator_type: params[:authenticator]
).call(
  parameters: params.to_hash.symbolize_keys,
  request_ip: request.ip,
  set_header: lambda { |key, value|
    response.set_header(key, value) unless key.blank? or value.blank?
  }
)

... and the set_header block could be used in the OIDC-V2-specific Authentication::AuthnOidc::V2::Strategy class.

Refactor OIDC client to include refresh token exchange
@john-odonnell john-odonnell changed the title Authn OIDC V2: Update auth code exchange for refresh token response Feature: OIDC refresh token support Oct 26, 2022
decoded_id_token
end

def verify_id_token(decoded_id_token, nonce, refresh: false)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authentication::AuthnOidc::V2::Client#verify_id_token has boolean parameter 'refresh'


def verify_id_token(decoded_id_token, nonce, refresh: false)
expected_nonce = nonce
if refresh && decoded_id_token.raw_attributes['nonce'].nil?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authentication::AuthnOidc::V2::Client#verify_id_token is controlled by argument 'refresh'


def verify_id_token(decoded_id_token, nonce, refresh: false)
expected_nonce = nonce
if refresh && decoded_id_token.raw_attributes['nonce'].nil?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authentication::AuthnOidc::V2::Client#verify_id_token performs a nil-check

@codeclimate
Copy link

codeclimate bot commented Oct 26, 2022

Code Climate has analyzed commit 127967c and detected 11 issues on this pull request.

Here's the issue category breakdown:

Category Count
Complexity 8
Style 3

The test coverage on the diff in this pull request is 100.0% (50% is the threshold).

This pull request will bring the total coverage in the repository to 89.9%.

View more on Code Climate.

@john-odonnell
Copy link
Contributor Author

Closing. Refresh token support was abandoned in favor of a configurable Conjur access token TTL - see #2683.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants