mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-30 17:53:31 -04:00 
			
		
		
		
	Merge pull request #3354 from cmintey/signing-algorithm-env-var
Add OIDC environment variable for specififying the signing algorithm
This commit is contained in:
		| @@ -46,6 +46,14 @@ Before you can start using OIDC Authentication, you must first configure a new c | |||||||
|  |  | ||||||
| Take the client id and your discovery URL and update your environment variables to include the required OIDC variables described in [Installation - Backend Configuration](../installation/backend-config.md#openid-connect-oidc). | Take the client id and your discovery URL and update your environment variables to include the required OIDC variables described in [Installation - Backend Configuration](../installation/backend-config.md#openid-connect-oidc). | ||||||
|  |  | ||||||
|  | ### Groups | ||||||
|  |  | ||||||
|  | There are two (optional) [environment variables](../installation/backend-config.md#openid-connect-oidc) that can control which of the users in your IdP can log in to Mealie and what permissions they will have. The groups should be **defined in your IdP** and be returned in the `groups` claim. | ||||||
|  |  | ||||||
|  | `OIDC_USER_GROUP`: Users must be a part of this group (within your IdP) to be able to log in. | ||||||
|  |  | ||||||
|  | `OIDC_ADMIN_GROUP`: Users that are in this group (within your IdP) will be made an **admin** in Mealie. | ||||||
|  |  | ||||||
| ## Examples | ## Examples | ||||||
|  |  | ||||||
| Example configurations for several Identity Providers have been provided by the Community in the [GitHub Discussions](https://github.com/mealie-recipes/mealie/discussions/categories/oauth-provider-example). | Example configurations for several Identity Providers have been provided by the Community in the [GitHub Discussions](https://github.com/mealie-recipes/mealie/discussions/categories/oauth-provider-example). | ||||||
|   | |||||||
| @@ -85,11 +85,12 @@ For usage, see [Usage - OpenID Connect](../authentication/oidc.md) | |||||||
| | OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC | | | OIDC_SIGNUP_ENABLED | True | Enables new users to be created when signing in for the first time with OIDC | | ||||||
| | OIDC_CONFIGURATION_URL | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration | | | OIDC_CONFIGURATION_URL | None | The URL to the OIDC configuration of your provider. This is usually something like https://auth.example.com/.well-known/openid-configuration | | ||||||
| | OIDC_CLIENT_ID | None | The client id of your configured client in your provider | | | OIDC_CLIENT_ID | None | The client id of your configured client in your provider | | ||||||
| | OIDC_USER_GROUP| None | If specified, only users belonging to this group will be able to successfully authenticate, regardless of the OIDC_ADMIN_GROUP | | | OIDC_USER_GROUP| None | If specified, only users belonging to this group will be able to successfully authenticate, regardless of the `OIDC_ADMIN_GROUP`. For more information see [this page](../authentication/oidc.md#groups) | | ||||||
| | OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be made an admin | | | OIDC_ADMIN_GROUP | None | If specified, users belonging to this group will be made an admin. For more information see [this page](../authentication/oidc.md#groups) | | ||||||
| | OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed an you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL | | | OIDC_AUTO_REDIRECT | False | If `True`, then the login page will be bypassed an you will be sent directly to your Identity Provider. You can still get to the login page by adding `?direct=1` to the login URL | | ||||||
| | OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with <OIDC_PROVIDER_NAME\>" | | | OIDC_PROVIDER_NAME | OAuth | The provider name is shown in SSO login button. "Login with <OIDC_PROVIDER_NAME\>" | | ||||||
| | OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked | | | OIDC_REMEMBER_ME | False | Because redirects bypass the login screen, you cant extend your session by clicking the "Remember Me" checkbox. By setting this value to true, a session will be extended as if "Remember Me" was checked | | ||||||
|  | | OIDC_SIGNING_ALGORITHM | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) | | ||||||
|  |  | ||||||
| ### Themeing | ### Themeing | ||||||
|  |  | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -3,13 +3,14 @@ from functools import lru_cache | |||||||
|  |  | ||||||
| import requests | import requests | ||||||
| from authlib.jose import JsonWebKey, JsonWebToken, JWTClaims, KeySet | from authlib.jose import JsonWebKey, JsonWebToken, JWTClaims, KeySet | ||||||
| from authlib.jose.errors import ExpiredTokenError | from authlib.jose.errors import ExpiredTokenError, UnsupportedAlgorithmError | ||||||
| from authlib.oidc.core import CodeIDToken | from authlib.oidc.core import CodeIDToken | ||||||
| from sqlalchemy.orm.session import Session | 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.security.providers.auth_provider import AuthProvider | from mealie.core.security.providers.auth_provider import AuthProvider | ||||||
|  | from mealie.core.settings.settings import AppSettings | ||||||
| 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 | ||||||
| from mealie.schema.user.auth import OIDCRequest | from mealie.schema.user.auth import OIDCRequest | ||||||
| @@ -26,11 +27,11 @@ class OpenIDProvider(AuthProvider[OIDCRequest]): | |||||||
|     async def authenticate(self) -> tuple[str, timedelta] | None: |     async def authenticate(self) -> tuple[str, timedelta] | None: | ||||||
|         """Attempt to authenticate a user given a username and password""" |         """Attempt to authenticate a user given a username and password""" | ||||||
|  |  | ||||||
|         claims = self.get_claims() |         settings = get_app_settings() | ||||||
|  |         claims = self.get_claims(settings) | ||||||
|         if not claims: |         if not claims: | ||||||
|             return None |             return None | ||||||
|  |  | ||||||
|         settings = get_app_settings() |  | ||||||
|         repos = get_repositories(self.session) |         repos = get_repositories(self.session) | ||||||
|  |  | ||||||
|         user = self.try_get_user(claims.get("email")) |         user = self.try_get_user(claims.get("email")) | ||||||
| @@ -76,13 +77,20 @@ class OpenIDProvider(AuthProvider[OIDCRequest]): | |||||||
|         self._logger.info("[OIDC] Found user but their AuthMethod does not match OIDC") |         self._logger.info("[OIDC] Found user but their AuthMethod does not match OIDC") | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|     def get_claims(self) -> JWTClaims | None: |     def get_claims(self, settings: AppSettings) -> JWTClaims | None: | ||||||
|         """Get the claims from the ID token and check if the required claims are present""" |         """Get the claims from the ID token and check if the required claims are present""" | ||||||
|         required_claims = {"preferred_username", "name", "email"} |         required_claims = {"preferred_username", "name", "email"} | ||||||
|         jwks = OpenIDProvider.get_jwks() |         jwks = OpenIDProvider.get_jwks() | ||||||
|         if not jwks: |         if not jwks: | ||||||
|             return None |             return None | ||||||
|         claims = JsonWebToken(["RS256"]).decode(s=self.data.id_token, key=jwks, claims_cls=CodeIDToken) |  | ||||||
|  |         algorithm = settings.OIDC_SIGNING_ALGORITHM | ||||||
|  |         try: | ||||||
|  |             claims = JsonWebToken([algorithm]).decode(s=self.data.id_token, key=jwks, claims_cls=CodeIDToken) | ||||||
|  |         except UnsupportedAlgorithmError: | ||||||
|  |             self._logger.error( | ||||||
|  |                 f"[OIDC] Unsupported algorithm '{algorithm}'. Unable to decode id token due to mismatched algorithm." | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             claims.validate() |             claims.validate() | ||||||
|   | |||||||
| @@ -182,6 +182,7 @@ class AppSettings(BaseSettings): | |||||||
|     OIDC_AUTO_REDIRECT: bool = False |     OIDC_AUTO_REDIRECT: bool = False | ||||||
|     OIDC_PROVIDER_NAME: str = "OAuth" |     OIDC_PROVIDER_NAME: str = "OAuth" | ||||||
|     OIDC_REMEMBER_ME: bool = False |     OIDC_REMEMBER_ME: bool = False | ||||||
|  |     OIDC_SIGNING_ALGORITHM: str = "RS256" | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def OIDC_READY(self) -> bool: |     def OIDC_READY(self) -> bool: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user