providers/oauth2: allow m2m for JWKS without alg in keys (#12196)
* providers/oauth2: allow m2m for JWKS without alg in keys Signed-off-by: Jens Langhammer <jens@goauthentik.io> * Update index.md Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com> Signed-off-by: Jens L. <jens@beryju.org> * Apply suggestions from code review Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com> Signed-off-by: Jens L. <jens@beryju.org> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Signed-off-by: Jens L. <jens@beryju.org> Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
This commit is contained in:
		| @ -393,19 +393,22 @@ class TokenParams: | |||||||
|             LOGGER.warning("failed to parse JWT for kid lookup", exc=exc) |             LOGGER.warning("failed to parse JWT for kid lookup", exc=exc) | ||||||
|             raise TokenError("invalid_grant") from None |             raise TokenError("invalid_grant") from None | ||||||
|         expected_kid = decode_unvalidated["header"]["kid"] |         expected_kid = decode_unvalidated["header"]["kid"] | ||||||
|  |         fallback_alg = decode_unvalidated["header"]["alg"] | ||||||
|         for source in self.provider.jwks_sources.filter( |         for source in self.provider.jwks_sources.filter( | ||||||
|             oidc_jwks__keys__contains=[{"kid": expected_kid}] |             oidc_jwks__keys__contains=[{"kid": expected_kid}] | ||||||
|         ): |         ): | ||||||
|             LOGGER.debug("verifying JWT with source", source=source.slug) |             LOGGER.debug("verifying JWT with source", source=source.slug) | ||||||
|             keys = source.oidc_jwks.get("keys", []) |             keys = source.oidc_jwks.get("keys", []) | ||||||
|             for key in keys: |             for key in keys: | ||||||
|  |                 if key.get("kid") and key.get("kid") != expected_kid: | ||||||
|  |                     continue | ||||||
|                 LOGGER.debug("verifying JWT with key", source=source.slug, key=key.get("kid")) |                 LOGGER.debug("verifying JWT with key", source=source.slug, key=key.get("kid")) | ||||||
|                 try: |                 try: | ||||||
|                     parsed_key = PyJWK.from_dict(key) |                     parsed_key = PyJWK.from_dict(key) | ||||||
|                     token = decode( |                     token = decode( | ||||||
|                         assertion, |                         assertion, | ||||||
|                         parsed_key.key, |                         parsed_key.key, | ||||||
|                         algorithms=[key.get("alg")], |                         algorithms=[key.get("alg")] if "alg" in key else [fallback_alg], | ||||||
|                         options={ |                         options={ | ||||||
|                             "verify_aud": False, |                             "verify_aud": False, | ||||||
|                         }, |                         }, | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ Hence identification is based on service-accounts, and authentication is based o | |||||||
|  |  | ||||||
| An example request can look like this: | An example request can look like this: | ||||||
|  |  | ||||||
| ``` | ```http | ||||||
| POST /application/o/token/ HTTP/1.1 | POST /application/o/token/ HTTP/1.1 | ||||||
| Host: authentik.company | Host: authentik.company | ||||||
| Content-Type: application/x-www-form-urlencoded | Content-Type: application/x-www-form-urlencoded | ||||||
| @ -42,7 +42,7 @@ Starting with authentik 2022.6, you can define a JWKS URL/raw JWKS data in OAuth | |||||||
|  |  | ||||||
| With this configure, any JWT issued by the configured certificates can be used to authenticate: | With this configure, any JWT issued by the configured certificates can be used to authenticate: | ||||||
|  |  | ||||||
| ``` | ```http | ||||||
| POST /application/o/token/ HTTP/1.1 | POST /application/o/token/ HTTP/1.1 | ||||||
| Host: authentik.company | Host: authentik.company | ||||||
| Content-Type: application/x-www-form-urlencoded | Content-Type: application/x-www-form-urlencoded | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ authentik doesn't ship with a default flow for this usecase, so it is recommende | |||||||
|  |  | ||||||
| The flow is initiated by sending a POST request to the device authorization endpoint, `/application/o/device/` with the following contents: | The flow is initiated by sending a POST request to the device authorization endpoint, `/application/o/device/` with the following contents: | ||||||
|  |  | ||||||
| ``` | ```http | ||||||
| POST /application/o/device/ HTTP/1.1 | POST /application/o/device/ HTTP/1.1 | ||||||
| Host: authentik.company | Host: authentik.company | ||||||
| Content-Type: application/x-www-form-urlencoded | Content-Type: application/x-www-form-urlencoded | ||||||
| @ -36,7 +36,7 @@ The response contains the following fields: | |||||||
|  |  | ||||||
| With this response, the device can start checking the status of the token by sending requests to the token endpoint like this: | With this response, the device can start checking the status of the token by sending requests to the token endpoint like this: | ||||||
|  |  | ||||||
| ``` | ```http | ||||||
| POST /application/o/token/ HTTP/1.1 | POST /application/o/token/ HTTP/1.1 | ||||||
| Host: authentik.company | Host: authentik.company | ||||||
| Content-Type: application/x-www-form-urlencoded | Content-Type: application/x-www-form-urlencoded | ||||||
|  | |||||||
| @ -111,3 +111,22 @@ return True | |||||||
| 9. Open **Flow settings** and choose _azure-ad-enrollment_ as enrollment flow. | 9. Open **Flow settings** and choose _azure-ad-enrollment_ as enrollment flow. | ||||||
|  |  | ||||||
| Try to login with a **_new_** user. You should see no prompts and the user should have the correct information. | Try to login with a **_new_** user. You should see no prompts and the user should have the correct information. | ||||||
|  |  | ||||||
|  | ### Machine-to-machine authentication <span class="badge badge--version">authentik 2024.12+</span> | ||||||
|  |  | ||||||
|  | If using [Machine-to-Machine](../../../../add-secure-apps/providers/oauth2/client_credentials.md#jwt-authentication) authentication, some specific steps need to be considered. | ||||||
|  |  | ||||||
|  | When getting the JWT token from Azure AD, set the scope to the Application ID URI, and _not_ the Graph URL; otherwise the JWT will be in an invalid format. | ||||||
|  |  | ||||||
|  | ```http | ||||||
|  | POST /<azure-ad-tenant-id>/oauth2/v2.0/token/ HTTP/1.1 | ||||||
|  | Host: login.microsoftonline.com | ||||||
|  | Content-Type: application/x-www-form-urlencoded | ||||||
|  |  | ||||||
|  | grant_type=client_credentials& | ||||||
|  | client_id=<application_client_id>& | ||||||
|  | scope=api://<application_client_id>/.default& | ||||||
|  | client_secret=<application_client_secret> | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The JWT returned from the request above can be used with authentik to exchange it for an authentik JWT. | ||||||
|  | |||||||
| @ -80,7 +80,7 @@ module.exports = async function (): Promise<Config> { | |||||||
|             prism: { |             prism: { | ||||||
|                 theme: prismThemes.oneLight, |                 theme: prismThemes.oneLight, | ||||||
|                 darkTheme: prismThemes.oneDark, |                 darkTheme: prismThemes.oneDark, | ||||||
|                 additionalLanguages: ["python", "diff", "json"], |                 additionalLanguages: ["python", "diff", "json", "http"], | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|         presets: [ |         presets: [ | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L.
					Jens L.