Skip to content

Commit

Permalink
Add Documenso provider (#516)
Browse files Browse the repository at this point in the history
  • Loading branch information
armanddidierjean authored Nov 16, 2024
1 parent 36ad945 commit 8ee6784
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ CLIENT_URL = "http://127.0.0.1:8000/"
# you will access the login page from your browser http://localhost:8000/auth/authorize
# but the docker container should call http://host.docker.internal:8000/auth/token and not your localhost address
# NOTE: A trailing / is required
#OVERRIDDEN_CLIENT_URL_FOR_OIDC: = "http://host.docker.internal:8000/"
#OVERRIDDEN_CLIENT_URL_FOR_OIDC = "http://host.docker.internal:8000/"


# Logging configuration #
Expand Down
87 changes: 47 additions & 40 deletions app/core/auth/endpoints_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,24 +312,28 @@ async def authorize_validation(
user=user,
allowed_groups=auth_client.allowed_groups,
):
# TODO We should show an HTML page explaining the issue
hyperion_access_logger.warning(
f"Authorize-validation: user is not member of an allowed group {authorizereq.email} ({request_id})",
)
url = redirect_uri + "?error=" + "consent_required"
if authorizereq.state:
url += "&state=" + authorizereq.state
return RedirectResponse(url, status_code=status.HTTP_302_FOUND)
return RedirectResponse(
settings.CLIENT_URL
+ calypsso.get_error_relative_url(
message="User is not member of an allowed group",
),
status_code=status.HTTP_302_FOUND,
)
if not auth_client.allow_external_users:
if is_user_external(user):
# TODO We should show an HTML page explaining the issue
hyperion_access_logger.warning(
f"Authorize-validation: external users are disabled for this auth provider {auth_client.client_id} ({request_id})",
)
url = redirect_uri + "?error=" + "consent_required"
if authorizereq.state:
url += "&state=" + authorizereq.state
return RedirectResponse(url, status_code=status.HTTP_302_FOUND)
return RedirectResponse(
settings.CLIENT_URL
+ calypsso.get_error_relative_url(
message="External users are not allowed",
),
status_code=status.HTTP_302_FOUND,
)

# We generate a new authorization_code
# The authorization code MUST expire
Expand Down Expand Up @@ -513,21 +517,43 @@ async def authorization_code_grant(
error_description="Invalid client id or secret",
)

# If the auth provider expect to use a client secret, we don't use PKCE
if auth_client.secret is not None:
# As PKCE is not used, we need to make sure that PKCE related parameters were not used
if (
db_authorization_code.code_challenge is not None
or tokenreq.code_verifier is not None
):
hyperion_access_logger.warning(
f"Token authorization_code_grant: PKCE related parameters should not be used when using a client secret ({request_id})",
)
raise AuthHTTPException(
status_code=400,
error="invalid_request",
error_description="PKCE related parameters should not be used",
)
# We allow some auth clients to bypass this verification
# because some auth providers may use PKCE with a client secret event if it's forbidden by the specifications
if not auth_client.allow_pkce_with_client_secret:
hyperion_access_logger.warning(
f"Token authorization_code_grant: PKCE related parameters should not be used when using a client secret ({request_id})",
)
raise AuthHTTPException(
status_code=400,
error="invalid_request",
error_description="PKCE related parameters should not be used",
)
elif (
db_authorization_code.code_challenge is not None
and tokenreq.code_verifier is not None
):
# We use PKCE
pass
else:
hyperion_access_logger.warning(
f"Token authorization_code_grant: Client must provide a client_secret or a code_verifier ({request_id})",
)
raise AuthHTTPException(
status_code=400,
error="invalid_request",
error_description="Client must provide a client_secret or a code_verifier",
)

# Then we verify passed client_secret or code_verifier are valid

# If the auth provider expect to use a client secret, we verify it
if auth_client.secret is not None:
# We need to check the correct client_secret was provided
if auth_client.secret != tokenreq.client_secret:
hyperion_access_logger.warning(
Expand All @@ -539,21 +565,11 @@ async def authorization_code_grant(
error_description="Invalid client id or secret",
)

# If there is no client secret, we use PKCE
elif (
# If we use PKCE, we need to verify the code_verifier
if (
db_authorization_code.code_challenge is not None
and tokenreq.code_verifier is not None
):
# As PKCE is used, we make sure a client secret was not provided
if tokenreq.client_secret is not None:
hyperion_access_logger.warning(
f"Token authorization_code_grant: A client secret should not be used when using PKCE ({request_id})",
)
raise AuthHTTPException(
status_code=400,
error="invalid_request",
error_description="A client secret should not be used with PKCE",
)
# We need to verify the hash correspond
# The hash is a H256, urlbase64 encoded
# If the last character is not a "=", we need to add it, as the = is optional for urlbase64 encoding
Expand All @@ -576,15 +592,6 @@ async def authorization_code_grant(
error="invalid_request",
error_description="Invalid code_verifier",
)
else:
hyperion_access_logger.warning(
f"Token authorization_code_grant: Client must provide a client_secret or a code_verifier ({request_id})",
)
raise AuthHTTPException(
status_code=400,
error="invalid_request",
error_description="Client must provide a client_secret or a code_verifier",
)

# We can check the authorization code
if db_authorization_code.expire_on < datetime.now(UTC):
Expand Down
1 change: 1 addition & 0 deletions app/core/groups/groups_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class GroupType(str, Enum):
raid_admin = "e9e6e3d3-9f5f-4e9b-8e5f-9f5f4e9b8e5f"
ph = "4ec5ae77-f955-4309-96a5-19cc3c8be71c"
admin_cdr = "c1275229-46b2-4e53-a7c4-305513bb1a2a"
eclair = "1f841bd9-00be-41a7-96e1-860a18a46105"

# Auth related groups

Expand Down
31 changes: 31 additions & 0 deletions app/utils/auth/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class BaseAuthClient:
# We don't want to enable token introspection for all clients as it may be a security risk, allowing attackers to do token fishing.
allow_token_introspection: bool = False

# OIDC specification forbids using PKCE when using a client secret.
# However, some clients may try to use PKCE anyway. This parameter allows to bypass this check.
# NOTE: you should only set this to True if you are sure the client is correctly configured and secure.
allow_pkce_with_client_secret: bool = False

def get_userinfo(self, user: models_core.CoreUser) -> dict[str, Any]:
"""
Return information about the user in a format understandable by the client.
Expand Down Expand Up @@ -319,6 +324,32 @@ def get_userinfo(cls, user: models_core.CoreUser):
}


class DocumensoAuthClient(BaseAuthClient):
allowed_scopes: set[ScopeType | str] = {ScopeType.openid}

allow_pkce_with_client_secret: bool = True

allowed_groups: list[GroupType] | None = [
GroupType.admin,
GroupType.BDE,
GroupType.eclair,
]

return_userinfo_in_id_token: bool = True

@classmethod
def get_userinfo(cls, user: models_core.CoreUser):
return {
"sub": user.id,
"name": get_display_name(
firstname=user.firstname,
name=user.name,
nickname=user.nickname,
),
"email": user.email,
}


class RAIDRegisteringAuthClient(BaseAuthClient):
"""
An auth client for The Raid registering website
Expand Down
Loading

0 comments on commit 8ee6784

Please sign in to comment.