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 <jens@goauthentik.io> * fix error Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		| @ -30,6 +30,8 @@ class SourceTypeSerializer(PassiveSerializer): | |||||||
|     authorization_url = CharField(read_only=True, allow_null=True) |     authorization_url = CharField(read_only=True, allow_null=True) | ||||||
|     access_token_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) |     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): | class OAuthSourceSerializer(SourceSerializer): | ||||||
| @ -56,7 +58,11 @@ class OAuthSourceSerializer(SourceSerializer): | |||||||
|  |  | ||||||
|     def validate(self, attrs: dict) -> dict: |     def validate(self, attrs: dict) -> dict: | ||||||
|         session = get_http_session() |         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 != "": |         if well_known and well_known != "": | ||||||
|             try: |             try: | ||||||
|                 well_known_config = session.get(well_known) |                 well_known_config = session.get(well_known) | ||||||
| @ -69,20 +75,22 @@ class OAuthSourceSerializer(SourceSerializer): | |||||||
|                 attrs["authorization_url"] = config["authorization_endpoint"] |                 attrs["authorization_url"] = config["authorization_endpoint"] | ||||||
|                 attrs["access_token_url"] = config["token_endpoint"] |                 attrs["access_token_url"] = config["token_endpoint"] | ||||||
|                 attrs["profile_url"] = config["userinfo_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: |             except (IndexError, KeyError) as exc: | ||||||
|                 raise ValidationError( |                 raise ValidationError( | ||||||
|                     {"oidc_well_known_url": f"Invalid well-known configuration: {exc}"} |                     {"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 != "": |         if jwks_url and jwks_url != "": | ||||||
|  |             attrs["oidc_jwks_url"] = jwks_url | ||||||
|             try: |             try: | ||||||
|                 jwks_config = session.get(jwks_url) |                 jwks_config = session.get(jwks_url) | ||||||
|                 jwks_config.raise_for_status() |                 jwks_config.raise_for_status() | ||||||
|             except RequestException as exc: |             except RequestException as exc: | ||||||
|                 text = exc.response.text if exc.response else str(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() |             config = jwks_config.json() | ||||||
|             attrs["oidc_jwks"] = config |             attrs["oidc_jwks"] = config | ||||||
|  |  | ||||||
|  | |||||||
| @ -51,3 +51,7 @@ class AzureADType(SourceType): | |||||||
|     authorization_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" |     authorization_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" | ||||||
|     access_token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"  # nosec |     access_token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"  # nosec | ||||||
|     profile_url = "https://graph.microsoft.com/v1.0/me" |     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" | ||||||
|  | |||||||
| @ -76,3 +76,7 @@ class GitHubType(SourceType): | |||||||
|     authorization_url = "https://github.com/login/oauth/authorize" |     authorization_url = "https://github.com/login/oauth/authorize" | ||||||
|     access_token_url = "https://github.com/login/oauth/access_token"  # nosec |     access_token_url = "https://github.com/login/oauth/access_token"  # nosec | ||||||
|     profile_url = "https://api.github.com/user" |     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" | ||||||
|  | |||||||
| @ -40,3 +40,5 @@ class GoogleType(SourceType): | |||||||
|     authorization_url = "https://accounts.google.com/o/oauth2/auth" |     authorization_url = "https://accounts.google.com/o/oauth2/auth" | ||||||
|     access_token_url = "https://oauth2.googleapis.com/token"  # nosec |     access_token_url = "https://oauth2.googleapis.com/token"  # nosec | ||||||
|     profile_url = "https://www.googleapis.com/oauth2/v1/userinfo" |     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" | ||||||
|  | |||||||
| @ -36,6 +36,8 @@ class SourceType: | |||||||
|     authorization_url: Optional[str] = None |     authorization_url: Optional[str] = None | ||||||
|     access_token_url: Optional[str] = None |     access_token_url: Optional[str] = None | ||||||
|     profile_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: |     def icon_url(self) -> str: | ||||||
|         """Get Icon URL for login""" |         """Get Icon URL for login""" | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								schema.yml
									
									
									
									
									
								
							| @ -40934,10 +40934,20 @@ components: | |||||||
|           type: string |           type: string | ||||||
|           readOnly: true |           readOnly: true | ||||||
|           nullable: true |           nullable: true | ||||||
|  |         oidc_well_known_url: | ||||||
|  |           type: string | ||||||
|  |           readOnly: true | ||||||
|  |           nullable: true | ||||||
|  |         oidc_jwks_url: | ||||||
|  |           type: string | ||||||
|  |           readOnly: true | ||||||
|  |           nullable: true | ||||||
|       required: |       required: | ||||||
|       - access_token_url |       - access_token_url | ||||||
|       - authorization_url |       - authorization_url | ||||||
|       - name |       - name | ||||||
|  |       - oidc_jwks_url | ||||||
|  |       - oidc_well_known_url | ||||||
|       - profile_url |       - profile_url | ||||||
|       - request_token_url |       - request_token_url | ||||||
|       - slug |       - slug | ||||||
|  | |||||||
| @ -192,7 +192,11 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> { | |||||||
|                           > |                           > | ||||||
|                               <input |                               <input | ||||||
|                                   type="text" |                                   type="text" | ||||||
|                                   value="${ifDefined(this.instance?.oidcWellKnownUrl)}" |                                   value="${first( | ||||||
|  |                                       this.instance?.oidcWellKnownUrl, | ||||||
|  |                                       this.providerType.oidcWellKnownUrl, | ||||||
|  |                                       "", | ||||||
|  |                                   )}" | ||||||
|                                   class="pf-c-form-control" |                                   class="pf-c-form-control" | ||||||
|                               /> |                               /> | ||||||
|                               <p class="pf-c-form__helper-text"> |                               <p class="pf-c-form__helper-text"> | ||||||
| @ -207,7 +211,11 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> { | |||||||
|                           > |                           > | ||||||
|                               <input |                               <input | ||||||
|                                   type="text" |                                   type="text" | ||||||
|                                   value="${ifDefined(this.instance?.oidcJwksUrl)}" |                                   value="${first( | ||||||
|  |                                       this.instance?.oidcJwksUrl, | ||||||
|  |                                       this.providerType.oidcJwksUrl, | ||||||
|  |                                       "", | ||||||
|  |                                   )}" | ||||||
|                                   class="pf-c-form-control" |                                   class="pf-c-form-control" | ||||||
|                               /> |                               /> | ||||||
|                               <p class="pf-c-form__helper-text"> |                               <p class="pf-c-form__helper-text"> | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2"> | <?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2"> | ||||||
|   <file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext"> |   <file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext"> | ||||||
|     <body> |     <body> | ||||||
|       <trans-unit id="s4caed5b7a7e5d89b"> |       <trans-unit id="s4caed5b7a7e5d89b"> | ||||||
| @ -613,9 +613,9 @@ | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="saa0e2675da69651b"> |       <trans-unit id="saa0e2675da69651b"> | ||||||
|         <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source> |         <source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source> | ||||||
|         <target>未找到 URL "  |         <target>未找到 URL "  | ||||||
|         <x id="0" equiv-text="${this.url}"/>"。</target> |         <x id="0" equiv-text="${this.url}"/>"。</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s58cd9c2fe836d9c6"> |       <trans-unit id="s58cd9c2fe836d9c6"> | ||||||
| @ -1067,8 +1067,8 @@ | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="sa8384c9c26731f83"> |       <trans-unit id="sa8384c9c26731f83"> | ||||||
|         <source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source> |         <source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source> | ||||||
|         <target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target> |         <target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s55787f4dfcdce52b"> |       <trans-unit id="s55787f4dfcdce52b"> | ||||||
| @ -1809,8 +1809,8 @@ | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="sa90b7809586c35ce"> |       <trans-unit id="sa90b7809586c35ce"> | ||||||
|         <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source> |         <source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source> | ||||||
|         <target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target> |         <target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s0410779cb47de312"> |       <trans-unit id="s0410779cb47de312"> | ||||||
| @ -3023,8 +3023,8 @@ doesn't pass when either or both of the selected options are equal or above the | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s76768bebabb7d543"> |       <trans-unit id="s76768bebabb7d543"> | ||||||
|         <source>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,...'</source> |         <source>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,...'</source> | ||||||
|         <target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target> |         <target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s026555347e589f0e"> |       <trans-unit id="s026555347e589f0e"> | ||||||
| @ -3816,8 +3816,8 @@ doesn't pass when either or both of the selected options are equal or above the | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s7b1fba26d245cb1c"> |       <trans-unit id="s7b1fba26d245cb1c"> | ||||||
|         <source>When using an external logging solution for archiving, this can be set to "minutes=5".</source> |         <source>When using an external logging solution for archiving, this can be set to "minutes=5".</source> | ||||||
|         <target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target> |         <target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s44536d20bb5c8257"> |       <trans-unit id="s44536d20bb5c8257"> | ||||||
| @ -3826,8 +3826,8 @@ doesn't pass when either or both of the selected options are equal or above the | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s3bb51cabb02b997e"> |       <trans-unit id="s3bb51cabb02b997e"> | ||||||
|         <source>Format: "weeks=3;days=2;hours=3,seconds=2".</source> |         <source>Format: "weeks=3;days=2;hours=3,seconds=2".</source> | ||||||
|         <target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target> |         <target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s04bfd02201db5ab8"> |       <trans-unit id="s04bfd02201db5ab8"> | ||||||
| @ -4023,10 +4023,10 @@ doesn't pass when either or both of the selected options are equal or above the | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="sa95a538bfbb86111"> |       <trans-unit id="sa95a538bfbb86111"> | ||||||
|         <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source> |         <source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source> | ||||||
|         <target>您确定要更新  |         <target>您确定要更新  | ||||||
|         <x id="0" equiv-text="${this.objectLabel}"/>"  |         <x id="0" equiv-text="${this.objectLabel}"/>"  | ||||||
|         <x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target> |         <x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="sc92d7cfb6ee1fec6"> |       <trans-unit id="sc92d7cfb6ee1fec6"> | ||||||
| @ -5122,7 +5122,7 @@ doesn't pass when either or both of the selected options are equal or above the | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="sdf1d8edef27236f0"> |       <trans-unit id="sdf1d8edef27236f0"> | ||||||
|         <source>A "roaming" authenticator, like a YubiKey</source> |         <source>A "roaming" authenticator, like a YubiKey</source> | ||||||
|         <target>像 YubiKey 这样的“漫游”身份验证器</target> |         <target>像 YubiKey 这样的“漫游”身份验证器</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
| @ -5457,10 +5457,10 @@ doesn't pass when either or both of the selected options are equal or above the | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s2d5f69929bb7221d"> |       <trans-unit id="s2d5f69929bb7221d"> | ||||||
|         <source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source> |         <source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source> | ||||||
|         <target> |         <target> | ||||||
|         <x id="0" equiv-text="${prompt.name}"/>("  |         <x id="0" equiv-text="${prompt.name}"/>("  | ||||||
|         <x id="1" equiv-text="${prompt.fieldKey}"/>",类型为  |         <x id="1" equiv-text="${prompt.fieldKey}"/>",类型为  | ||||||
|         <x id="2" equiv-text="${prompt.type}"/>)</target> |         <x id="2" equiv-text="${prompt.type}"/>)</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
| @ -5509,7 +5509,7 @@ doesn't pass when either or both of the selected options are equal or above the | |||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
|       <trans-unit id="s1608b2f94fa0dbd4"> |       <trans-unit id="s1608b2f94fa0dbd4"> | ||||||
|         <source>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.</source> |         <source>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.</source> | ||||||
|         <target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target> |         <target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target> | ||||||
|          |          | ||||||
|       </trans-unit> |       </trans-unit> | ||||||
| @ -7900,4 +7900,4 @@ Bindings to groups/users are checked against the user of the event.</source> | |||||||
| </trans-unit> | </trans-unit> | ||||||
|     </body> |     </body> | ||||||
|   </file> |   </file> | ||||||
| </xliff> | </xliff> | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L