Skip to content

Commit

Permalink
Add cucumber test for AuthnOIDC with Identity
Browse files Browse the repository at this point in the history
  • Loading branch information
john-odonnell authored and gl-johnson committed Oct 16, 2023
1 parent 40401f4 commit 5496154
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 2 deletions.
13 changes: 13 additions & 0 deletions ci/test_suites/authenticators_oidc/secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@ ci:
OKTA_USERNAME: !var ci/okta/user/assigned/username
OKTA_PASSWORD: !var ci/okta/user/assigned/password

IDENTITY_CLIENT_ID: !var ci/identity/app/client-id
IDENTITY_CLIENT_SECRET: !var ci/identity/app/client-secret
IDENTITY_PROVIDER_URI: !var ci/identity/app/provider-uri
IDENTITY_USERNAME: !var ci/identity/user/assigned/username
IDENTITY_PASSWORD: !var ci/identity/user/assigned/password

development:
OKTA_CLIENT_ID: !var dev/okta/app/client-id
OKTA_CLIENT_SECRET: !var dev/okta/app/client-secret
OKTA_PROVIDER_URI: !var dev/okta/app/provider-uri
OKTA_USERNAME: !var dev/okta/user/assigned/username
OKTA_PASSWORD: !var dev/okta/user/assigned/password

IDENTITY_CLIENT_ID: !var dev/identity/app/client-id
IDENTITY_CLIENT_SECRET: !var dev/identity/app/client-secret
IDENTITY_PROVIDER_URI: !var dev/identity/app/provider-uri
# Identity integration tests depend on the following variables.
# IDENTITY_USERNAME: myUsername
# IDENTITY_PASSWORD: myP@ssword!
5 changes: 5 additions & 0 deletions ci/test_suites/authenticators_oidc/test
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ function _hydrate_all_env_args() {
"OKTA_PROVIDER_URI=${OKTA_PROVIDER_URI}oauth2/default"
"OKTA_USERNAME=$OKTA_USERNAME"
"OKTA_PASSWORD=$OKTA_PASSWORD"
"IDENTITY_CLIENT_ID=$IDENTITY_CLIENT_ID"
"IDENTITY_CLIENT_SECRET=$IDENTITY_CLIENT_SECRET"
"IDENTITY_PROVIDER_URI=$IDENTITY_PROVIDER_URI"
"IDENTITY_USERNAME=$IDENTITY_USERNAME"
"IDENTITY_PASSWORD=$IDENTITY_PASSWORD"
)
}

Expand Down
51 changes: 51 additions & 0 deletions cucumber/authenticators_oidc/features/authn_oidc_identity.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@authenticators_oidc
Feature: OIDC Authenticator V2 - Users can authenticate with Identity using OIDC

Background:
Given the following environment variables are available:
| context_variable | environment_variable | default_value |
| oidc_provider_uri | IDENTITY_PROVIDER_URI | |
| oidc_client_id | IDENTITY_CLIENT_ID | |
| oidc_client_secret | IDENTITY_CLIENT_SECRET | |
| oidc_redirect_url | IDENTITY_REDIRECT | http://localhost:3000/authn-oidc/identity/cucumber/authenticate |
| oidc_username | IDENTITY_USERNAME | |
| oidc_password | IDENTITY_PASSWORD | |

And I load a policy and enable an oidc user into group "conjur/authn-oidc/identity/users":
"""
- !policy
id: conjur/authn-oidc/identity
body:
- !webservice
annotations:
description: Authentication service for Identity, based on Open ID Connect.
- !variable provider-uri
- !variable client-id
- !variable client-secret
- !variable claim-mapping
- !variable state
- !variable nonce
- !variable redirect-uri
- !group users
- !permit
role: !group users
privilege: [ read, authenticate ]
resource: !webservice
"""
And I set the following conjur variables:
| variable_id | context_variable | default_value |
| conjur/authn-oidc/identity/provider-uri | oidc_provider_uri | |
| conjur/authn-oidc/identity/client-id | oidc_client_id | |
| conjur/authn-oidc/identity/client-secret | oidc_client_secret | |
| conjur/authn-oidc/identity/claim-mapping | | preferred_username |
| conjur/authn-oidc/identity/redirect-uri | oidc_redirect_url | |

@smoke
Scenario: Authenticating with Conjur using Identity
Given I retrieve OIDC configuration from the provider endpoint for "identity"
And I authenticate and fetch a code from Identity
When I authenticate via OIDC with code and service_id "identity"
Then the OIDC user has been authorized by conjur
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ Feature: OIDC Authenticator V2 - Users can authenticate with Okta using OIDC
Given I retrieve OIDC configuration from the provider endpoint for "okta"
And I authenticate and fetch a code from Okta
When I authenticate via OIDC with code and service_id "okta"
Then the okta user has been authorized by conjur
Then the OIDC user has been authorized by conjur
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,113 @@
@scenario_context.add(:redirect_uri, provider['redirect_uri'])
end

Given(/^I authenticate and fetch a code from Identity/) do
# A request to /Security/StartAuthentication begins the login process,
# and returns a list of authentication mechanisms to engage with.

host = URI(@scenario_context.get(:redirect_uri)).host
resp = start_auth_request(host, @scenario_context.get(:oidc_username))
resp_h = JSON.parse(resp.body)

if resp_h["Result"]["PodFqdn"]
resp = start_auth_request(resp_h["Result"]["PodFqdn"], @scenario_context.get(:oidc_username))
resp_h = JSON.parse(resp.body)
end

raise "Failed to retrieve OIDC code status: #{resp.code}" unless resp_h["success"]

session_id = resp_h["Result"]["SessionId"]
challenges = resp_h["Result"]["Challenges"]

# Usually, we would iterate through MFA challenges sequentially.
# For our purposes, though, we want to make sure to use the Password
# and Mobile Authenticator mechanisms.

password_mechanism = challenges[0]["Mechanisms"].detect { |m| m["PromptSelectMech"] == "Password" }
mobile_auth_mechanism = challenges[1]["Mechanisms"].detect { |m| m["PromptSelectMech"] == "Mobile Authenticator" }

# Advance Password-based authentication handshake.

password_body = JSON.generate({
"Action": "Answer",
"Answer": @scenario_context.get(:oidc_password),
"MechanismId": password_mechanism["MechanismId"],
"SessionId": session_id
})
resp = advance_auth_request(host, password_body)
resp_h = JSON.parse(resp.body)
raise "Failed to advance authentication: #{resp_h['Message']}" unless resp_h["success"]

# Begin temporary block
#
# Engaging with a Mobile Auth and polling for out-of-band authentication
# success is included temporarily, and is required for users that are required
# to perform MFA. Before merging, and service account should be made available
# for running this test in CI, and the account should not be bound by MFA.

# Advance Mobile Authenticator-based authentication handshake.

mobile_auth_body = JSON.generate({
"Action": "StartOOB",
"MechanismId": mobile_auth_mechanism["MechanismId"],
"SessionId": session_id
})
resp = advance_auth_request(host, mobile_auth_body)
resp_h = JSON.parse(resp.body)
raise "Failed to advance authentication: #{resp_h['Message']}" unless resp_h["success"]

puts "Dev env users: select #{resp_h['Result']['GeneratedAuthValue']} in your Identity notification"

# For 30 seconds, Poll for out-of-band authentication success.

poll_body = JSON.generate({
"Action": "Poll",
"MechanismId": mobile_auth_mechanism["MechanismId"],
"SessionId": session_id
})

token = ""
current = Time.current
while Time.current < current + 30
resp = advance_auth_request(host, poll_body)
resp_h = JSON.parse(resp.body)

next unless resp_h["Result"]["Summary"] == "LoginSuccess"

cookies = resp.get_fields('Set-Cookie')
token_cookie = cookies.detect { |c| c.start_with?(".ASPXAUTH") }
token = token_cookie.split('; ')[0].split('=')[1]
end
if token == ""
raise "Failed to advance authentication: please reattempt"
end

# End temporary block
#
# Make request to /Authorization endpoint with bearer token.

target = URI("#{@scenario_context.get(:redirect_uri)}&state=test-state")
resp = nil
until target.to_s.include?("localhost:3000/authn-oidc/identity/cucumber")
http = Net::HTTP.new(target.host, 443)
http.use_ssl = true
req = Net::HTTP::Get.new(target.request_uri)
req['Accept'] = '*/*'
req['Authorization'] = "Bearer #{token}"
resp = http.request(req)

target = URI(resp['location'].to_s)
end

if resp.is_a?(Net::HTTPRedirection)
parse_oidc_code(resp['location']).each do |key, value|
@scenario_context.set(key, value)
end
else
raise "Failed to retrieve OIDC code status: #{resp.code}"
end
end

Given(/^I authenticate and fetch a code from Okta/) do
uri = URI("https://#{URI(@scenario_context.get(:redirect_uri)).host}/api/v1/authn")
body = JSON.generate({ username: @scenario_context.get(:oidc_username), password: @scenario_context.get(:oidc_password) })
Expand Down Expand Up @@ -176,7 +283,7 @@
)
end

Then(/^the okta user has been authorized by conjur/) do
Then(/^the OIDC user has been authorized by conjur/) do
username = @scenario_context.get(:oidc_username)
expect(retrieved_access_token.username).to eq(username)
end
Expand Down
27 changes: 27 additions & 0 deletions cucumber/authenticators_oidc/features/support/authn_oidc_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,33 @@ def invalid_id_token
"invalididtoken"
end

def identity_request(uri, body)
http = Net::HTTP.new(uri.host, 443)
http.use_ssl = true

req = Net::HTTP::Post.new(uri.request_uri)
req['Accept'] = '*/*'
req['Content-Type'] = 'application/json'
req.body = body

http.request(req)
end

def start_auth_request(host, username)
body = JSON.generate({
"User": username,
"Version": "1.0"
})

uri = URI("https://#{host}/Security/StartAuthentication")
identity_request(uri, body)
end

def advance_auth_request(host, body)
uri = URI("https://#{host}/Security/AdvanceAuthentication")
identity_request(uri, body)
end

private

def parse_oidc_id_token
Expand Down
12 changes: 12 additions & 0 deletions dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ services:
# See https://github.com/DatabaseCleaner/database_cleaner#safeguards
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: "true"
BUNDLE_GEMFILE: /src/conjur-server/Gemfile
# Adding the following envvars allows users to run Cucumber tests for
# AuthnOIDC V2 with Okta and Identity from the dev environment.
OKTA_CLIENT_ID: ${OKTA_CLIENT_ID}
OKTA_CLIENT_SECRET: ${OKTA_CLIENT_SECRET}
OKTA_PROVIDER_URI: ${OKTA_PROVIDER_URI}oauth2/default
OKTA_USERNAME: ${OKTA_USERNAME}
OKTA_PASSWORD: ${OKTA_PASSWORD}
IDENTITY_CLIENT_ID: ${IDENTITY_CLIENT_ID}
IDENTITY_CLIENT_SECRET: ${IDENTITY_CLIENT_SECRET}
IDENTITY_PROVIDER_URI: ${IDENTITY_PROVIDER_URI}
IDENTITY_USERNAME: ${IDENTITY_USERNAME}
IDENTITY_PASSWORD: ${IDENTITY_PASSWORD}
cap_add:
- SYSLOG
ports:
Expand Down

0 comments on commit 5496154

Please sign in to comment.