diff --git a/README.md b/README.md index 0b24bd9..60cd739 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# Tokendito +
@@ -26,11 +28,19 @@ tokens into your local `~/.aws/credentials` file. See [Releases](https://github.com/dowjones/tokendito/releases) for a detailed Changelog. +### Tokendito 2.4.0 + +Version 2.4.0 of Tokendito introduces the following new features: + +- Add support for Okta question MFA. +- Many bug fixes and contributions. + ### Tokendito 2.3.0 Version 2.3.0 of Tokendito introduces the following new features: - Basic OIE support while forcing Classic mode. +- Support for saving the Device Token ID for later reuse. - Misc bug fixes Note: This feature currently works with locally enabled OIE organizations, but it does not for Organizations with chained Authentication in mixed OIE/Classic environments. diff --git a/docs/README.md b/docs/README.md index b277771..d7614bf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -74,7 +74,8 @@ tokendito --profile engineer usage: tokendito [-h] [--version] [--configure] [--username OKTA_USERNAME] [--password OKTA_PASSWORD] [--profile USER_CONFIG_PROFILE] [--config-file USER_CONFIG_FILE] [--loglevel {DEBUG,INFO,WARN,ERROR}] [--log-output-file USER_LOG_OUTPUT_FILE] [--aws-config-file AWS_CONFIG_FILE] [--aws-output AWS_OUTPUT] [--aws-profile AWS_PROFILE] [--aws-region AWS_REGION] [--aws-role-arn AWS_ROLE_ARN] [--aws-shared-credentials-file AWS_SHARED_CREDENTIALS_FILE] - [--okta-org OKTA_ORG | --okta-tile OKTA_TILE] [--okta-mfa OKTA_MFA] [--okta-mfa-response OKTA_MFA_RESPONSE] [--use-device-token] [--quiet] + [--okta-org OKTA_ORG | --okta-tile OKTA_TILE] [--okta-client-id OKTA_CLIENT_ID] [--okta-mfa OKTA_MFA] [--okta-mfa-response OKTA_MFA_RESPONSE] + [--use-device-token] [--quiet] Gets an STS token to use with the AWS CLI and SDK. @@ -111,9 +112,9 @@ options: Okta tile URL to use. --okta-client-id OKTA_CLIENT_ID For OIE enabled Orgs this sets the Okta client ID to replace the value found by tokendito. It is used in the authorize code flow. - --okta-mfa OKTA_MFA Sets the MFA method + --okta-mfa OKTA_MFA Sets the MFA method. You can also use the TOKENDITO_OKTA_MFA environment variable. --okta-mfa-response OKTA_MFA_RESPONSE - Sets the MFA response to a challenge + Sets the MFA response to a challenge. You can also use the TOKENDITO_OKTA_MFA_RESPONSE environment variable. --use-device-token Use device token across sessions --quiet Suppress output ``` diff --git a/pyproject.toml b/pyproject.toml index 9d131ff..d77300e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ exclude_lines = [ "break", "except KeyboardInterrupt:", "if __name__ == .__main__.:", - "if __package__ is None:", + "if not __package__:", "logger.debug", "pragma: no cover", "print..Invalid input, try again...", diff --git a/tokendito/__init__.py b/tokendito/__init__.py index d8c54dd..9b7f8b1 100644 --- a/tokendito/__init__.py +++ b/tokendito/__init__.py @@ -1,7 +1,7 @@ # vim: set filetype=python ts=4 sw=4 # -*- coding: utf-8 -*- """Tokendito module initialization.""" -__version__ = "2.3.3" +__version__ = "2.4.0" __title__ = "tokendito" __description__ = "Get AWS STS tokens from Okta SSO" __long_description_content_type__ = "text/markdown" diff --git a/tokendito/__main__.py b/tokendito/__main__.py index 99bff00..1c7e7cd 100755 --- a/tokendito/__main__.py +++ b/tokendito/__main__.py @@ -7,7 +7,7 @@ def main(args=None): # needed for console script """Packge entry point.""" - if __package__ is None: + if not __package__: import os.path path = os.path.dirname(os.path.dirname(__file__)) diff --git a/tokendito/aws.py b/tokendito/aws.py index d7ada32..3f2fc83 100644 --- a/tokendito/aws.py +++ b/tokendito/aws.py @@ -72,7 +72,7 @@ def authenticate_to_roles(config, urls): saml_xml = okta.extract_saml_response(saml_response_string) if not saml_xml: state_token = okta.extract_state_token(saml_response_string) - if "Extra Verification" in saml_response_string and state_token: + if state_token: logger.info(f"Step-Up authentication required for {url}.") if okta.step_up_authenticate(config, state_token): return authenticate_to_roles(config, urls) diff --git a/tokendito/http_client.py b/tokendito/http_client.py index 872c5a6..843a22e 100644 --- a/tokendito/http_client.py +++ b/tokendito/http_client.py @@ -81,6 +81,7 @@ def get(self, url, params=None, headers=None, allow_redirects=True): def post(self, url, data=None, json=None, headers=None, params=None, return_json=False): """Perform a POST request.""" + response = None logger.debug(f"POST to {url}") try: response = self.session.post(url, data=data, json=json, params=params, headers=headers) @@ -95,6 +96,11 @@ def post(self, url, data=None, json=None, headers=None, params=None, return_json return response except requests.RequestException as e: logger.error(f"Error during POST request to {url}. Error: {e}") + if response: + logger.debug(f"Response Headers: {response.headers}") + logger.debug(f"Response Text: {response.text}") + else: + logger.debug("No response received") sys.exit(1) except Exception as err: logger.error(f"The post request to {url} failed with {err}") diff --git a/tokendito/okta.py b/tokendito/okta.py index 53448a3..55e4b3a 100644 --- a/tokendito/okta.py +++ b/tokendito/okta.py @@ -129,9 +129,11 @@ def get_saml_request(auth_properties): response = HTTP_client.get(url, headers=headers) # Extract the required parameters from the SAML request. + post_url = extract_form_post_url(response.text) + base_url = user.get_base_url(post_url) saml_request = { - "base_url": user.get_base_url(extract_form_post_url(response.text)), - "post_url": extract_form_post_url(response.text), + "base_url": base_url, + "post_url": post_url, "relay_state": extract_saml_relaystate(response.text), "request": extract_saml_request(response.text, raw=True), } @@ -263,7 +265,6 @@ def send_saml_response(config, saml_response): # Get the 'sid' value from the reponse cookies. sid = response.cookies.get("sid", None) - logger.debug(f"New sid is {sid}") # If 'sid' is present, mask its value for logging purposes. if sid: @@ -564,6 +565,12 @@ def authorize_request(oauth2_config, oauth2_session_data): params=payload, ) + idx = HTTP_client.session.cookies.get("idx", None) + if idx: + user.add_sensitive_value_to_be_masked(idx) + else: + logger.debug("We did not find an 'idx' entry in the cookies.") + authorize_code = get_authorize_code(response, session_token) return authorize_code @@ -699,6 +706,7 @@ def idp_authenticate(config): logger.error("Okta auth failed: unknown type.") sys.exit(1) + # Possible recursion ahead. The exit condition should be the first if statement. if local_authentication_enabled(auth_properties): session_token = local_authenticate(config) # authentication sends us a token diff --git a/tokendito/tokendito.py b/tokendito/tokendito.py index 85cc374..9b9d523 100755 --- a/tokendito/tokendito.py +++ b/tokendito/tokendito.py @@ -7,7 +7,7 @@ def main(args=None): # needed for console script """Packge entry point.""" - if __package__ is None: + if not __package__: import os.path path = os.path.dirname(os.path.dirname(__file__)) diff --git a/tokendito/user.py b/tokendito/user.py index 7e14cc5..be88f39 100644 --- a/tokendito/user.py +++ b/tokendito/user.py @@ -79,12 +79,7 @@ def cmd_interface(args): # get authentication and authorization cookies from okta okta.access_control(config) - logger.debug( - f""" - about to call discover_tile - we have client cookies: {HTTP_client.session.cookies} - """ - ) + if config.okta["tile"]: tile_label = "" config.okta["tile"] = (config.okta["tile"], tile_label)