feature/specify-valid-client-ids (#808)
* allow additional valid client ids to be specified for the purpose of token validation * import the correct typedict and notrequired for python 3.10 * remove HEY --------- Co-authored-by: jasquat <jasquat@users.noreply.github.com> Co-authored-by: burnettk <burnettk@users.noreply.github.com>
This commit is contained in:
parent
5cceb52756
commit
7234f0f181
|
@ -234,6 +234,7 @@ def setup_config(app: Flask) -> None:
|
|||
"uri": app.config.get("SPIFFWORKFLOW_BACKEND_OPEN_ID_SERVER_URL"),
|
||||
"client_id": app.config.get("SPIFFWORKFLOW_BACKEND_OPEN_ID_CLIENT_ID"),
|
||||
"client_secret": app.config.get("SPIFFWORKFLOW_BACKEND_OPEN_ID_CLIENT_SECRET_KEY"),
|
||||
"additional_valid_client_ids": app.config.get("SPIFFWORKFLOW_BACKEND_OPEN_ID_ADDITIONAL_VALID_CLIENT_IDS"),
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -106,6 +106,15 @@ else:
|
|||
SPIFFWORKFLOW_BACKEND_OPEN_ID_SERVER_URL = url_config
|
||||
config_from_env("SPIFFWORKFLOW_BACKEND_OPEN_ID_CLIENT_ID", default="spiffworkflow-backend")
|
||||
config_from_env("SPIFFWORKFLOW_BACKEND_OPEN_ID_CLIENT_SECRET_KEY", default="JXeQExm0JhQPLumgHtIIqf52bDalHz0q")
|
||||
|
||||
# comma-separated list of client ids that can be successfully validated against.
|
||||
# useful for api users that will login to a different client on the same realm but from something external to backend.
|
||||
# Example:
|
||||
# client-A is configured as the main client id in backend
|
||||
# client-B is for api users who will authenticate directly with keycloak
|
||||
# if client-B is added to this list, then an api user can auth with keycloak
|
||||
# and use that token successfully with backend
|
||||
config_from_env("SPIFFWORKFLOW_BACKEND_OPEN_ID_ADDITIONAL_VALID_CLIENT_IDS")
|
||||
else:
|
||||
SPIFFWORKFLOW_BACKEND_AUTH_CONFIGS = [
|
||||
{
|
||||
|
@ -114,6 +123,7 @@ else:
|
|||
"uri": "http://localhost:7002/realms/spiffworkflow",
|
||||
"client_id": "spiffworkflow-backend",
|
||||
"client_secret": "JXeQExm0JhQPLumgHtIIqf52bDalHz0q",
|
||||
"additional_valid_client_ids": None,
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import base64
|
||||
import enum
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
from hashlib import sha256
|
||||
from hmac import HMAC
|
||||
from hmac import compare_digest
|
||||
|
||||
if sys.version_info < (3, 11):
|
||||
from typing_extensions import NotRequired
|
||||
from typing_extensions import TypedDict
|
||||
else:
|
||||
from typing import NotRequired
|
||||
from typing import TypedDict
|
||||
|
||||
import jwt
|
||||
|
@ -36,6 +43,7 @@ class AuthenticationOptionForApi(TypedDict):
|
|||
identifier: str
|
||||
label: str
|
||||
uri: str
|
||||
additional_valid_client_ids: NotRequired[str]
|
||||
|
||||
|
||||
class AuthenticationOption(AuthenticationOptionForApi):
|
||||
|
@ -163,6 +171,24 @@ class AuthenticationService:
|
|||
auth_token_object: dict = json.loads(response.text)
|
||||
return auth_token_object
|
||||
|
||||
@classmethod
|
||||
def is_valid_azp(cls, authentication_identifier: str, azp: str | None) -> bool:
|
||||
# not all open id token include an azp so only check if present
|
||||
if azp is None:
|
||||
return True
|
||||
|
||||
valid_client_ids = [cls.client_id(authentication_identifier), "account"]
|
||||
if (
|
||||
"additional_valid_client_ids" in cls.authentication_option_for_identifier(authentication_identifier)
|
||||
and cls.authentication_option_for_identifier(authentication_identifier)["additional_valid_client_ids"] is not None
|
||||
):
|
||||
additional_valid_client_ids = cls.authentication_option_for_identifier(authentication_identifier)[
|
||||
"additional_valid_client_ids"
|
||||
].split(",")
|
||||
additional_valid_client_ids = [value.strip() for value in additional_valid_client_ids]
|
||||
valid_client_ids = valid_client_ids + additional_valid_client_ids
|
||||
return azp in valid_client_ids
|
||||
|
||||
@classmethod
|
||||
def validate_id_or_access_token(cls, token: str, authentication_identifier: str) -> bool:
|
||||
"""Https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation."""
|
||||
|
@ -198,10 +224,7 @@ class AuthenticationService:
|
|||
f"TOKEN INVALID because audience '{aud}' does not match client id '{cls.client_id(authentication_identifier)}'"
|
||||
)
|
||||
valid = False
|
||||
elif azp and azp not in (
|
||||
cls.client_id(authentication_identifier),
|
||||
"account",
|
||||
):
|
||||
elif not cls.is_valid_azp(authentication_identifier, azp):
|
||||
current_app.logger.error(
|
||||
f"TOKEN INVALID because azp '{azp}' does not match client id '{cls.client_id(authentication_identifier)}'"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue