diff --git a/mealie/core/security/providers/openid_provider.py b/mealie/core/security/providers/openid_provider.py index a1124d248..5cee3b613 100644 --- a/mealie/core/security/providers/openid_provider.py +++ b/mealie/core/security/providers/openid_provider.py @@ -16,8 +16,9 @@ class OpenIDProvider(AuthProvider[UserInfo]): _logger = root_logger.get_logger("openid_provider") - def __init__(self, session: Session, data: UserInfo) -> None: + def __init__(self, session: Session, data: UserInfo, use_default_groups: bool = False) -> None: super().__init__(session, data) + self.use_default_groups = use_default_groups def authenticate(self) -> tuple[str, timedelta] | None: """Attempt to authenticate a user given a username and password""" @@ -51,6 +52,15 @@ class OpenIDProvider(AuthProvider[UserInfo]): is_admin = False if settings.OIDC_REQUIRES_GROUP_CLAIM: + # We explicitly allow the groups claim to be missing to account for the behaviour of some IdPs: + # https://github.com/keycloak/keycloak/issues/22340 + # We still log a warning though + if settings.OIDC_GROUPS_CLAIM not in claims: + self._logger.warning( + "[OIDC] claims did not include a %s claim%s", + settings.OIDC_GROUPS_CLAIM, + ", using an empty list as default" if self.use_default_groups else "", + ) group_claim = claims.get(settings.OIDC_GROUPS_CLAIM, []) or [] is_admin = settings.OIDC_ADMIN_GROUP in group_claim if settings.OIDC_ADMIN_GROUP else False is_valid_user = settings.OIDC_USER_GROUP in group_claim if settings.OIDC_USER_GROUP else True @@ -111,6 +121,6 @@ class OpenIDProvider(AuthProvider[UserInfo]): settings = get_app_settings() claims = {settings.OIDC_NAME_CLAIM, "email", settings.OIDC_USER_CLAIM} - if settings.OIDC_REQUIRES_GROUP_CLAIM: + if settings.OIDC_REQUIRES_GROUP_CLAIM and not self.use_default_groups: claims.add(settings.OIDC_GROUPS_CLAIM) return claims diff --git a/mealie/routes/auth/auth.py b/mealie/routes/auth/auth.py index 593ebda89..73025933e 100644 --- a/mealie/routes/auth/auth.py +++ b/mealie/routes/auth/auth.py @@ -138,7 +138,7 @@ async def oauth_callback(request: Request, response: Response, session: Session try: logger.debug("[OIDC] Claims not present in the ID token, pulling user info") userinfo = await client.userinfo(token=token) - auth_provider = OpenIDProvider(session, userinfo) + auth_provider = OpenIDProvider(session, userinfo, use_default_groups=True) auth = auth_provider.authenticate() except MissingClaimException: auth = None diff --git a/tests/unit_tests/core/security/providers/test_openid_provider.py b/tests/unit_tests/core/security/providers/test_openid_provider.py index 0fcd92690..6b198a294 100644 --- a/tests/unit_tests/core/security/providers/test_openid_provider.py +++ b/tests/unit_tests/core/security/providers/test_openid_provider.py @@ -61,6 +61,49 @@ def test_missing_groups_claim(monkeypatch: MonkeyPatch): auth_provider.authenticate() +def test_missing_groups_claim_admin(monkeypatch: MonkeyPatch): + monkeypatch.setenv("OIDC_ADMIN_GROUP", "mealie_admin") + get_app_settings.cache_clear() + + data = { + "preferred_username": "dude1", + "email": "email@email.com", + "name": "Firstname Lastname", + } + auth_provider = OpenIDProvider(None, data) + + with pytest.raises(MissingClaimException): + auth_provider.authenticate() + + +def test_missing_groups_claim_with_default(monkeypatch: MonkeyPatch): + monkeypatch.setenv("OIDC_USER_GROUP", "mealie_user") + get_app_settings.cache_clear() + + data = { + "preferred_username": "dude1", + "email": "email@email.com", + "name": "Firstname Lastname", + } + auth_provider = OpenIDProvider(None, data, True) + + assert auth_provider.authenticate() is None + + +def test_missing_groups_claim_admin_group_with_default(monkeypatch: MonkeyPatch, unique_user: TestUser): + monkeypatch.setenv("OIDC_ADMIN_GROUP", "mealie_admin") + get_app_settings.cache_clear() + + data = { + "preferred_username": "dude1", + "email": unique_user.email, + "name": "Firstname Lastname", + } + auth_provider = OpenIDProvider(unique_user.repos.session, data, True) + + assert auth_provider.authenticate() is not None + + def test_missing_user_group(monkeypatch: MonkeyPatch): monkeypatch.setenv("OIDC_USER_GROUP", "mealie_user") get_app_settings.cache_clear()