mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-22 14:49:40 +02:00
feat: OIDC: Call userinfo if no claims found in id token (#5228)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
This commit is contained in:
parent
3b1a6280d6
commit
d724f408cc
4 changed files with 37 additions and 16 deletions
|
@ -43,3 +43,6 @@ def mealie_registered_exceptions(t: Translator) -> dict:
|
||||||
|
|
||||||
|
|
||||||
class UserLockedOut(Exception): ...
|
class UserLockedOut(Exception): ...
|
||||||
|
|
||||||
|
|
||||||
|
class MissingClaimException(Exception): ...
|
||||||
|
|
|
@ -5,6 +5,7 @@ from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
from mealie.core import root_logger
|
from mealie.core import root_logger
|
||||||
from mealie.core.config import get_app_settings
|
from mealie.core.config import get_app_settings
|
||||||
|
from mealie.core.exceptions import MissingClaimException
|
||||||
from mealie.core.security.providers.auth_provider import AuthProvider
|
from mealie.core.security.providers.auth_provider import AuthProvider
|
||||||
from mealie.db.models.users.users import AuthMethod
|
from mealie.db.models.users.users import AuthMethod
|
||||||
from mealie.repos.all_repositories import get_repositories
|
from mealie.repos.all_repositories import get_repositories
|
||||||
|
@ -25,7 +26,7 @@ class OpenIDProvider(AuthProvider[UserInfo]):
|
||||||
claims = self.data
|
claims = self.data
|
||||||
if not claims:
|
if not claims:
|
||||||
self._logger.error("[OIDC] No claims in the id_token")
|
self._logger.error("[OIDC] No claims in the id_token")
|
||||||
return None
|
raise MissingClaimException()
|
||||||
|
|
||||||
# Log all claims for debugging
|
# Log all claims for debugging
|
||||||
self._logger.debug("[OIDC] Received claims:")
|
self._logger.debug("[OIDC] Received claims:")
|
||||||
|
@ -38,13 +39,13 @@ class OpenIDProvider(AuthProvider[UserInfo]):
|
||||||
self.required_claims,
|
self.required_claims,
|
||||||
claims.keys(),
|
claims.keys(),
|
||||||
)
|
)
|
||||||
return None
|
raise MissingClaimException()
|
||||||
|
|
||||||
# Check for empty required claims
|
# Check for empty required claims
|
||||||
for claim in self.required_claims:
|
for claim in self.required_claims:
|
||||||
if not claims.get(claim):
|
if not claims.get(claim):
|
||||||
self._logger.error("[OIDC] Required claim '%s' is empty", claim)
|
self._logger.error("[OIDC] Required claim '%s' is empty", claim)
|
||||||
return None
|
raise MissingClaimException()
|
||||||
|
|
||||||
repos = get_repositories(self.session, group_id=None, household_id=None)
|
repos = get_repositories(self.session, group_id=None, household_id=None)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from starlette.datastructures import URLPath
|
||||||
from mealie.core import root_logger, security
|
from mealie.core import root_logger, security
|
||||||
from mealie.core.config import get_app_settings
|
from mealie.core.config import get_app_settings
|
||||||
from mealie.core.dependencies import get_current_user
|
from mealie.core.dependencies import get_current_user
|
||||||
from mealie.core.exceptions import UserLockedOut
|
from mealie.core.exceptions import MissingClaimException, UserLockedOut
|
||||||
from mealie.core.security.providers.openid_provider import OpenIDProvider
|
from mealie.core.security.providers.openid_provider import OpenIDProvider
|
||||||
from mealie.core.security.security import get_auth_provider
|
from mealie.core.security.security import get_auth_provider
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
|
@ -125,14 +125,24 @@ async def oauth_callback(request: Request, response: Response, session: Session
|
||||||
detail="Could not initialize OAuth client",
|
detail="Could not initialize OAuth client",
|
||||||
)
|
)
|
||||||
client = oauth.create_client("oidc")
|
client = oauth.create_client("oidc")
|
||||||
|
|
||||||
token = await client.authorize_access_token(request)
|
token = await client.authorize_access_token(request)
|
||||||
auth_provider = OpenIDProvider(session, token["userinfo"])
|
|
||||||
auth = auth_provider.authenticate()
|
auth = None
|
||||||
|
try:
|
||||||
|
auth_provider = OpenIDProvider(session, token["userinfo"])
|
||||||
|
auth = auth_provider.authenticate()
|
||||||
|
except MissingClaimException:
|
||||||
|
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 = auth_provider.authenticate()
|
||||||
|
except MissingClaimException:
|
||||||
|
auth = None
|
||||||
|
|
||||||
if not auth:
|
if not auth:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
||||||
)
|
|
||||||
access_token, duration = auth
|
access_token, duration = auth
|
||||||
|
|
||||||
expires_in = duration.total_seconds() if duration else None
|
expires_in = duration.total_seconds() if duration else None
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import pytest
|
|
||||||
from pytest import MonkeyPatch, Session
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest import MonkeyPatch, Session
|
||||||
|
|
||||||
from mealie.core.config import get_app_settings
|
from mealie.core.config import get_app_settings
|
||||||
|
from mealie.core.exceptions import MissingClaimException
|
||||||
from mealie.core.security.providers.openid_provider import OpenIDProvider
|
from mealie.core.security.providers.openid_provider import OpenIDProvider
|
||||||
from mealie.repos.all_repositories import get_repositories
|
from mealie.repos.all_repositories import get_repositories
|
||||||
from tests.utils.factories import random_email, random_string
|
from tests.utils.factories import random_email, random_string
|
||||||
|
@ -12,13 +14,15 @@ from tests.utils.fixture_schemas import TestUser
|
||||||
def test_no_claims():
|
def test_no_claims():
|
||||||
auth_provider = OpenIDProvider(None, None)
|
auth_provider = OpenIDProvider(None, None)
|
||||||
|
|
||||||
assert auth_provider.authenticate() is None
|
with pytest.raises(MissingClaimException):
|
||||||
|
auth_provider.authenticate()
|
||||||
|
|
||||||
|
|
||||||
def test_empty_claims():
|
def test_empty_claims():
|
||||||
auth_provider = OpenIDProvider(None, {})
|
auth_provider = OpenIDProvider(None, {})
|
||||||
|
|
||||||
assert auth_provider.authenticate() is None
|
with pytest.raises(MissingClaimException):
|
||||||
|
auth_provider.authenticate()
|
||||||
|
|
||||||
|
|
||||||
def test_empty_required_claims():
|
def test_empty_required_claims():
|
||||||
|
@ -30,14 +34,16 @@ def test_empty_required_claims():
|
||||||
}
|
}
|
||||||
auth_provider = OpenIDProvider(None, data)
|
auth_provider = OpenIDProvider(None, data)
|
||||||
|
|
||||||
assert auth_provider.authenticate() is None
|
with pytest.raises(MissingClaimException):
|
||||||
|
auth_provider.authenticate()
|
||||||
|
|
||||||
|
|
||||||
def test_missing_claims():
|
def test_missing_claims():
|
||||||
data = {"preferred_username": "dude1"}
|
data = {"preferred_username": "dude1"}
|
||||||
auth_provider = OpenIDProvider(None, data)
|
auth_provider = OpenIDProvider(None, data)
|
||||||
|
|
||||||
assert auth_provider.authenticate() is None
|
with pytest.raises(MissingClaimException):
|
||||||
|
auth_provider.authenticate()
|
||||||
|
|
||||||
|
|
||||||
def test_missing_groups_claim(monkeypatch: MonkeyPatch):
|
def test_missing_groups_claim(monkeypatch: MonkeyPatch):
|
||||||
|
@ -51,7 +57,8 @@ def test_missing_groups_claim(monkeypatch: MonkeyPatch):
|
||||||
}
|
}
|
||||||
auth_provider = OpenIDProvider(None, data)
|
auth_provider = OpenIDProvider(None, data)
|
||||||
|
|
||||||
assert auth_provider.authenticate() is None
|
with pytest.raises(MissingClaimException):
|
||||||
|
auth_provider.authenticate()
|
||||||
|
|
||||||
|
|
||||||
def test_missing_user_group(monkeypatch: MonkeyPatch):
|
def test_missing_user_group(monkeypatch: MonkeyPatch):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue