mealie-recipes / mealie

Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and mealie will automatically import the relevant data or add a family recipe with the UI editor
https://docs.mealie.io
GNU Affero General Public License v3.0
7.19k stars 724 forks source link

[BUG] - OIDC infinite loop if user is not in OIDC_USER_GROUP #3483

Closed seppeel closed 6 months ago

seppeel commented 6 months ago

First Check

What is the issue you are experiencing?

When trying to log in via OIDC but the user is not in the group defined in OIDC_USER_GROUP i am ending up in an infinite redirect loop at the mealie login page.

Steps to Reproduce

  1. Create a testuser in lldap backend of authelia but don't add the user to the group defined in OIDC_USER_GROUP
  2. Configure OIDC (config details below)
  3. Go to mealie login page
  4. choose "Anmelden mit OAuth" (i have german locale)
  5. login with my testuser
  6. confirm the authelia request that mealie may use identity, mail, name and group
  7. get redirected back to mealie but end up in an infinite loop

Please provide relevant logs

mealie     | INFO     2024-04-17T15:42:32 - [127.0.0.1:57024] 200 OK "GET /api/app/about HTTP/1.1"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 307 Temporary Redirect "GET /login?code=authelia_<xxxx> HTTP/1.0"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 200 OK "GET /login/?code=authelia_<xxxx> HTTP/1.0"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 500 Internal Server Error "POST /api/auth/token HTTP/1.0"
mealie     | ERROR    2024-04-17T15:42:42 - Exception in ASGI application
mealie     | Traceback (most recent call last):
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 411, in run_asgi
mealie     |     result = await app(  # type: ignore[func-returns-value]
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 69, in __call__
mealie     |     return await self.app(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/applications.py", line 1054, in __call__
mealie     |     await super().__call__(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/applications.py", line 123, in __call__
mealie     |     await self.middleware_stack(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 186, in __call__
mealie     |     raise exc
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 164, in __call__
mealie     |     await self.app(scope, receive, _send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 24, in __call__
mealie     |     await responder(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 44, in __call__
mealie     |     await self.app(scope, receive, self.send_with_gzip)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
mealie     |     await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
mealie     |     raise exc
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
mealie     |     await app(scope, receive, sender)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 756, in __call__
mealie     |     await self.middleware_stack(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 776, in app
mealie     |     await route.handle(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 297, in handle
mealie     |     await self.app(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 77, in app
mealie     |     await wrap_app_handling_exceptions(app, request)(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
mealie     |     raise exc
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
mealie     |     await app(scope, receive, sender)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 72, in app
mealie     |     response = await func(request)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/routing.py", line 278, in app
mealie     |     raw_response = await run_endpoint_function(
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
mealie     |     return await dependant.call(**values)
mealie     |   File "/app/mealie/routes/auth/auth.py", line 50, in get_token
mealie     |     auth = await auth_provider.authenticate()
mealie     |   File "/app/mealie/core/security/providers/openid_provider.py", line 39, in authenticate
mealie     |     is_admin = settings.OIDC_ADMIN_GROUP in group_claim if settings.OIDC_ADMIN_GROUP else False
mealie     | TypeError: argument of type 'NoneType' is not iterable
mealie     | ERROR    2024-04-17T15:42:42 - Exception in ASGI application
mealie     | Traceback (most recent call last):
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 411, in run_asgi
mealie     |     result = await app(  # type: ignore[func-returns-value]
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 69, in __call__
mealie     |     return await self.app(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/applications.py", line 1054, in __call__
mealie     |     await super().__call__(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/applications.py", line 123, in __call__
mealie     |     await self.middleware_stack(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 186, in __call__
mealie     |     raise exc
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 164, in __call__
mealie     |     await self.app(scope, receive, _send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 24, in __call__
mealie     |     await responder(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 44, in __call__
mealie     |     await self.app(scope, receive, self.send_with_gzip)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
mealie     |     await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
mealie     |     raise exc
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
mealie     |     await app(scope, receive, sender)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 756, in __call__
mealie     |     await self.middleware_stack(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 776, in app
mealie     |     await route.handle(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 297, in handle
mealie     |     await self.app(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 77, in app
mealie     |     await wrap_app_handling_exceptions(app, request)(scope, receive, send)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
mealie     |     raise exc
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
mealie     |     await app(scope, receive, sender)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 72, in app
mealie     |     response = await func(request)
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/routing.py", line 278, in app
mealie     |     raw_response = await run_endpoint_function(
mealie     |   File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
mealie     |     return await dependant.call(**values)
mealie     |   File "/app/mealie/routes/auth/auth.py", line 50, in get_token
mealie     |     auth = await auth_provider.authenticate()
mealie     |   File "/app/mealie/core/security/providers/openid_provider.py", line 39, in authenticate
mealie     |     is_admin = settings.OIDC_ADMIN_GROUP in group_claim if settings.OIDC_ADMIN_GROUP else False
mealie     | TypeError: argument of type 'NoneType' is not iterable
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 307 Temporary Redirect "GET /login?direct=1 HTTP/1.0"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 401 Unauthorized "GET /api/users/self HTTP/1.0"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 200 OK "GET /login/?direct=1 HTTP/1.0"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 200 OK "GET /api/app/about HTTP/1.0"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 200 OK "GET /api/app/about HTTP/1.0"
mealie     | INFO     2024-04-17T15:42:42 - [xxxx:0] 500 Internal Server Error "POST /api/auth/token HTTP/1.0"
mealie     | ERROR    2024-04-17T15:42:42 - Exception in ASGI application
mealie     | Traceback (most recent call last):

and then is repeats this endlessly.

Mealie Version

1.5.0

Deployment

Docker (Linux)

Additional Deployment Details

My OIDC Environment settings:

  - OIDC_AUTH_ENABLED=true
  - OIDC_SIGNUP_ENABLED=true
  - OIDC_CONFIGURATION_URL=https://auth.<myhostname>.com/.well-known/openid-configuration
  - OIDC_CLIENT_ID=mealie
  - OIDC_AUTO_REDIRECT=true
  - OIDC_ADMIN_GROUP=mealie_admin
  - OIDC_USER_GROUP=mealie
jimski427 commented 6 months ago

OIDC_USER_GROUP=mealie means that anyone you want to access mealie, regardless of admin status or not, needs to be in the mealie group, then users in that group that are also in OIDC_ADMIN_GROUP=mealie_admin will me made admins. I have Authentik setup so only certain groups can use mealie anyways, so i just don't use OIDC_USER_GROUP and it works fine.

seppeel commented 6 months ago

OIDC_USER_GROUP=mealie means that anyone you want to access mealie, regardless of admin status or not, needs to be in the mealie group, then users in that group that are also in OIDC_ADMIN_GROUP=mealie_admin will me made admins. I have Authentik setup so only certain groups can use mealie anyways, so i just don't use OIDC_USER_GROUP and it works fine.

thats not the issue here. I know how i get my oidc login to work but the way mealie handles a missing group membership is not good. No user should ever end up in an infinite loop and that should be fixed.

jimski427 commented 6 months ago

I agree. Informing the end user could be better handled. But you're telling mealie to only allow people who are part of OIDC_USER_GROUP to login... and then they can't login. It makes sense.

BossDarkReaper commented 6 months ago

I am not setting either the OIDC_USER_GROUP or OIDC_ADMIN_GROUP and am seeing the infinite loop also. But my error seems to be different from the logs that have been shared.

ERROR    2024-04-17T15:01:32 - Exception in ASGI application
Traceback (most recent call last):
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 411, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 69, in __call__
    return await self.app(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 24, in __call__
    await responder(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 44, in __call__
    await self.app(scope, receive, self.send_with_gzip)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/starlette/routing.py", line 72, in app
    response = await func(request)
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/routing.py", line 278, in app
    raw_response = await run_endpoint_function(
  File "/opt/pysetup/.venv/lib/python3.10/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
    return await dependant.call(**values)
  File "/app/mealie/routes/auth/auth.py", line 50, in get_token
    auth = await auth_provider.authenticate()
  File "/app/mealie/core/security/providers/openid_provider.py", line 31, in authenticate
    claims = self.get_claims(settings)
  File "/app/mealie/core/security/providers/openid_provider.py", line 96, in get_claims
    claims.validate()
UnboundLocalError: local variable 'claims' referenced before assignment
INFO     2024-04-17T15:01:32 - [86.177.44.229:0] 307 Temporary Redirect "GET /login?direct=1 HTTP/1.1"
INFO     2024-04-17T15:01:32 - [86.177.44.229:0] 200 OK "GET /api/app/about HTTP/1.1"