From 63426bc9a8b1caa68ff1bc5771ab9b8b9e3b76e0 Mon Sep 17 00:00:00 2001 From: Jens L Date: Fri, 20 Oct 2023 16:54:03 +0200 Subject: [PATCH] sources/oauth: include default JWKS URLs for OAuth sources (#6992) * sources/oauth: include default JWKS URLs for OAuth sources makes it easier to use pre-defined types like github, google, azure with JWT M2M instead of needing to create a generic OAuth Source Signed-off-by: Jens Langhammer * fix error Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- authentik/sources/oauth/api/source.py | 16 +++++-- authentik/sources/oauth/types/azure_ad.py | 4 ++ authentik/sources/oauth/types/github.py | 4 ++ authentik/sources/oauth/types/google.py | 2 + authentik/sources/oauth/types/registry.py | 2 + schema.yml | 10 ++++ .../admin/sources/oauth/OAuthSourceForm.ts | 12 ++++- web/xliff/zh-Hans.xlf | 46 +++++++++---------- 8 files changed, 67 insertions(+), 29 deletions(-) diff --git a/authentik/sources/oauth/api/source.py b/authentik/sources/oauth/api/source.py index 1a52a765f..62941f863 100644 --- a/authentik/sources/oauth/api/source.py +++ b/authentik/sources/oauth/api/source.py @@ -30,6 +30,8 @@ class SourceTypeSerializer(PassiveSerializer): authorization_url = CharField(read_only=True, allow_null=True) access_token_url = CharField(read_only=True, allow_null=True) profile_url = CharField(read_only=True, allow_null=True) + oidc_well_known_url = CharField(read_only=True, allow_null=True) + oidc_jwks_url = CharField(read_only=True, allow_null=True) class OAuthSourceSerializer(SourceSerializer): @@ -56,7 +58,11 @@ class OAuthSourceSerializer(SourceSerializer): def validate(self, attrs: dict) -> dict: session = get_http_session() - well_known = attrs.get("oidc_well_known_url") + source_type = registry.find_type(attrs["provider_type"]) + + well_known = attrs.get("oidc_well_known_url") or source_type.oidc_well_known_url + inferred_oidc_jwks_url = None + if well_known and well_known != "": try: well_known_config = session.get(well_known) @@ -69,20 +75,22 @@ class OAuthSourceSerializer(SourceSerializer): attrs["authorization_url"] = config["authorization_endpoint"] attrs["access_token_url"] = config["token_endpoint"] attrs["profile_url"] = config["userinfo_endpoint"] - attrs["oidc_jwks_url"] = config["jwks_uri"] + inferred_oidc_jwks_url = config["jwks_uri"] except (IndexError, KeyError) as exc: raise ValidationError( {"oidc_well_known_url": f"Invalid well-known configuration: {exc}"} ) - jwks_url = attrs.get("oidc_jwks_url") + # Prefer user-entered URL to inferred URL to default URL + jwks_url = attrs.get("oidc_jwks_url") or inferred_oidc_jwks_url or source_type.oidc_jwks_url if jwks_url and jwks_url != "": + attrs["oidc_jwks_url"] = jwks_url try: jwks_config = session.get(jwks_url) jwks_config.raise_for_status() except RequestException as exc: text = exc.response.text if exc.response else str(exc) - raise ValidationError({"jwks_url": text}) + raise ValidationError({"oidc_jwks_url": text}) config = jwks_config.json() attrs["oidc_jwks"] = config diff --git a/authentik/sources/oauth/types/azure_ad.py b/authentik/sources/oauth/types/azure_ad.py index 6e2f3730a..39c744843 100644 --- a/authentik/sources/oauth/types/azure_ad.py +++ b/authentik/sources/oauth/types/azure_ad.py @@ -51,3 +51,7 @@ class AzureADType(SourceType): authorization_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" access_token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token" # nosec profile_url = "https://graph.microsoft.com/v1.0/me" + oidc_well_known_url = ( + "https://login.microsoftonline.com/common/.well-known/openid-configuration" + ) + oidc_jwks_url = "https://login.microsoftonline.com/common/discovery/keys" diff --git a/authentik/sources/oauth/types/github.py b/authentik/sources/oauth/types/github.py index 262414180..70152d8b5 100644 --- a/authentik/sources/oauth/types/github.py +++ b/authentik/sources/oauth/types/github.py @@ -76,3 +76,7 @@ class GitHubType(SourceType): authorization_url = "https://github.com/login/oauth/authorize" access_token_url = "https://github.com/login/oauth/access_token" # nosec profile_url = "https://api.github.com/user" + oidc_well_known_url = ( + "https://token.actions.githubusercontent.com/.well-known/openid-configuration" + ) + oidc_jwks_url = "https://token.actions.githubusercontent.com/.well-known/jwks" diff --git a/authentik/sources/oauth/types/google.py b/authentik/sources/oauth/types/google.py index 0e0b3ce67..1956aadcd 100644 --- a/authentik/sources/oauth/types/google.py +++ b/authentik/sources/oauth/types/google.py @@ -40,3 +40,5 @@ class GoogleType(SourceType): authorization_url = "https://accounts.google.com/o/oauth2/auth" access_token_url = "https://oauth2.googleapis.com/token" # nosec profile_url = "https://www.googleapis.com/oauth2/v1/userinfo" + oidc_well_known_url = "https://accounts.google.com/.well-known/openid-configuration" + oidc_jwks_url = "https://www.googleapis.com/oauth2/v3/certs" diff --git a/authentik/sources/oauth/types/registry.py b/authentik/sources/oauth/types/registry.py index 786cc1303..ae8a5dd0b 100644 --- a/authentik/sources/oauth/types/registry.py +++ b/authentik/sources/oauth/types/registry.py @@ -36,6 +36,8 @@ class SourceType: authorization_url: Optional[str] = None access_token_url: Optional[str] = None profile_url: Optional[str] = None + oidc_well_known_url: Optional[str] = None + oidc_jwks_url: Optional[str] = None def icon_url(self) -> str: """Get Icon URL for login""" diff --git a/schema.yml b/schema.yml index a311785f0..5a6a347ab 100644 --- a/schema.yml +++ b/schema.yml @@ -40934,10 +40934,20 @@ components: type: string readOnly: true nullable: true + oidc_well_known_url: + type: string + readOnly: true + nullable: true + oidc_jwks_url: + type: string + readOnly: true + nullable: true required: - access_token_url - authorization_url - name + - oidc_jwks_url + - oidc_well_known_url - profile_url - request_token_url - slug diff --git a/web/src/admin/sources/oauth/OAuthSourceForm.ts b/web/src/admin/sources/oauth/OAuthSourceForm.ts index a5799eba1..5109ce0c4 100644 --- a/web/src/admin/sources/oauth/OAuthSourceForm.ts +++ b/web/src/admin/sources/oauth/OAuthSourceForm.ts @@ -192,7 +192,11 @@ export class OAuthSourceForm extends ModelForm { >

@@ -207,7 +211,11 @@ export class OAuthSourceForm extends ModelForm { >

diff --git a/web/xliff/zh-Hans.xlf b/web/xliff/zh-Hans.xlf index 953a19c6d..80b665610 100644 --- a/web/xliff/zh-Hans.xlf +++ b/web/xliff/zh-Hans.xlf @@ -1,4 +1,4 @@ - + @@ -613,9 +613,9 @@ - The URL "" was not found. - 未找到 URL " - "。 + The URL "" was not found. + 未找到 URL " + "。 @@ -1067,8 +1067,8 @@ - To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. - 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 + To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 @@ -1809,8 +1809,8 @@ - Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". - 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 + Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 @@ -3023,8 +3023,8 @@ doesn't pass when either or both of the selected options are equal or above the - Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' - 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' + Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...' + 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' @@ -3816,8 +3816,8 @@ doesn't pass when either or both of the selected options are equal or above the - When using an external logging solution for archiving, this can be set to "minutes=5". - 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 + When using an external logging solution for archiving, this can be set to "minutes=5". + 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 @@ -3826,8 +3826,8 @@ doesn't pass when either or both of the selected options are equal or above the - Format: "weeks=3;days=2;hours=3,seconds=2". - 格式:"weeks=3;days=2;hours=3,seconds=2"。 + Format: "weeks=3;days=2;hours=3,seconds=2". + 格式:"weeks=3;days=2;hours=3,seconds=2"。 @@ -4023,10 +4023,10 @@ doesn't pass when either or both of the selected options are equal or above the - Are you sure you want to update ""? + Are you sure you want to update ""? 您确定要更新 - " - " 吗? + " + " 吗? @@ -5122,7 +5122,7 @@ doesn't pass when either or both of the selected options are equal or above the - A "roaming" authenticator, like a YubiKey + A "roaming" authenticator, like a YubiKey 像 YubiKey 这样的“漫游”身份验证器 @@ -5457,10 +5457,10 @@ doesn't pass when either or both of the selected options are equal or above the - ("", of type ) + ("", of type ) - (" - ",类型为 + (" + ",类型为 @@ -5509,7 +5509,7 @@ doesn't pass when either or both of the selected options are equal or above the - If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. + If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here. 如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。 @@ -7900,4 +7900,4 @@ Bindings to groups/users are checked against the user of the event. - \ No newline at end of file +