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:
Jens L 2023-10-20 16:54:03 +02:00 committed by GitHub
parent 63c52fd936
commit 63426bc9a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 29 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"""

View File

@ -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

View File

@ -192,7 +192,11 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
>
<input
type="text"
value="${ifDefined(this.instance?.oidcWellKnownUrl)}"
value="${first(
this.instance?.oidcWellKnownUrl,
this.providerType.oidcWellKnownUrl,
"",
)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
@ -207,7 +211,11 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
>
<input
type="text"
value="${ifDefined(this.instance?.oidcJwksUrl)}"
value="${first(
this.instance?.oidcJwksUrl,
this.providerType.oidcJwksUrl,
"",
)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">

View File

@ -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">
<body>
<trans-unit id="s4caed5b7a7e5d89b">
@ -613,9 +613,9 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source>
<target>未找到 URL &quot;
<x id="0" equiv-text="${this.url}"/>&quot;。</target>
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>未找到 URL "
<x id="0" equiv-text="${this.url}"/>"。</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -1067,8 +1067,8 @@
</trans-unit>
<trans-unit id="sa8384c9c26731f83">
<source>To allow any redirect URI, set this value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请将此值设置为 &quot;.*&quot;。请注意这可能带来的安全影响。</target>
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
</trans-unit>
<trans-unit id="s55787f4dfcdce52b">
@ -1809,8 +1809,8 @@
</trans-unit>
<trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source>
<target>输入完整 URL、相对路径或者使用 'fa://fa-test' 来使用 Font Awesome 图标 &quot;fa-test&quot;。</target>
<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>
</trans-unit>
<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 id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; 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>包含组成员的字段。请注意,如果使用 &quot;memberUid&quot; 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
<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>
</trans-unit>
<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 id="s7b1fba26d245cb1c">
<source>When using an external logging solution for archiving, this can be set to &quot;minutes=5&quot;.</source>
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 &quot;minutes=5&quot;。</target>
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
</trans-unit>
<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 id="s3bb51cabb02b997e">
<source>Format: &quot;weeks=3;days=2;hours=3,seconds=2&quot;.</source>
<target>格式:&quot;weeks=3;days=2;hours=3,seconds=2&quot;。</target>
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
</trans-unit>
<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 id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</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>您确定要更新
<x id="0" equiv-text="${this.objectLabel}"/>&quot;
<x id="1" equiv-text="${this.obj?.name}"/>&quot; 吗?</target>
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
</trans-unit>
<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 id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source>
<source>A "roaming" authenticator, like a YubiKey</source>
<target>像 YubiKey 这样的“漫游”身份验证器</target>
</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 id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> (&quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;, 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>
<x id="0" equiv-text="${prompt.name}"/>&quot;
<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;,类型为
<x id="0" equiv-text="${prompt.name}"/>"
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
<x id="2" equiv-text="${prompt.type}"/></target>
</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 id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, 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>
</trans-unit>
@ -7900,4 +7900,4 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
</body>
</file>
</xliff>
</xliff>