Skip to content

Commit

Permalink
✨(backend) add option to configure list of required OIDC claims
Browse files Browse the repository at this point in the history
We want to be able to refuse connection for users who have missing
claims from a list of required keys.
  • Loading branch information
sampaccoud committed Dec 24, 2024
1 parent 02a4740 commit dca001e
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to

## Added

🔧(backend) add option to configure list of required OIDC claims #525
🔧(helm) add option to disable default tls setting by @dominikkaminski #519

## Changed
Expand Down
12 changes: 12 additions & 0 deletions src/backend/core/authentication/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ def get_userinfo(self, access_token, id_token, payload):
_("Invalid response format or token verification failed")
) from e

# Validate required claims
missing_claims = [
claim
for claim in settings.USER_OIDC_REQUIRED_CLAIMS
if claim not in userinfo
]
if missing_claims:
raise SuspiciousOperation(
_("Missing required claims in user info: %(claims)s")
% {"claims": ", ".join(missing_claims)}
)

return userinfo

def get_or_create_user(self, access_token, id_token, payload):
Expand Down
84 changes: 84 additions & 0 deletions src/backend/core/tests/authentication/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,87 @@ def get_userinfo_mocked(*args):
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)

assert models.User.objects.count() == 1


# Required claims


@override_settings(
OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo",
USER_OIDC_REQUIRED_CLAIMS=["email", "sub", "address"],
)
@responses.activate
def test_authentication_get_userinfo_required_claims_missing():
"""Ensure SuspiciousOperation is raised if required claims are missing."""

responses.add(
responses.GET,
re.compile(r".*/userinfo"),
json={
"last_name": "Doe",
"email": "[email protected]",
},
status=200,
)

oidc_backend = OIDCAuthenticationBackend()

with pytest.raises(
SuspiciousOperation, match="Missing required claims in user info: sub, address"
):
oidc_backend.get_userinfo("fake_access_token", None, None)


@override_settings(
OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo",
USER_OIDC_REQUIRED_CLAIMS=["email", "Sub"],
)
@responses.activate
def test_authentication_get_userinfo_required_claims_case_sensitivity():
"""Ensure the system respects case sensitivity for required claims."""

responses.add(
responses.GET,
re.compile(r".*/userinfo"),
json={
"sub": "123",
"last_name": "Doe",
"email": "[email protected]",
},
status=200,
)

oidc_backend = OIDCAuthenticationBackend()

with pytest.raises(
SuspiciousOperation, match="Missing required claims in user info: Sub"
):
oidc_backend.get_userinfo("fake_access_token", None, None)


@override_settings(
OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo",
USER_OIDC_REQUIRED_CLAIMS=["email", "sub"],
)
@responses.activate
def test_authentication_get_userinfo_required_claims_success():
"""Ensure user is authenticated when required claims are present."""

responses.add(
responses.GET,
re.compile(r".*/userinfo"),
json={
"sub": "123",
"last_name": "Doe",
"email": "[email protected]",
},
status=200,
)

oidc_backend = OIDCAuthenticationBackend()
result = oidc_backend.get_userinfo("fake_access_token", None, None)

assert result["sub"] == "123"
assert result.get("first_name") is None
assert result["last_name"] == "Doe"
assert result["email"] == "[email protected]"
3 changes: 3 additions & 0 deletions src/backend/impress/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,9 @@ class Base(Configuration):
environ_prefix=None,
)

USER_OIDC_REQUIRED_CLAIMS = values.ListValue(
default=[], environ_name="USER_OIDC_REQUIRED_CLAIMS", environ_prefix=None
)
USER_OIDC_FIELDS_TO_FULLNAME = values.ListValue(
default=["first_name", "last_name"],
environ_name="USER_OIDC_FIELDS_TO_FULLNAME",
Expand Down

0 comments on commit dca001e

Please sign in to comment.