Skip to content

Commit

Permalink
Merge 0f3a343 into 66257b7
Browse files Browse the repository at this point in the history
  • Loading branch information
pcmxgti authored Dec 1, 2023
2 parents 66257b7 + 0f3a343 commit d5a03e5
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 32 deletions.
9 changes: 8 additions & 1 deletion tests/unit/test_okta.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,9 @@ def test_create_authz_cookies():
"org": "acme",
"authorization_endpoint": "pytesturl",
"token_endpoint": "tokeneurl",
"nonce": "pytest",
"issuer": "pytest",
"ln": "pytest",
}
assert okta.create_authz_cookies(pytest_oauth2_config, pytest_oauth2_session_data) is None
from tokendito import okta
Expand Down Expand Up @@ -708,7 +711,9 @@ def test_get_oauth2_configuration(mocker):
"grant_types_supported": "authorization_code",
"request_parameter_supported": "pytest",
}
pytest_config = Config(okta={"client_id": "test_client_id", "org": "acme"})
pytest_config = Config(
okta={"client_id": "test_client_id", "org": "acme", "username": "pytest"}
)
mocker.patch.object(HTTP_client, "get", return_value=response)
assert okta.get_oauth2_configuration(pytest_config)["org"] == "acme"

Expand Down Expand Up @@ -739,6 +744,8 @@ def test_validate_oauth2_configuration():
"scopes_supported": "pytest",
"response_types_supported": "code",
"request_parameter_supported": "pytest",
"ln": "pytest",
"nonce": "pytest",
}
assert okta.validate_oauth2_configuration(pytest_oauth2_config) is None

Expand Down
77 changes: 46 additions & 31 deletions tokendito/okta.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import re
import sys
import time
from urllib.parse import urlencode
from urllib.parse import quote
from urllib.parse import urlparse

import bs4
Expand Down Expand Up @@ -149,7 +149,6 @@ def send_saml_request(saml_request):
Submit SAML request to IdP, and get the response back.
:param saml_request: dict with IdP post_url, relay_state, and saml_request
:param cookies: session cookies with `sid`
:returns: dict with with SP post_url, relay_state, and saml_response
"""
# Define the payload and headers for the request
Expand All @@ -172,8 +171,6 @@ def send_saml_request(saml_request):
# Use the HTTP client to make a GET request
response = HTTP_client.get(url, params=payload, headers=headers)

logger.debug(f"{base64.b64decode(payload['SAMLRequest'])}")

# Extract relevant information from the response to form the saml_response dictionary
saml_response = {
"response": extract_saml_response(response.text, raw=True),
Expand All @@ -184,6 +181,7 @@ def send_saml_request(saml_request):
# Mask sensitive values for logging purposes
user.add_sensitive_value_to_be_masked(saml_response["response"])

logger.debug(f"SAML response is {saml_response}")
# Return the formed SAML response
return saml_response

Expand All @@ -194,20 +192,33 @@ def create_authz_cookies(oauth2_config, oauth2_session_data):
Needed for SAML2 flow for OIE.
"""
session_token = HTTP_client.session.cookies.get("sessionToken")
try:
oauth2_url = f"{oauth2_config['org']}/oauth2/v1"
oauth2_config_reformatted = {
"responseType": "code",
"state": oauth2_session_data["state"],
"nonce": oauth2_config["nonce"],
"scopes": [
"openid",
"profile",
"email",
"okta.users.read.self",
"okta.users.manage.self",
"okta.internal.enduser.read",
"okta.internal.enduser.manage",
"okta.enduser.dashboard.read",
"okta.enduser.dashboard.manage",
],
"clientId": oauth2_config["client_id"],
"authorizeUrl": oauth2_config["authorization_endpoint"],
"tokenUrl": oauth2_config["token_endpoint"],
"scope": "openid",
"sessionToken": session_token,
"userInfoUrl": f"{oauth2_url}/userinfo",
"revokeUrl": f"{oauth2_url}/revoke",
"logoutUrl": f"{oauth2_url}/logout",
"urls": {
"issuer": oauth2_config["issuer"],
"authorizeUrl": oauth2_config["authorization_endpoint"],
"userInfoUrl": f"{oauth2_url}/userinfo",
"tokenUrl": oauth2_config["token_endpoint"],
"revokeUrl": f"{oauth2_url}/revoke",
"logoutUrl": f"{oauth2_url}/logout",
},
"ignoreSignature": False,
}
except KeyError as e:
logger.error(f"Missing key in config:{e}")
Expand All @@ -217,11 +228,13 @@ def create_authz_cookies(oauth2_config, oauth2_session_data):
domain = urlparse(oauth2_config["org"]).netloc
cookiejar.set(
"okta-oauth-redirect-params",
f"{{{urlencode(oauth2_config_reformatted)}}}",
quote(json.dumps(oauth2_config_reformatted, separators=(",", ":")), safe="{}:[]/"),
domain=domain,
path="/",
)
cookiejar.set("okta-oauth-state", oauth2_session_data["state"], domain=domain, path="/")
cookiejar.set("okta-oauth-nonce", oauth2_config["nonce"], domain=domain, path="/")
cookiejar.set("ln", oauth2_config["ln"], domain=domain, path="/")
HTTP_client.add_cookies(cookiejar) # add cookies


Expand All @@ -242,7 +255,6 @@ def send_saml_response(config, saml_response):
}
url = saml_response["post_url"]

logger.debug(f"{base64.b64decode(saml_response['response'])}")
# Log the SAML response details.
logger.debug(f"Sending SAML response to {url}")
# Use the HTTP client to make a POST request.
Expand All @@ -264,20 +276,19 @@ def send_saml_response(config, saml_response):
params = {"stateToken": state_token}
headers = {
"accept": "text/html,application/xhtml+xml,application/xml",
"content-type": "application/json",
}
response = HTTP_client.get(
# myurl, allow_redirects=False, params={"stateToken": state_token}
f"{config.okta['org']}/login/token/redirect",
params=params,
headers=headers,
)
logger.warning(
f"""
State token from {url}: {state_token}. TODO: need to go from this state token
to an idx cookies.
"""
)
if "idx" not in response.cookies:
logger.error(
f"Session cookie idx for {config.okta['org']} not found. Please file a bug."
)
logger.debug(f"Response: {response.headers}")
logger.debug(f"Response: {response.text}")
sys.exit(2)


def get_session_token(config, primary_auth, headers):
Expand Down Expand Up @@ -591,10 +602,12 @@ def get_oauth2_configuration(config):
headers = {"accept": "application/json"}
response = HTTP_client.get(url, headers=headers)
logger.debug(f"Authorization Server info: {response.json()}")
# todo: handle errors.n
# TODO: handle errors
oauth2_config = response.json()
oauth2_config["org"] = config.okta["org"]
oauth2_config["client_id"] = get_client_id(config)
oauth2_config["ln"] = config.okta["username"]
oauth2_config["nonce"] = "TODO"
validate_oauth2_configuration(oauth2_config)
return oauth2_config

Expand All @@ -614,6 +627,8 @@ def validate_oauth2_configuration(oauth2_config):
"scopes_supported",
"client_id",
"org",
"ln",
"nonce",
} # the authorization server must have these config elements
for item in mandadory_oauth2_config_items:
if item not in oauth2_config:
Expand Down Expand Up @@ -676,15 +691,15 @@ def idp_authenticate(config):
logger.error("Okta auth failed: unknown type.")
sys.exit(1)

if is_saml2_authentication(auth_properties):
# We may loop thru the saml2 servers until
# we find the authentication server.
saml2_authenticate(config, auth_properties)
elif local_authentication_enabled(auth_properties):
if local_authentication_enabled(auth_properties):
session_token = local_authenticate(config)
# authentication sends us a token
# which we then put in our session cookies
create_authn_cookies(config.okta["org"], session_token)
elif is_saml2_authentication(auth_properties):
# We may loop thru the saml2 servers until
# we find the authentication server.
saml2_authenticate(config, auth_properties)
else:
logger.error(f"{auth_properties['type']} login via IdP Discovery is not curretly supported")
sys.exit(1)
Expand Down Expand Up @@ -714,8 +729,8 @@ def access_control(config):
oauth2_config = get_oauth2_configuration(config)
oauth2_session_data = generate_oauth2_session_data(config.okta["org"])
create_authz_cookies(oauth2_config, oauth2_session_data)
# The flow says to initially call /authorize here, but that doesnt do anything...
# idp_authorize(oauth2_config, oauth2_session_data)
# The flow says to initially call /authorize here, but that doesnt do anything.
# We call it later, after we are authenticated.

idp_authenticate(config)

Expand Down Expand Up @@ -1056,7 +1071,7 @@ def mfa_challenge(config, headers, primary_auth):
:param primary_auth: primary authentication
:return: Okta MFA Session token after the successful entry of the code
"""
logger.debug("Handle user MFA challenges")
logger.debug("Handle user MFA challenge")
try:
mfa_options = primary_auth["_embedded"]["factors"]
except KeyError as error:
Expand Down

0 comments on commit d5a03e5

Please sign in to comment.