Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ orbs:

jobs:
build_and_test:
executor: python/default
docker:
- image: cimg/python:3.10
steps:
- checkout
- python/install-packages:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v3
Expand Down
55 changes: 54 additions & 1 deletion okta/config/config_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# See the License for the specific language governing permissions and limitations under the License.
# coding: utf-8

from okta.constants import FINDING_OKTA_DOMAIN, REPO_URL
from okta.constants import FINDING_OKTA_DOMAIN, REPO_URL, MIN_DPOP_KEY_ROTATION_SECONDS
from okta.error_messages import (
ERROR_MESSAGE_ORG_URL_MISSING,
ERROR_MESSAGE_API_TOKEN_DEFAULT,
Expand Down Expand Up @@ -67,6 +67,8 @@ def validate_config(self):
]
client_fields_values = [client.get(field, "") for field in client_fields]
errors += self._validate_client_fields(*client_fields_values)
# FIX #9: Validate DPoP configuration if enabled
errors += self._validate_dpop_config(client)
else: # Not a valid authorization mode
errors += [
(
Expand Down Expand Up @@ -221,3 +223,54 @@ def _validate_proxy_settings(self, proxy):
proxy_errors.append(ERROR_MESSAGE_PROXY_INVALID_PORT)

return proxy_errors

def _validate_dpop_config(self, client):
"""
FIX #9: Validate DPoP-specific configuration.

Args:
client: Client configuration dict

Returns:
list: List of error messages (empty if valid)
"""
import logging
logger = logging.getLogger("okta-sdk-python")

errors = []

if not client.get('dpopEnabled'):
return errors # DPoP not enabled, nothing to validate

# DPoP requires PrivateKey authorization mode (already checked above)
auth_mode = client.get('authorizationMode')
if auth_mode != 'PrivateKey':
errors.append(
f"DPoP authentication requires authorizationMode='PrivateKey', "
f"but got '{auth_mode}'. "
"Update your configuration to use PrivateKey mode with DPoP."
)

# Validate key rotation interval
rotation_interval = client.get('dpopKeyRotationInterval', 86400)

if not isinstance(rotation_interval, int):
errors.append(
f"dpopKeyRotationInterval must be an integer (seconds), "
f"but got {type(rotation_interval).__name__}"
)
elif rotation_interval < MIN_DPOP_KEY_ROTATION_SECONDS: # Minimum 1 hour
errors.append(
f"dpopKeyRotationInterval must be at least {MIN_DPOP_KEY_ROTATION_SECONDS} seconds (1 hour), "
f"but got {rotation_interval} seconds. "
"Shorter intervals may cause performance issues."
)
elif rotation_interval > 604800: # Maximum 7 days (recommendation)
# This is a warning, not an error
logger.warning(
f"dpopKeyRotationInterval is very long ({rotation_interval} seconds, "
f"{rotation_interval // 86400} days). "
"Consider shorter intervals (24-48 hours) for better security."
)

return errors
2 changes: 2 additions & 0 deletions okta/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@

SWA_APP_NAME = "template_swa"
SWA3_APP_NAME = "template_swa3field"

MIN_DPOP_KEY_ROTATION_SECONDS = 3600
Loading