🛂(backend) oidc userinfo endpoint json format
The userinfo endpoint can return 2 content types: - application/json - application/jwt Gitlab oidc returns a json object, while Agent Connect oidc returns a jwt token. We are adapting the authentication to handle both cases.
This commit is contained in:
@@ -9,6 +9,10 @@ and this project adheres to
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- 🐛 (backend) gitlab oicd userinfo endpoint #232
|
||||||
|
|
||||||
|
|
||||||
## [1.4.0] - 2024-09-17
|
## [1.4.0] - 2024-09-17
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,17 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
|
|||||||
proxies=self.get_settings("OIDC_PROXY", None),
|
proxies=self.get_settings("OIDC_PROXY", None),
|
||||||
)
|
)
|
||||||
user_response.raise_for_status()
|
user_response.raise_for_status()
|
||||||
userinfo = self.verify_token(user_response.text)
|
|
||||||
|
try:
|
||||||
|
userinfo = user_response.json()
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
userinfo = self.verify_token(user_response.text)
|
||||||
|
except Exception as e:
|
||||||
|
raise SuspiciousOperation(
|
||||||
|
_("Invalid response format or token verification failed")
|
||||||
|
) from e
|
||||||
|
|
||||||
return userinfo
|
return userinfo
|
||||||
|
|
||||||
def get_or_create_user(self, access_token, id_token, payload):
|
def get_or_create_user(self, access_token, id_token, payload):
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
"""Unit tests for the Authentication Backends."""
|
"""Unit tests for the Authentication Backends."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import responses
|
||||||
|
|
||||||
from core import models
|
from core import models
|
||||||
from core.authentication.backends import OIDCAuthenticationBackend
|
from core.authentication.backends import OIDCAuthenticationBackend
|
||||||
@@ -81,7 +85,7 @@ def test_authentication_getter_new_user_with_email(monkeypatch):
|
|||||||
assert models.User.objects.count() == 1
|
assert models.User.objects.count() == 1
|
||||||
|
|
||||||
|
|
||||||
def test_models_oidc_user_getter_invalid_token(django_assert_num_queries, monkeypatch):
|
def test_authentication_getter_invalid_token(django_assert_num_queries, monkeypatch):
|
||||||
"""The user's info doesn't contain a sub."""
|
"""The user's info doesn't contain a sub."""
|
||||||
klass = OIDCAuthenticationBackend()
|
klass = OIDCAuthenticationBackend()
|
||||||
|
|
||||||
@@ -102,3 +106,64 @@ def test_models_oidc_user_getter_invalid_token(django_assert_num_queries, monkey
|
|||||||
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)
|
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)
|
||||||
|
|
||||||
assert models.User.objects.exists() is False
|
assert models.User.objects.exists() is False
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
||||||
|
@responses.activate
|
||||||
|
def test_authentication_get_userinfo_json_response():
|
||||||
|
"""Test get_userinfo method with a JSON response."""
|
||||||
|
|
||||||
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
re.compile(r".*/userinfo"),
|
||||||
|
json={"name": "John Doe", "email": "john.doe@example.com"},
|
||||||
|
status=200,
|
||||||
|
)
|
||||||
|
|
||||||
|
oidc_backend = OIDCAuthenticationBackend()
|
||||||
|
result = oidc_backend.get_userinfo("fake_access_token", None, None)
|
||||||
|
|
||||||
|
assert result["name"] == "John Doe"
|
||||||
|
assert result["email"] == "john.doe@example.com"
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
||||||
|
@responses.activate
|
||||||
|
def test_authentication_get_userinfo_token_response(monkeypatch):
|
||||||
|
"""Test get_userinfo method with a token response."""
|
||||||
|
|
||||||
|
responses.add(
|
||||||
|
responses.GET, re.compile(r".*/userinfo"), body="fake.jwt.token", status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
def mock_verify_token(self, token): # pylint: disable=unused-argument
|
||||||
|
return {"name": "Jane Doe", "email": "jane.doe@example.com"}
|
||||||
|
|
||||||
|
monkeypatch.setattr(OIDCAuthenticationBackend, "verify_token", mock_verify_token)
|
||||||
|
|
||||||
|
oidc_backend = OIDCAuthenticationBackend()
|
||||||
|
result = oidc_backend.get_userinfo("fake_access_token", None, None)
|
||||||
|
|
||||||
|
assert result["name"] == "Jane Doe"
|
||||||
|
assert result["email"] == "jane.doe@example.com"
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(OIDC_OP_USER_ENDPOINT="http://oidc.endpoint.test/userinfo")
|
||||||
|
@responses.activate
|
||||||
|
def test_authentication_get_userinfo_invalid_response():
|
||||||
|
"""
|
||||||
|
Test get_userinfo method with an invalid JWT response that
|
||||||
|
causes verify_token to raise an error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
responses.add(
|
||||||
|
responses.GET, re.compile(r".*/userinfo"), body="fake.jwt.token", status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
oidc_backend = OIDCAuthenticationBackend()
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
SuspiciousOperation,
|
||||||
|
match="Invalid response format or token verification failed",
|
||||||
|
):
|
||||||
|
oidc_backend.get_userinfo("fake_access_token", None, None)
|
||||||
|
|||||||
Reference in New Issue
Block a user