Compare commits
36 Commits
providers/
...
providers/
Author | SHA1 | Date | |
---|---|---|---|
faf8bf591f | |||
52115f9345 | |||
b476551f13 | |||
f9563c25cd | |||
0067e6e155 | |||
ce183929d4 | |||
2fdf345271 | |||
bbcf8418b4 | |||
dc57be46f4 | |||
d68b3ba516 | |||
a9c46cfcbd | |||
c50353ebf6 | |||
db6be9e1b6 | |||
a74892886d | |||
74cd4c2236 | |||
ef3bd7e77b | |||
3f5ad2baa4 | |||
24805f087b | |||
9464b422a3 | |||
da6d4ede51 | |||
cecad5bfd3 | |||
bc4b07d57b | |||
e85d2d0096 | |||
be1dd3103b | |||
5dfde5e1d3 | |||
7cb1e6d81e | |||
d7c3129b1c | |||
2a1d33021b | |||
f273e49ae6 | |||
cc31957900 | |||
b1ccdecc8e | |||
34031003a4 | |||
055e1d1025 | |||
59a804273e | |||
bce70a1796 | |||
e86c40a00c |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2025.6.1
|
||||
current_version = 2025.6.2
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
||||
|
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
@ -202,7 +202,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: web/dist
|
||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
|
||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
|
||||
- name: prepare web ui
|
||||
if: steps.cache-web.outputs.cache-hit != 'true'
|
||||
working-directory: web
|
||||
|
@ -77,7 +77,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
# Stage 4: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.7.13 AS uv
|
||||
# Stage 5: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.4-slim-bookworm-fips AS python-base
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.5-slim-bookworm-fips AS python-base
|
||||
|
||||
ENV VENV_PATH="/ak-root/.venv" \
|
||||
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2025.6.1"
|
||||
__version__ = "2025.6.2"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
@ -15,7 +15,6 @@ class OAuth2Error(SentryIgnoredException):
|
||||
|
||||
error: str
|
||||
description: str
|
||||
cause: str | None = None
|
||||
|
||||
def create_dict(self):
|
||||
"""Return error as dict for JSON Rendering"""
|
||||
@ -35,10 +34,6 @@ class OAuth2Error(SentryIgnoredException):
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def with_cause(self, cause: str):
|
||||
self.cause = cause
|
||||
return self
|
||||
|
||||
|
||||
class RedirectUriError(OAuth2Error):
|
||||
"""The request fails due to a missing, invalid, or mismatching
|
||||
|
@ -12,7 +12,7 @@ from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.providers.oauth2.constants import SCOPE_OFFLINE_ACCESS, SCOPE_OPENID, TOKEN_TYPE
|
||||
from authentik.providers.oauth2.constants import TOKEN_TYPE
|
||||
from authentik.providers.oauth2.errors import AuthorizeError, ClientIdError, RedirectUriError
|
||||
from authentik.providers.oauth2.models import (
|
||||
AccessToken,
|
||||
@ -43,7 +43,7 @@ class TestAuthorize(OAuthTestCase):
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")],
|
||||
)
|
||||
with self.assertRaises(AuthorizeError) as cm:
|
||||
with self.assertRaises(AuthorizeError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -53,7 +53,6 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.error, "unsupported_response_type")
|
||||
|
||||
def test_invalid_client_id(self):
|
||||
"""Test invalid client ID"""
|
||||
@ -69,7 +68,7 @@ class TestAuthorize(OAuthTestCase):
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid/Foo")],
|
||||
)
|
||||
with self.assertRaises(AuthorizeError) as cm:
|
||||
with self.assertRaises(AuthorizeError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -80,30 +79,19 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.error, "request_not_supported")
|
||||
|
||||
def test_invalid_redirect_uri_missing(self):
|
||||
"""test missing redirect URI"""
|
||||
OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid")],
|
||||
)
|
||||
with self.assertRaises(RedirectUriError) as cm:
|
||||
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.cause, "redirect_uri_missing")
|
||||
|
||||
def test_invalid_redirect_uri(self):
|
||||
"""test invalid redirect URI"""
|
||||
"""test missing/invalid redirect URI"""
|
||||
OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid")],
|
||||
)
|
||||
with self.assertRaises(RedirectUriError) as cm:
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -113,7 +101,6 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
|
||||
|
||||
def test_blocked_redirect_uri(self):
|
||||
"""test missing/invalid redirect URI"""
|
||||
@ -121,9 +108,9 @@ class TestAuthorize(OAuthTestCase):
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "data:localhost")],
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "data:local.invalid")],
|
||||
)
|
||||
with self.assertRaises(RedirectUriError) as cm:
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -133,7 +120,6 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.cause, "redirect_uri_forbidden_scheme")
|
||||
|
||||
def test_invalid_redirect_uri_empty(self):
|
||||
"""test missing/invalid redirect URI"""
|
||||
@ -143,6 +129,9 @@ class TestAuthorize(OAuthTestCase):
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[],
|
||||
)
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -161,9 +150,12 @@ class TestAuthorize(OAuthTestCase):
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, "http://local.invalid?")],
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://local.invalid?")],
|
||||
)
|
||||
with self.assertRaises(RedirectUriError) as cm:
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -173,7 +165,6 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
|
||||
|
||||
def test_redirect_uri_invalid_regex(self):
|
||||
"""test missing/invalid redirect URI (invalid regex)"""
|
||||
@ -181,9 +172,12 @@ class TestAuthorize(OAuthTestCase):
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, "+")],
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "+")],
|
||||
)
|
||||
with self.assertRaises(RedirectUriError) as cm:
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -193,22 +187,23 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.cause, "redirect_uri_no_match")
|
||||
|
||||
def test_redirect_uri_regex(self):
|
||||
"""test valid redirect URI (regex)"""
|
||||
def test_empty_redirect_uri(self):
|
||||
"""test empty redirect URI (configure in provider)"""
|
||||
OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.REGEX, ".+")],
|
||||
)
|
||||
with self.assertRaises(RedirectUriError):
|
||||
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
"response_type": "code",
|
||||
"client_id": "test",
|
||||
"redirect_uri": "http://foo.bar.baz",
|
||||
"redirect_uri": "http://localhost",
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
@ -263,7 +258,7 @@ class TestAuthorize(OAuthTestCase):
|
||||
GrantTypes.IMPLICIT,
|
||||
)
|
||||
# Implicit without openid scope
|
||||
with self.assertRaises(AuthorizeError) as cm:
|
||||
with self.assertRaises(AuthorizeError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -290,7 +285,7 @@ class TestAuthorize(OAuthTestCase):
|
||||
self.assertEqual(
|
||||
OAuthAuthorizationParams.from_request(request).grant_type, GrantTypes.HYBRID
|
||||
)
|
||||
with self.assertRaises(AuthorizeError) as cm:
|
||||
with self.assertRaises(AuthorizeError):
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
@ -300,7 +295,6 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.error, "unsupported_response_type")
|
||||
|
||||
def test_full_code(self):
|
||||
"""Test full authorization"""
|
||||
@ -393,8 +387,7 @@ class TestAuthorize(OAuthTestCase):
|
||||
self.assertEqual(
|
||||
response.url,
|
||||
(
|
||||
f"http://localhost#access_token={token.token}"
|
||||
f"&id_token={provider.encode(token.id_token.to_dict())}"
|
||||
f"http://localhost#id_token={provider.encode(token.id_token.to_dict())}"
|
||||
f"&token_type={TOKEN_TYPE}"
|
||||
f"&expires_in={int(expires)}&state={state}"
|
||||
),
|
||||
@ -569,7 +562,6 @@ class TestAuthorize(OAuthTestCase):
|
||||
"url": "http://localhost",
|
||||
"title": f"Redirecting to {app.name}...",
|
||||
"attrs": {
|
||||
"access_token": token.token,
|
||||
"id_token": provider.encode(token.id_token.to_dict()),
|
||||
"token_type": TOKEN_TYPE,
|
||||
"expires_in": "3600",
|
||||
@ -621,54 +613,3 @@ class TestAuthorize(OAuthTestCase):
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_openid_missing_invalid(self):
|
||||
"""test request requiring an OpenID scope to be set"""
|
||||
OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],
|
||||
)
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
"response_type": "id_token",
|
||||
"client_id": "test",
|
||||
"redirect_uri": "http://localhost",
|
||||
"scope": "",
|
||||
},
|
||||
)
|
||||
with self.assertRaises(AuthorizeError) as cm:
|
||||
OAuthAuthorizationParams.from_request(request)
|
||||
self.assertEqual(cm.exception.cause, "scope_openid_missing")
|
||||
|
||||
@apply_blueprint("system/providers-oauth2.yaml")
|
||||
def test_offline_access_invalid(self):
|
||||
"""test request for offline_access with invalid response type"""
|
||||
provider = OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris=[RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost")],
|
||||
)
|
||||
provider.property_mappings.set(
|
||||
ScopeMapping.objects.filter(
|
||||
managed__in=[
|
||||
"goauthentik.io/providers/oauth2/scope-openid",
|
||||
"goauthentik.io/providers/oauth2/scope-offline_access",
|
||||
]
|
||||
)
|
||||
)
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
data={
|
||||
"response_type": "id_token",
|
||||
"client_id": "test",
|
||||
"redirect_uri": "http://localhost",
|
||||
"scope": f"{SCOPE_OPENID} {SCOPE_OFFLINE_ACCESS}",
|
||||
"nonce": generate_id(),
|
||||
},
|
||||
)
|
||||
parsed = OAuthAuthorizationParams.from_request(request)
|
||||
self.assertNotIn(SCOPE_OFFLINE_ACCESS, parsed.scope)
|
||||
|
@ -150,12 +150,12 @@ class OAuthAuthorizationParams:
|
||||
self.check_redirect_uri()
|
||||
self.check_grant()
|
||||
self.check_scope(github_compat)
|
||||
self.check_nonce()
|
||||
self.check_code_challenge()
|
||||
if self.request:
|
||||
raise AuthorizeError(
|
||||
self.redirect_uri, "request_not_supported", self.grant_type, self.state
|
||||
)
|
||||
self.check_nonce()
|
||||
self.check_code_challenge()
|
||||
|
||||
def check_grant(self):
|
||||
"""Check grant"""
|
||||
@ -190,7 +190,7 @@ class OAuthAuthorizationParams:
|
||||
allowed_redirect_urls = self.provider.redirect_uris
|
||||
if not self.redirect_uri:
|
||||
LOGGER.warning("Missing redirect uri.")
|
||||
raise RedirectUriError("", allowed_redirect_urls).with_cause("redirect_uri_missing")
|
||||
raise RedirectUriError("", allowed_redirect_urls)
|
||||
|
||||
if len(allowed_redirect_urls) < 1:
|
||||
LOGGER.info("Setting redirect for blank redirect_uris", redirect=self.redirect_uri)
|
||||
@ -219,14 +219,10 @@ class OAuthAuthorizationParams:
|
||||
provider=self.provider,
|
||||
)
|
||||
if not match_found:
|
||||
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls).with_cause(
|
||||
"redirect_uri_no_match"
|
||||
)
|
||||
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
|
||||
# Check against forbidden schemes
|
||||
if urlparse(self.redirect_uri).scheme in FORBIDDEN_URI_SCHEMES:
|
||||
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls).with_cause(
|
||||
"redirect_uri_forbidden_scheme"
|
||||
)
|
||||
raise RedirectUriError(self.redirect_uri, allowed_redirect_urls)
|
||||
|
||||
def check_scope(self, github_compat=False):
|
||||
"""Ensure openid scope is set in Hybrid flows, or when requesting an id_token"""
|
||||
@ -255,9 +251,7 @@ class OAuthAuthorizationParams:
|
||||
or self.response_type in [ResponseTypes.ID_TOKEN, ResponseTypes.ID_TOKEN_TOKEN]
|
||||
):
|
||||
LOGGER.warning("Missing 'openid' scope.")
|
||||
raise AuthorizeError(
|
||||
self.redirect_uri, "invalid_scope", self.grant_type, self.state
|
||||
).with_cause("scope_openid_missing")
|
||||
raise AuthorizeError(self.redirect_uri, "invalid_scope", self.grant_type, self.state)
|
||||
if SCOPE_OFFLINE_ACCESS in self.scope:
|
||||
# https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
|
||||
# Don't explicitly request consent with offline_access, as the spec allows for
|
||||
@ -292,9 +286,7 @@ class OAuthAuthorizationParams:
|
||||
return
|
||||
if not self.nonce:
|
||||
LOGGER.warning("Missing nonce for OpenID Request")
|
||||
raise AuthorizeError(
|
||||
self.redirect_uri, "invalid_request", self.grant_type, self.state
|
||||
).with_cause("none_missing")
|
||||
raise AuthorizeError(self.redirect_uri, "invalid_request", self.grant_type, self.state)
|
||||
|
||||
def check_code_challenge(self):
|
||||
"""PKCE validation of the transformation method."""
|
||||
@ -353,10 +345,10 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
||||
self.request, github_compat=self.github_compat
|
||||
)
|
||||
except AuthorizeError as error:
|
||||
LOGGER.warning(error.description, redirect_uri=error.redirect_uri, cause=error.cause)
|
||||
LOGGER.warning(error.description, redirect_uri=error.redirect_uri)
|
||||
raise RequestValidationError(error.get_response(self.request)) from None
|
||||
except OAuth2Error as error:
|
||||
LOGGER.warning(error.description, cause=error.cause)
|
||||
LOGGER.warning(error.description)
|
||||
raise RequestValidationError(
|
||||
bad_request_message(self.request, error.description, title=error.error)
|
||||
) from None
|
||||
@ -638,7 +630,6 @@ class OAuthFulfillmentStage(StageView):
|
||||
if self.params.response_type in [
|
||||
ResponseTypes.ID_TOKEN_TOKEN,
|
||||
ResponseTypes.CODE_ID_TOKEN_TOKEN,
|
||||
ResponseTypes.ID_TOKEN,
|
||||
ResponseTypes.CODE_TOKEN,
|
||||
]:
|
||||
query_fragment["access_token"] = token.token
|
||||
|
@ -190,6 +190,7 @@ class SAMLProviderSerializer(ProviderSerializer):
|
||||
"sign_response",
|
||||
"sp_binding",
|
||||
"default_relay_state",
|
||||
"default_name_id_policy",
|
||||
"url_download_metadata",
|
||||
"url_sso_post",
|
||||
"url_sso_redirect",
|
||||
|
@ -0,0 +1,31 @@
|
||||
# Generated by Django 5.1.11 on 2025-06-18 09:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_saml", "0018_alter_samlprovider_acs_url"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="samlprovider",
|
||||
name="default_name_id_policy",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "Email"),
|
||||
("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "Persistent"),
|
||||
("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", "X509"),
|
||||
(
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
|
||||
"Windows",
|
||||
),
|
||||
("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", "Transient"),
|
||||
("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "Unspecified"),
|
||||
],
|
||||
default="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
|
||||
),
|
||||
),
|
||||
]
|
@ -12,6 +12,7 @@ from authentik.core.models import PropertyMapping, Provider
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.lib.models import DomainlessURLValidator
|
||||
from authentik.lib.utils.time import timedelta_string_validator
|
||||
from authentik.sources.saml.models import SAMLNameIDPolicy
|
||||
from authentik.sources.saml.processors.constants import (
|
||||
DSA_SHA1,
|
||||
ECDSA_SHA1,
|
||||
@ -179,6 +180,9 @@ class SAMLProvider(Provider):
|
||||
default_relay_state = models.TextField(
|
||||
default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins")
|
||||
)
|
||||
default_name_id_policy = models.TextField(
|
||||
choices=SAMLNameIDPolicy.choices, default=SAMLNameIDPolicy.UNSPECIFIED
|
||||
)
|
||||
|
||||
sign_assertion = models.BooleanField(default=True)
|
||||
sign_response = models.BooleanField(default=False)
|
||||
|
@ -205,6 +205,13 @@ class AssertionProcessor:
|
||||
def get_name_id(self) -> Element:
|
||||
"""Get NameID Element"""
|
||||
name_id = Element(f"{{{NS_SAML_ASSERTION}}}NameID")
|
||||
# For requests that don't specify a NameIDPolicy, check if we
|
||||
# can fall back to the provider default
|
||||
if (
|
||||
self.auth_n_request.name_id_policy == SAML_NAME_ID_FORMAT_UNSPECIFIED
|
||||
and self.provider.default_name_id_policy != SAML_NAME_ID_FORMAT_UNSPECIFIED
|
||||
):
|
||||
self.auth_n_request.name_id_policy = self.provider.default_name_id_policy
|
||||
name_id.attrib["Format"] = self.auth_n_request.name_id_policy
|
||||
# persistent is used as a fallback, so always generate it
|
||||
persistent = self.http_request.user.uid
|
||||
|
@ -13,6 +13,7 @@ from authentik.lib.xml import lxml_from_string
|
||||
from authentik.providers.saml.exceptions import CannotHandleAssertion
|
||||
from authentik.providers.saml.models import SAMLProvider
|
||||
from authentik.providers.saml.utils.encoding import decode_base64_and_inflate
|
||||
from authentik.sources.saml.models import SAMLNameIDPolicy
|
||||
from authentik.sources.saml.processors.constants import (
|
||||
DSA_SHA1,
|
||||
NS_MAP,
|
||||
@ -175,7 +176,9 @@ class AuthNRequestParser:
|
||||
|
||||
def idp_initiated(self) -> AuthNRequest:
|
||||
"""Create IdP Initiated AuthNRequest"""
|
||||
relay_state = None
|
||||
request = AuthNRequest(relay_state=None)
|
||||
if self.provider.default_relay_state != "":
|
||||
relay_state = self.provider.default_relay_state
|
||||
return AuthNRequest(relay_state=relay_state)
|
||||
request.relay_state = self.provider.default_relay_state
|
||||
if self.provider.default_name_id_policy != SAMLNameIDPolicy.UNSPECIFIED:
|
||||
request.name_id_policy = self.provider.default_name_id_policy
|
||||
return request
|
||||
|
@ -13,6 +13,7 @@ from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
|
||||
from authentik.providers.saml.utils.encoding import PEM_FOOTER, PEM_HEADER
|
||||
from authentik.sources.saml.models import SAMLNameIDPolicy
|
||||
from authentik.sources.saml.processors.constants import (
|
||||
NS_MAP,
|
||||
NS_SAML_METADATA,
|
||||
@ -46,6 +47,7 @@ class ServiceProviderMetadata:
|
||||
|
||||
auth_n_request_signed: bool
|
||||
assertion_signed: bool
|
||||
name_id_policy: SAMLNameIDPolicy
|
||||
|
||||
signing_keypair: CertificateKeyPair | None = None
|
||||
|
||||
@ -60,6 +62,7 @@ class ServiceProviderMetadata:
|
||||
provider.issuer = self.entity_id
|
||||
provider.sp_binding = self.acs_binding
|
||||
provider.acs_url = self.acs_location
|
||||
provider.default_name_id_policy = self.name_id_policy
|
||||
if self.signing_keypair and self.auth_n_request_signed:
|
||||
self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate"
|
||||
self.signing_keypair.save()
|
||||
@ -148,6 +151,11 @@ class ServiceProviderMetadataParser:
|
||||
if signing_keypair:
|
||||
self.check_signature(root, signing_keypair)
|
||||
|
||||
name_id_format = descriptor.findall(f"{{{NS_SAML_METADATA}}}NameIDFormat")
|
||||
name_id_policy = SAMLNameIDPolicy.UNSPECIFIED
|
||||
if len(name_id_format) > 0:
|
||||
name_id_policy = SAMLNameIDPolicy(name_id_format[0].text)
|
||||
|
||||
return ServiceProviderMetadata(
|
||||
entity_id=entity_id,
|
||||
acs_binding=acs_binding,
|
||||
@ -155,4 +163,5 @@ class ServiceProviderMetadataParser:
|
||||
auth_n_request_signed=auth_n_request_signed,
|
||||
assertion_signed=assertion_signed,
|
||||
signing_keypair=signing_keypair,
|
||||
name_id_policy=name_id_policy,
|
||||
)
|
||||
|
@ -4,7 +4,7 @@
|
||||
cacheDuration="PT604800S"
|
||||
entityID="http://localhost:8080/saml/metadata">
|
||||
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
|
||||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
|
||||
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="http://localhost:8080/saml/acs"
|
||||
index="1" />
|
||||
|
@ -14,6 +14,7 @@ from authentik.lib.xml import lxml_from_string
|
||||
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
|
||||
from authentik.providers.saml.processors.metadata import MetadataProcessor
|
||||
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
|
||||
from authentik.sources.saml.models import SAMLNameIDPolicy
|
||||
from authentik.sources.saml.processors.constants import ECDSA_SHA256, NS_MAP, NS_SAML_METADATA
|
||||
|
||||
|
||||
@ -86,6 +87,7 @@ class TestServiceProviderMetadataParser(TestCase):
|
||||
self.assertEqual(provider.acs_url, "http://localhost:8080/saml/acs")
|
||||
self.assertEqual(provider.issuer, "http://localhost:8080/saml/metadata")
|
||||
self.assertEqual(provider.sp_binding, SAMLBindings.POST)
|
||||
self.assertEqual(provider.default_name_id_policy, SAMLNameIDPolicy.EMAIL)
|
||||
self.assertEqual(
|
||||
len(provider.property_mappings.all()),
|
||||
len(SAMLPropertyMapping.objects.exclude(managed__isnull=True)),
|
||||
|
@ -166,6 +166,7 @@ SPECTACULAR_SETTINGS = {
|
||||
"UserVerificationEnum": "authentik.stages.authenticator_webauthn.models.UserVerification",
|
||||
"UserTypeEnum": "authentik.core.models.UserTypes",
|
||||
"OutgoingSyncDeleteAction": "authentik.lib.sync.outgoing.models.OutgoingSyncDeleteAction",
|
||||
"SAMLNameIDPolicyEnum": "authentik.sources.saml.models.SAMLNameIDPolicy",
|
||||
},
|
||||
"ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False,
|
||||
"ENUM_GENERATE_CHOICE_DESCRIPTION": False,
|
||||
|
@ -0,0 +1,32 @@
|
||||
# Generated by Django 5.1.11 on 2025-06-18 09:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_sources_saml", "0019_migrate_usersamlsourceconnection_identifier"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="samlsource",
|
||||
name="name_id_policy",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "Email"),
|
||||
("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "Persistent"),
|
||||
("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", "X509"),
|
||||
(
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
|
||||
"Windows",
|
||||
),
|
||||
("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", "Transient"),
|
||||
("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "Unspecified"),
|
||||
],
|
||||
default="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||
help_text="NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent.",
|
||||
),
|
||||
),
|
||||
]
|
@ -39,6 +39,7 @@ from authentik.sources.saml.processors.constants import (
|
||||
SAML_NAME_ID_FORMAT_EMAIL,
|
||||
SAML_NAME_ID_FORMAT_PERSISTENT,
|
||||
SAML_NAME_ID_FORMAT_TRANSIENT,
|
||||
SAML_NAME_ID_FORMAT_UNSPECIFIED,
|
||||
SAML_NAME_ID_FORMAT_WINDOWS,
|
||||
SAML_NAME_ID_FORMAT_X509,
|
||||
SHA1,
|
||||
@ -73,6 +74,7 @@ class SAMLNameIDPolicy(models.TextChoices):
|
||||
X509 = SAML_NAME_ID_FORMAT_X509
|
||||
WINDOWS = SAML_NAME_ID_FORMAT_WINDOWS
|
||||
TRANSIENT = SAML_NAME_ID_FORMAT_TRANSIENT
|
||||
UNSPECIFIED = SAML_NAME_ID_FORMAT_UNSPECIFIED
|
||||
|
||||
|
||||
class SAMLSource(Source):
|
||||
|
@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2025.6.1 Blueprint schema",
|
||||
"title": "authentik 2025.6.2 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
@ -9233,6 +9233,18 @@
|
||||
"type": "string",
|
||||
"title": "Default relay state",
|
||||
"description": "Default relay_state value for IDP-initiated logins"
|
||||
},
|
||||
"default_name_id_policy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
],
|
||||
"title": "Default name id policy"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@ -11655,7 +11667,8 @@
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
],
|
||||
"title": "Name id policy",
|
||||
"description": "NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent."
|
||||
|
@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.2}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -55,7 +55,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.2}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
4
go.mod
4
go.mod
@ -18,7 +18,7 @@ require (
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/grafana/pyroscope-go v1.2.2
|
||||
github.com/jellydator/ttlcache/v3 v3.3.0
|
||||
github.com/jellydator/ttlcache/v3 v3.4.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
@ -29,7 +29,7 @@ require (
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2025061.2
|
||||
goauthentik.io/api/v3 v3.2025062.1
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sync v0.15.0
|
||||
|
8
go.sum
8
go.sum
@ -203,8 +203,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
|
||||
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
|
||||
github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
|
||||
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
@ -298,8 +298,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2025061.2 h1:bKmrl82Gz6J8lz3f+QIH9g+MEkl3MvkMXF34GktesA0=
|
||||
goauthentik.io/api/v3 v3.2025061.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2025062.1 h1:spvILDpDDWJNO3pM6QGqmryx6NvSchr1E8H60J/XUCA=
|
||||
goauthentik.io/api/v3 v3.2025062.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
@ -33,4 +33,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2025.6.1"
|
||||
const VERSION = "2025.6.2"
|
||||
|
@ -26,7 +26,7 @@ Parameters:
|
||||
Description: authentik Docker image
|
||||
AuthentikVersion:
|
||||
Type: String
|
||||
Default: 2025.6.1
|
||||
Default: 2025.6.2
|
||||
Description: authentik Docker image tag
|
||||
AuthentikServerCPU:
|
||||
Type: Number
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.6.1",
|
||||
"version": "2025.6.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.6.1",
|
||||
"version": "2025.6.2",
|
||||
"devDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.3.3",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.6.1",
|
||||
"version": "2025.6.2",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
|
182
packages/eslint-config/package-lock.json
generated
182
packages/eslint-config/package-lock.json
generated
@ -216,9 +216,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array": {
|
||||
"version": "0.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
|
||||
"integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
|
||||
"version": "0.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz",
|
||||
"integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/object-schema": "^2.1.6",
|
||||
@ -274,9 +274,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz",
|
||||
"integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==",
|
||||
"version": "9.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz",
|
||||
"integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -576,17 +576,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz",
|
||||
"integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz",
|
||||
"integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.0",
|
||||
"@typescript-eslint/type-utils": "8.34.0",
|
||||
"@typescript-eslint/utils": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.1",
|
||||
"@typescript-eslint/type-utils": "8.34.1",
|
||||
"@typescript-eslint/utils": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -600,7 +600,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.34.0",
|
||||
"@typescript-eslint/parser": "^8.34.1",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
@ -616,16 +616,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz",
|
||||
"integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz",
|
||||
"integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.34.0",
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/typescript-estree": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.1",
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/typescript-estree": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -641,14 +641,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz",
|
||||
"integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz",
|
||||
"integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.34.0",
|
||||
"@typescript-eslint/types": "^8.34.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.34.1",
|
||||
"@typescript-eslint/types": "^8.34.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -663,14 +663,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz",
|
||||
"integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz",
|
||||
"integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0"
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -681,9 +681,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz",
|
||||
"integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz",
|
||||
"integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -698,14 +698,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz",
|
||||
"integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz",
|
||||
"integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.34.0",
|
||||
"@typescript-eslint/utils": "8.34.0",
|
||||
"@typescript-eslint/typescript-estree": "8.34.1",
|
||||
"@typescript-eslint/utils": "8.34.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@ -722,9 +722,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz",
|
||||
"integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz",
|
||||
"integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -736,16 +736,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz",
|
||||
"integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz",
|
||||
"integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.34.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.34.0",
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0",
|
||||
"@typescript-eslint/project-service": "8.34.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.34.1",
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -765,9 +765,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -804,16 +804,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz",
|
||||
"integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz",
|
||||
"integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.0",
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/typescript-estree": "8.34.0"
|
||||
"@typescript-eslint/scope-manager": "8.34.1",
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/typescript-estree": "8.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -828,14 +828,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz",
|
||||
"integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz",
|
||||
"integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -846,9 +846,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -1554,18 +1554,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.28.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz",
|
||||
"integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==",
|
||||
"version": "9.29.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz",
|
||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.20.0",
|
||||
"@eslint/config-array": "^0.20.1",
|
||||
"@eslint/config-helpers": "^0.2.1",
|
||||
"@eslint/core": "^0.14.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.28.0",
|
||||
"@eslint/js": "9.29.0",
|
||||
"@eslint/plugin-kit": "^0.3.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@ -1577,9 +1577,9 @@
|
||||
"cross-spawn": "^7.0.6",
|
||||
"debug": "^4.3.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^8.3.0",
|
||||
"eslint-visitor-keys": "^4.2.0",
|
||||
"espree": "^10.3.0",
|
||||
"eslint-scope": "^8.4.0",
|
||||
"eslint-visitor-keys": "^4.2.1",
|
||||
"espree": "^10.4.0",
|
||||
"esquery": "^1.5.0",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@ -1792,9 +1792,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
|
||||
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
|
||||
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
@ -1808,9 +1808,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-visitor-keys": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
|
||||
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -1820,14 +1820,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
|
||||
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
|
||||
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
"acorn": "^8.15.0",
|
||||
"acorn-jsx": "^5.3.2",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -4035,15 +4035,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz",
|
||||
"integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.1.tgz",
|
||||
"integrity": "sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.34.0",
|
||||
"@typescript-eslint/parser": "8.34.0",
|
||||
"@typescript-eslint/utils": "8.34.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.34.1",
|
||||
"@typescript-eslint/parser": "8.34.1",
|
||||
"@typescript-eslint/utils": "8.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "authentik"
|
||||
version = "2025.6.1"
|
||||
version = "2025.6.2"
|
||||
description = ""
|
||||
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
|
||||
requires-python = "==3.13.*"
|
||||
@ -48,7 +48,7 @@ dependencies = [
|
||||
"packaging==25.0",
|
||||
"paramiko==3.5.1",
|
||||
"psycopg[c,pool]==3.2.9",
|
||||
"pydantic==2.11.5",
|
||||
"pydantic==2.11.7",
|
||||
"pydantic-scim==0.0.8",
|
||||
"pyjwt==2.10.1",
|
||||
"pyrad==2.4",
|
||||
@ -68,7 +68,7 @@ dependencies = [
|
||||
"urllib3<3",
|
||||
"uvicorn[standard]==0.34.3",
|
||||
"watchdog==6.0.0",
|
||||
"webauthn==2.5.2",
|
||||
"webauthn==2.6.0",
|
||||
"wsproto==1.2.0",
|
||||
"xmlsec==1.3.15",
|
||||
"zxcvbn==4.5.0",
|
||||
|
43
schema.yml
43
schema.yml
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2025.6.1
|
||||
version: 2025.6.2
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
@ -22454,6 +22454,17 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: default_name_id_policy
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
|
||||
- in: query
|
||||
name: default_relay_state
|
||||
schema:
|
||||
@ -29670,6 +29681,7 @@ paths:
|
||||
enum:
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
|
||||
@ -48745,14 +48757,6 @@ components:
|
||||
- mode
|
||||
- name
|
||||
- user_attribute
|
||||
NameIdPolicyEnum:
|
||||
enum:
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
|
||||
type: string
|
||||
NetworkBindingEnum:
|
||||
enum:
|
||||
- no_binding
|
||||
@ -54501,6 +54505,8 @@ components:
|
||||
default_relay_state:
|
||||
type: string
|
||||
description: Default relay_state value for IDP-initiated logins
|
||||
default_name_id_policy:
|
||||
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
|
||||
PatchedSAMLSourcePropertyMappingRequest:
|
||||
type: object
|
||||
description: SAMLSourcePropertyMapping Serializer
|
||||
@ -54594,7 +54600,7 @@ components:
|
||||
be a security risk, as no validation of the request ID is done.
|
||||
name_id_policy:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/NameIdPolicyEnum'
|
||||
- $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
|
||||
description: NameID Policy sent to the IdP. Can be unset, in which case
|
||||
no Policy is sent.
|
||||
binding_type:
|
||||
@ -57305,6 +57311,15 @@ components:
|
||||
required:
|
||||
- download_url
|
||||
- metadata
|
||||
SAMLNameIDPolicyEnum:
|
||||
enum:
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
||||
type: string
|
||||
SAMLPropertyMapping:
|
||||
type: object
|
||||
description: SAMLPropertyMapping Serializer
|
||||
@ -57522,6 +57537,8 @@ components:
|
||||
default_relay_state:
|
||||
type: string
|
||||
description: Default relay_state value for IDP-initiated logins
|
||||
default_name_id_policy:
|
||||
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
|
||||
url_download_metadata:
|
||||
type: string
|
||||
description: Get metadata download URL
|
||||
@ -57694,6 +57711,8 @@ components:
|
||||
default_relay_state:
|
||||
type: string
|
||||
description: Default relay_state value for IDP-initiated logins
|
||||
default_name_id_policy:
|
||||
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
|
||||
required:
|
||||
- acs_url
|
||||
- authorization_flow
|
||||
@ -57802,7 +57821,7 @@ components:
|
||||
be a security risk, as no validation of the request ID is done.
|
||||
name_id_policy:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/NameIdPolicyEnum'
|
||||
- $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
|
||||
description: NameID Policy sent to the IdP. Can be unset, in which case
|
||||
no Policy is sent.
|
||||
binding_type:
|
||||
@ -57992,7 +58011,7 @@ components:
|
||||
be a security risk, as no validation of the request ID is done.
|
||||
name_id_policy:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/NameIdPolicyEnum'
|
||||
- $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
|
||||
description: NameID Policy sent to the IdP. Can be unset, in which case
|
||||
no Policy is sent.
|
||||
binding_type:
|
||||
|
@ -7,7 +7,7 @@ services:
|
||||
network_mode: host
|
||||
restart: always
|
||||
mailpit:
|
||||
image: docker.io/axllent/mailpit:v1.26.0
|
||||
image: docker.io/axllent/mailpit:v1.26.1
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
|
18
uv.lock
generated
18
uv.lock
generated
@ -165,7 +165,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "authentik"
|
||||
version = "2025.6.1"
|
||||
version = "2025.6.2"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "argon2-cffi" },
|
||||
@ -309,7 +309,7 @@ requires-dist = [
|
||||
{ name = "packaging", specifier = "==25.0" },
|
||||
{ name = "paramiko", specifier = "==3.5.1" },
|
||||
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" },
|
||||
{ name = "pydantic", specifier = "==2.11.5" },
|
||||
{ name = "pydantic", specifier = "==2.11.7" },
|
||||
{ name = "pydantic-scim", specifier = "==0.0.8" },
|
||||
{ name = "pyjwt", specifier = "==2.10.1" },
|
||||
{ name = "pyrad", specifier = "==2.4" },
|
||||
@ -329,7 +329,7 @@ requires-dist = [
|
||||
{ name = "urllib3", specifier = "<3" },
|
||||
{ name = "uvicorn", extras = ["standard"], specifier = "==0.34.3" },
|
||||
{ name = "watchdog", specifier = "==6.0.0" },
|
||||
{ name = "webauthn", specifier = "==2.5.2" },
|
||||
{ name = "webauthn", specifier = "==2.6.0" },
|
||||
{ name = "wsproto", specifier = "==1.2.0" },
|
||||
{ name = "xmlsec", specifier = "==1.3.15" },
|
||||
{ name = "zxcvbn", specifier = "==4.5.0" },
|
||||
@ -2463,7 +2463,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.5"
|
||||
version = "2.11.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
@ -2471,9 +2471,9 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@ -3391,7 +3391,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "webauthn"
|
||||
version = "2.5.2"
|
||||
version = "2.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asn1crypto" },
|
||||
@ -3399,9 +3399,9 @@ dependencies = [
|
||||
{ name = "cryptography" },
|
||||
{ name = "pyopenssl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8d/92/8d2a4eec83d8e7feacdaad37c6eb6eb922100cecce5c14a41d8069a59a03/webauthn-2.5.2.tar.gz", hash = "sha256:09c13dfc1c68c810f32fa4d89b1d37acb9f9ae9091c9d7019e313be4525a95ef", size = 124114, upload-time = "2025-03-07T19:44:05.243Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/63/38/5792cb2034673c162a721df0ad65825699516ee0c938a65670ad3cdabf6c/webauthn-2.6.0.tar.gz", hash = "sha256:13cf5b009a64cef569599ffecf24550df1d7c0cd4fbaea870f937148484a80b4", size = 123608, upload-time = "2025-06-16T22:25:26.76Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/fe/f6ae41de9f383439e30b303a67f6f45d2fceabedaedc34c62f74d58c5c73/webauthn-2.5.2-py3-none-any.whl", hash = "sha256:44246e496e617eb5e2f51165046b9f0197fcdf470f69cd6734061a27ba365f8e", size = 71624, upload-time = "2025-03-07T19:44:03.728Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/c5/b1bba7f6a50caca77f37003e098f48f8dc68d990aba8a03ac8376016430b/webauthn-2.6.0-py3-none-any.whl", hash = "sha256:459973eb5780c1f41bec42b682acf587456b185733398a0b99a0714705b79447", size = 71189, upload-time = "2025-06-16T22:25:25.535Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
1703
web/package-lock.json
generated
1703
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -93,7 +93,7 @@
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.7.11",
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"@goauthentik/api": "^2025.6.1-1749515784",
|
||||
"@goauthentik/api": "^2025.6.2-1750112513",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
@ -102,10 +102,9 @@
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^4.1.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^9.28.1",
|
||||
"@sentry/browser": "^9.30.0",
|
||||
"@spotlightjs/spotlight": "^3.0.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.4.9",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
@ -137,6 +136,7 @@
|
||||
"trusted-types": "^2.0.0",
|
||||
"ts-pattern": "^5.7.1",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"webauthn-polyfills": "^0.1.7",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
"yaml": "^2.8.0"
|
||||
},
|
||||
@ -170,16 +170,16 @@
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"@typescript-eslint/eslint-plugin": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^8.8.0",
|
||||
"@wdio/browser-runner": "9.4",
|
||||
"@wdio/cli": "9.4",
|
||||
"@wdio/spec-reporter": "^9.1.2",
|
||||
"@wdio/browser-runner": "9.15",
|
||||
"@wdio/cli": "9.15",
|
||||
"@wdio/spec-reporter": "^9.15.0",
|
||||
"@web/test-runner": "^0.20.2",
|
||||
"chromedriver": "^136.0.3",
|
||||
"esbuild": "^0.25.5",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint": "^9.29.0",
|
||||
"eslint-plugin-lit": "^2.1.1",
|
||||
"eslint-plugin-wc": "^3.0.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
@ -194,7 +194,7 @@
|
||||
"storybook-addon-mock": "^5.0.0",
|
||||
"turnstile-types": "^1.2.3",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.34.0",
|
||||
"typescript-eslint": "^8.34.1",
|
||||
"vite-plugin-lit-css": "^2.0.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"wireit": "^0.14.12"
|
||||
|
2
web/packages/core/types/node.d.ts
vendored
2
web/packages/core/types/node.d.ts
vendored
@ -14,7 +14,7 @@ declare module "module" {
|
||||
* const relativeDirname = dirname(fileURLToPath(import.meta.url));
|
||||
* ```
|
||||
*/
|
||||
// eslint-disable-next-line no-var
|
||||
|
||||
var __dirname: string;
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@goauthentik/api": "^2024.6.0-1719577139",
|
||||
"base64-js": "^1.5.1",
|
||||
"bootstrap": "^4.6.1",
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"jquery": "^3.7.1",
|
||||
"weakmap-polyfill": "^2.0.4"
|
||||
"weakmap-polyfill": "^2.0.4",
|
||||
"webauthn-polyfills": "^0.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { fromByteArray } from "base64-js";
|
||||
import "formdata-polyfill";
|
||||
import $ from "jquery";
|
||||
import "weakmap-polyfill";
|
||||
import "webauthn-polyfills";
|
||||
|
||||
import {
|
||||
type AuthenticatorValidationChallenge,
|
||||
@ -257,47 +257,9 @@ class AutosubmitStage extends Stage<AutosubmitChallenge> {
|
||||
}
|
||||
}
|
||||
|
||||
export interface Assertion {
|
||||
id: string;
|
||||
rawId: string;
|
||||
type: string;
|
||||
registrationClientExtensions: string;
|
||||
response: {
|
||||
clientDataJSON: string;
|
||||
attestationObject: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AuthAssertion {
|
||||
id: string;
|
||||
rawId: string;
|
||||
type: string;
|
||||
assertionClientExtensions: string;
|
||||
response: {
|
||||
clientDataJSON: string;
|
||||
authenticatorData: string;
|
||||
signature: string;
|
||||
userHandle: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> {
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
|
||||
b64enc(buf: Uint8Array): string {
|
||||
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||
}
|
||||
|
||||
b64RawEnc(buf: Uint8Array): string {
|
||||
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
u8arr(input: string): Uint8Array {
|
||||
return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) =>
|
||||
c.charCodeAt(0),
|
||||
);
|
||||
}
|
||||
|
||||
checkWebAuthnSupport(): boolean {
|
||||
if ("credentials" in navigator) {
|
||||
return true;
|
||||
@ -310,98 +272,6 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms items in the credentialCreateOptions generated on the server
|
||||
* into byte arrays expected by the navigator.credentials.create() call
|
||||
*/
|
||||
transformCredentialCreateOptions(
|
||||
credentialCreateOptions: PublicKeyCredentialCreationOptions,
|
||||
userId: string,
|
||||
): PublicKeyCredentialCreationOptions {
|
||||
const user = credentialCreateOptions.user;
|
||||
// Because json can't contain raw bytes, the server base64-encodes the User ID
|
||||
// So to get the base64 encoded byte array, we first need to convert it to a regular
|
||||
// string, then a byte array, re-encode it and wrap that in an array.
|
||||
const stringId = decodeURIComponent(window.atob(userId));
|
||||
user.id = this.u8arr(this.b64enc(this.u8arr(stringId)));
|
||||
const challenge = this.u8arr(credentialCreateOptions.challenge.toString());
|
||||
|
||||
return Object.assign({}, credentialCreateOptions, {
|
||||
challenge,
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the binary data in the credential into base64 strings
|
||||
* for posting to the server.
|
||||
* @param {PublicKeyCredential} newAssertion
|
||||
*/
|
||||
transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion {
|
||||
const attObj = new Uint8Array(
|
||||
(newAssertion.response as AuthenticatorAttestationResponse).attestationObject,
|
||||
);
|
||||
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
|
||||
const rawId = new Uint8Array(newAssertion.rawId);
|
||||
|
||||
const registrationClientExtensions = newAssertion.getClientExtensionResults();
|
||||
return {
|
||||
id: newAssertion.id,
|
||||
rawId: this.b64enc(rawId),
|
||||
type: newAssertion.type,
|
||||
registrationClientExtensions: JSON.stringify(registrationClientExtensions),
|
||||
response: {
|
||||
clientDataJSON: this.b64enc(clientDataJSON),
|
||||
attestationObject: this.b64enc(attObj),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
transformCredentialRequestOptions(
|
||||
credentialRequestOptions: PublicKeyCredentialRequestOptions,
|
||||
): PublicKeyCredentialRequestOptions {
|
||||
const challenge = this.u8arr(credentialRequestOptions.challenge.toString());
|
||||
|
||||
const allowCredentials = (credentialRequestOptions.allowCredentials || []).map(
|
||||
(credentialDescriptor) => {
|
||||
const id = this.u8arr(credentialDescriptor.id.toString());
|
||||
return Object.assign({}, credentialDescriptor, { id });
|
||||
},
|
||||
);
|
||||
|
||||
return Object.assign({}, credentialRequestOptions, {
|
||||
challenge,
|
||||
allowCredentials,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the binary data in the assertion into strings for posting to the server.
|
||||
* @param {PublicKeyCredential} newAssertion
|
||||
*/
|
||||
transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion {
|
||||
const response = newAssertion.response as AuthenticatorAssertionResponse;
|
||||
const authData = new Uint8Array(response.authenticatorData);
|
||||
const clientDataJSON = new Uint8Array(response.clientDataJSON);
|
||||
const rawId = new Uint8Array(newAssertion.rawId);
|
||||
const sig = new Uint8Array(response.signature);
|
||||
const assertionClientExtensions = newAssertion.getClientExtensionResults();
|
||||
|
||||
return {
|
||||
id: newAssertion.id,
|
||||
rawId: this.b64enc(rawId),
|
||||
type: newAssertion.type,
|
||||
assertionClientExtensions: JSON.stringify(assertionClientExtensions),
|
||||
|
||||
response: {
|
||||
clientDataJSON: this.b64RawEnc(clientDataJSON),
|
||||
signature: this.b64RawEnc(sig),
|
||||
authenticatorData: this.b64RawEnc(authData),
|
||||
userHandle: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.challenge.deviceChallenges.length === 1) {
|
||||
this.deviceChallenge = this.challenge.deviceChallenges[0];
|
||||
@ -505,8 +375,8 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
`);
|
||||
navigator.credentials
|
||||
.get({
|
||||
publicKey: this.transformCredentialRequestOptions(
|
||||
this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptions,
|
||||
publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(
|
||||
this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptionsJSON,
|
||||
),
|
||||
})
|
||||
.then((assertion) => {
|
||||
@ -514,15 +384,9 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
throw new Error("No assertion");
|
||||
}
|
||||
try {
|
||||
// we now have an authentication assertion! encode the byte arrays contained
|
||||
// in the assertion data as strings for posting to the server
|
||||
const transformedAssertionForServer = this.transformAssertionForServer(
|
||||
assertion as PublicKeyCredential,
|
||||
);
|
||||
|
||||
// post the assertion to the server for verification.
|
||||
this.executor.submit({
|
||||
webauthn: transformedAssertionForServer,
|
||||
webauthn: (assertion as PublicKeyCredential).toJSON(),
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Error when validating assertion on server: ${err}`);
|
||||
|
@ -88,7 +88,8 @@ export class RecentEventsCard extends Table<Event> {
|
||||
}
|
||||
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
html`<ak-empty-state
|
||||
><span slot="header">${msg("No Events found.")}</span>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import { policyEngineModes } from "@goauthentik/admin/policies/PolicyEngineModes
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-file-input";
|
||||
import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/components/ak-slug-input";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
@ -130,14 +131,14 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
required
|
||||
help=${msg("Application's display Name.")}
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
help=${msg("Internal application name used in URLs.")}
|
||||
input-hint="code"
|
||||
></ak-text-input>
|
||||
></ak-slug-input>
|
||||
<ak-text-input
|
||||
name="group"
|
||||
value=${ifDefined(this.instance?.group)}
|
||||
|
@ -117,13 +117,11 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
?invalid=${this.errors.has("name")}
|
||||
.errorMessages=${errors.name ?? this.errorMessages("name")}
|
||||
help=${msg("Application's display Name.")}
|
||||
id="ak-application-wizard-details-name"
|
||||
></ak-text-input>
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(app.slug)}
|
||||
label=${msg("Slug")}
|
||||
source="#ak-application-wizard-details-name"
|
||||
required
|
||||
?invalid=${errors.slug ?? this.errors.has("slug")}
|
||||
.errorMessages=${this.errorMessages("slug")}
|
||||
|
@ -115,7 +115,8 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
.columns=${COLUMNS}
|
||||
.content=${[]}
|
||||
></ak-select-table>
|
||||
<ak-empty-state header=${msg("No bound policies.")} icon="pf-icon-module">
|
||||
<ak-empty-state icon="pf-icon-module"
|
||||
><span slot="header">${msg("No bound policies.")} </span>
|
||||
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
|
||||
<div slot="primary">
|
||||
<button
|
||||
|
@ -135,7 +135,8 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Stages bound")} icon="pf-icon-module">
|
||||
html`<ak-empty-state icon="pf-icon-module">
|
||||
<span slot="header">${msg("No Stages bound")}</span>
|
||||
<div slot="body">${msg("No stages are currently bound to this flow.")}</div>
|
||||
<div slot="primary">
|
||||
<ak-stage-wizard
|
||||
|
@ -3,6 +3,7 @@ import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/util
|
||||
import { policyEngineModes } from "@goauthentik/admin/policies/PolicyEngineModes";
|
||||
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-slug-input.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
@ -91,17 +92,16 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Shown as the Title in Flow pages.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">${msg("Visible in the URL.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
help=${msg("Visible in the URL.")}
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
|
||||
<ak-form-element-horizontal label=${msg("Designation")} required name="designation">
|
||||
<select class="pf-c-form-control">
|
||||
<option value="" ?selected=${this.instance?.designation === undefined}>
|
||||
|
@ -198,7 +198,8 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Policies bound.")} icon="pf-icon-module">
|
||||
html`<ak-empty-state icon="pf-icon-module"
|
||||
><span slot="header">${msg("No Policies bound.")}</span>
|
||||
<div slot="body">${msg("No policies are currently bound to this object.")}</div>
|
||||
<div slot="primary">
|
||||
<ak-policy-wizard
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
FlowsInstancesListDesignationEnum,
|
||||
PropertymappingsApi,
|
||||
PropertymappingsProviderSamlListRequest,
|
||||
SAMLNameIDPolicyEnum,
|
||||
SAMLPropertyMapping,
|
||||
SAMLProvider,
|
||||
SpBindingEnum,
|
||||
@ -316,6 +317,54 @@ export function renderForm(
|
||||
"When using IDP-initiated logins, the relay state will be set to this value.",
|
||||
)}
|
||||
></ak-text-input>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Default NameID Policy")}
|
||||
required
|
||||
name="defaultNameIdPolicy"
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
|
||||
?selected=${provider?.defaultNameIdPolicy ===
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
|
||||
>
|
||||
${msg("Persistent")}
|
||||
</option>
|
||||
<option
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
|
||||
?selected=${provider?.defaultNameIdPolicy ===
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
|
||||
>
|
||||
${msg("Email address")}
|
||||
</option>
|
||||
<option
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
|
||||
?selected=${provider?.defaultNameIdPolicy ===
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
|
||||
>
|
||||
${msg("Windows")}
|
||||
</option>
|
||||
<option
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
|
||||
?selected=${provider?.defaultNameIdPolicy ===
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
|
||||
>
|
||||
${msg("X509 Subject")}
|
||||
</option>
|
||||
<option
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
|
||||
?selected=${provider?.defaultNameIdPolicy ===
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
|
||||
>
|
||||
${msg("Transient")}
|
||||
</option>
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Configure the default NameID Policy used by IDP-initiated logins and when an incoming assertion doesn't specify a NameID Policy (also applies when using a custom NameID Mapping).",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-radio-input
|
||||
name="digestAlgorithm"
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-secret-text-input.js";
|
||||
import "@goauthentik/components/ak-secret-textarea-input.js";
|
||||
import "@goauthentik/components/ak-slug-input.js";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
@ -87,12 +88,13 @@ export class KerberosSourceForm extends WithCapabilitiesConfig(BaseSourceForm<Ke
|
||||
value=${ifDefined(this.instance?.name)}
|
||||
required
|
||||
></ak-text-input>
|
||||
<ak-text-input
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
label=${msg("Slug")}
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
></ak-text-input>
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
<ak-switch-input
|
||||
name="enabled"
|
||||
?checked=${this.instance?.enabled ?? true}
|
||||
|
@ -3,6 +3,7 @@ import { placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-secret-text-input.js";
|
||||
import "@goauthentik/components/ak-slug-input.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
@ -54,14 +55,15 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/components/ak-secret-textarea-input.js";
|
||||
import "@goauthentik/components/ak-slug-input.js";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
@ -267,16 +268,13 @@ export class OAuthSourceForm extends WithCapabilitiesConfig(BaseSourceForm<OAuth
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
|
||||
import { ascii_letters, digits, randomString } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/components/ak-slug-input.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
|
||||
@ -183,14 +184,15 @@ export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm<PlexSo
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
UserMatchingModeToLabel,
|
||||
} from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-slug-input.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
@ -25,7 +26,7 @@ import {
|
||||
DigestAlgorithmEnum,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
GroupMatchingModeEnum,
|
||||
NameIdPolicyEnum,
|
||||
SAMLNameIDPolicyEnum,
|
||||
SAMLSource,
|
||||
SignatureAlgorithmEnum,
|
||||
SourcesApi,
|
||||
@ -89,14 +90,15 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
@ -351,37 +353,37 @@ export class SAMLSourceForm extends WithCapabilitiesConfig(BaseSourceForm<SAMLSo
|
||||
>
|
||||
<select class="pf-c-form-control">
|
||||
<option
|
||||
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatPersistent}
|
||||
>
|
||||
${msg("Persistent")}
|
||||
</option>
|
||||
<option
|
||||
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatEmailAddress}
|
||||
>
|
||||
${msg("Email address")}
|
||||
</option>
|
||||
<option
|
||||
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatWindowsDomainQualifiedName}
|
||||
>
|
||||
${msg("Windows")}
|
||||
</option>
|
||||
<option
|
||||
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml11NameidFormatX509SubjectName}
|
||||
>
|
||||
${msg("X509 Subject")}
|
||||
</option>
|
||||
<option
|
||||
value=${NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
|
||||
value=${SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
|
||||
?selected=${this.instance?.nameIdPolicy ===
|
||||
NameIdPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
|
||||
SAMLNameIDPolicyEnum.UrnOasisNamesTcSaml20NameidFormatTransient}
|
||||
>
|
||||
${msg("Transient")}
|
||||
</option>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-slug-input.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
@ -48,14 +49,15 @@ export class SCIMSourceForm extends BaseSourceForm<SCIMSource> {
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Slug")} required name="slug">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.slug)}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-slug-input
|
||||
name="slug"
|
||||
value=${ifDefined(this.instance?.slug)}
|
||||
label=${msg("Slug")}
|
||||
required
|
||||
input-hint="code"
|
||||
></ak-slug-input>
|
||||
|
||||
<ak-form-element-horizontal name="enabled">
|
||||
<div class="pf-c-check">
|
||||
<input
|
||||
|
@ -41,14 +41,27 @@ export class InvitationForm extends ModelForm<Invitation, string> {
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal slugMode label=${msg("Name")} required name="name">
|
||||
const checkSlug = (ev: InputEvent) => {
|
||||
if (ev && ev.target && ev.target instanceof HTMLInputElement) {
|
||||
ev.target.value = (ev.target.value ?? "").replace(/[^a-z0-9-]/g, "");
|
||||
}
|
||||
};
|
||||
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
id="admin-stages-invitation-name"
|
||||
value="${this.instance?.name || ""}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
@input=${(ev: InputEvent) => checkSlug(ev)}
|
||||
data-ak-slug="true"
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Expires")} required name="expires">
|
||||
<input
|
||||
|
@ -1,21 +1,5 @@
|
||||
import * as base64js from "base64-js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
export function b64enc(buf: Uint8Array): string {
|
||||
return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||
}
|
||||
|
||||
export function b64RawEnc(buf: Uint8Array): string {
|
||||
return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
export function u8arr(input: string): Uint8Array {
|
||||
return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) =>
|
||||
c.charCodeAt(0),
|
||||
);
|
||||
}
|
||||
|
||||
export function checkWebAuthnSupport() {
|
||||
if ("credentials" in navigator) {
|
||||
return;
|
||||
@ -25,121 +9,3 @@ export function checkWebAuthnSupport() {
|
||||
}
|
||||
throw new Error(msg("WebAuthn not supported by browser."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms items in the credentialCreateOptions generated on the server
|
||||
* into byte arrays expected by the navigator.credentials.create() call
|
||||
*/
|
||||
export function transformCredentialCreateOptions(
|
||||
credentialCreateOptions: PublicKeyCredentialCreationOptions,
|
||||
userId: string,
|
||||
): PublicKeyCredentialCreationOptions {
|
||||
const user = credentialCreateOptions.user;
|
||||
// Because json can't contain raw bytes, the server base64-encodes the User ID
|
||||
// So to get the base64 encoded byte array, we first need to convert it to a regular
|
||||
// string, then a byte array, re-encode it and wrap that in an array.
|
||||
const stringId = decodeURIComponent(window.atob(userId));
|
||||
user.id = u8arr(b64enc(u8arr(stringId)));
|
||||
const challenge = u8arr(credentialCreateOptions.challenge.toString());
|
||||
|
||||
return {
|
||||
...credentialCreateOptions,
|
||||
challenge,
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
||||
export interface Assertion {
|
||||
id: string;
|
||||
rawId: string;
|
||||
type: string;
|
||||
registrationClientExtensions: string;
|
||||
response: {
|
||||
clientDataJSON: string;
|
||||
attestationObject: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the binary data in the credential into base64 strings
|
||||
* for posting to the server.
|
||||
* @param {PublicKeyCredential} newAssertion
|
||||
*/
|
||||
export function transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion {
|
||||
const attObj = new Uint8Array(
|
||||
(newAssertion.response as AuthenticatorAttestationResponse).attestationObject,
|
||||
);
|
||||
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
|
||||
const rawId = new Uint8Array(newAssertion.rawId);
|
||||
|
||||
const registrationClientExtensions = newAssertion.getClientExtensionResults();
|
||||
return {
|
||||
id: newAssertion.id,
|
||||
rawId: b64enc(rawId),
|
||||
type: newAssertion.type,
|
||||
registrationClientExtensions: JSON.stringify(registrationClientExtensions),
|
||||
response: {
|
||||
clientDataJSON: b64enc(clientDataJSON),
|
||||
attestationObject: b64enc(attObj),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function transformCredentialRequestOptions(
|
||||
credentialRequestOptions: PublicKeyCredentialRequestOptions,
|
||||
): PublicKeyCredentialRequestOptions {
|
||||
const challenge = u8arr(credentialRequestOptions.challenge.toString());
|
||||
|
||||
const allowCredentials = (credentialRequestOptions.allowCredentials || []).map(
|
||||
(credentialDescriptor) => {
|
||||
const id = u8arr(credentialDescriptor.id.toString());
|
||||
return Object.assign({}, credentialDescriptor, { id });
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
...credentialRequestOptions,
|
||||
challenge,
|
||||
allowCredentials,
|
||||
};
|
||||
}
|
||||
|
||||
export interface AuthAssertion {
|
||||
id: string;
|
||||
rawId: string;
|
||||
type: string;
|
||||
assertionClientExtensions: string;
|
||||
response: {
|
||||
clientDataJSON: string;
|
||||
authenticatorData: string;
|
||||
signature: string;
|
||||
userHandle: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the binary data in the assertion into strings for posting to the server.
|
||||
* @param {PublicKeyCredential} newAssertion
|
||||
*/
|
||||
export function transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion {
|
||||
const response = newAssertion.response as AuthenticatorAssertionResponse;
|
||||
const authData = new Uint8Array(response.authenticatorData);
|
||||
const clientDataJSON = new Uint8Array(response.clientDataJSON);
|
||||
const rawId = new Uint8Array(newAssertion.rawId);
|
||||
const sig = new Uint8Array(response.signature);
|
||||
const assertionClientExtensions = newAssertion.getClientExtensionResults();
|
||||
|
||||
return {
|
||||
id: newAssertion.id,
|
||||
rawId: b64enc(rawId),
|
||||
type: newAssertion.type,
|
||||
assertionClientExtensions: JSON.stringify(assertionClientExtensions),
|
||||
|
||||
response: {
|
||||
clientDataJSON: b64RawEnc(clientDataJSON),
|
||||
signature: b64RawEnc(sig),
|
||||
authenticatorData: b64RawEnc(authData),
|
||||
userHandle: null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { me } from "@goauthentik/common/users.js";
|
||||
import { isUserRoute } from "@goauthentik/elements/router/utils.js";
|
||||
import { deepmerge, deepmergeInto } from "deepmerge-ts";
|
||||
|
||||
import { UiThemeEnum, UserSelf } from "@goauthentik/api";
|
||||
import { CurrentBrand } from "@goauthentik/api";
|
||||
@ -96,13 +97,12 @@ export class DefaultUIConfig implements UIConfig {
|
||||
let globalUiConfig: Promise<UIConfig>;
|
||||
|
||||
export function getConfigForUser(user: UserSelf): UIConfig {
|
||||
const settings = user.settings;
|
||||
let config = new DefaultUIConfig();
|
||||
const settings = user.settings as UIConfig;
|
||||
const config = new DefaultUIConfig();
|
||||
if (!settings) {
|
||||
return config;
|
||||
}
|
||||
config = Object.assign(new DefaultUIConfig(), settings);
|
||||
return config;
|
||||
return deepmerge({ ...config }, settings);
|
||||
}
|
||||
|
||||
export function uiConfig(): Promise<UIConfig> {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { formatSlug } from "@goauthentik/elements/router/utils.js";
|
||||
import { bound } from "@goauthentik/elements/decorators/bound.js";
|
||||
import { kebabCase } from "change-case";
|
||||
|
||||
import { html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
@ -6,59 +7,83 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import { HorizontalLightComponent } from "./HorizontalLightComponent";
|
||||
|
||||
const slugify = (s: string) => kebabCase(s, { suffixCharacters: "-" });
|
||||
|
||||
/**
|
||||
* @element ak-slug-input
|
||||
* @class AkSlugInput
|
||||
*
|
||||
* A wrapper around `ak-form-element-horizontal` and a text input control that listens for input on
|
||||
* a peer text input control and automatically mirrors that control's value, transforming the value
|
||||
* into a slug and displaying it separately.
|
||||
*
|
||||
* If the user manually changes the slug, mirroring and transformation stop. If, after that, both
|
||||
* fields are cleared manually, mirroring and transformation resume.
|
||||
*
|
||||
* ## Limitations:
|
||||
*
|
||||
* Both the source text field and the slug field must be rendered in the same render pass (i.e.,
|
||||
* part of the same singular call to a `render` function) so that the slug field can find its
|
||||
* source.
|
||||
*
|
||||
* For the same reason, both the source text field and the slug field must share the same immediate
|
||||
* parent DOM object.
|
||||
*
|
||||
* Since we expect the source text field and the slug to be part of the same form and rendered not
|
||||
* just in the same form but in the same form group, these are not considered burdensome
|
||||
* restrictions.
|
||||
*/
|
||||
@customElement("ak-slug-input")
|
||||
export class AkSlugInput extends HorizontalLightComponent<string> {
|
||||
@property({ type: String, reflect: true })
|
||||
value = "";
|
||||
|
||||
/**
|
||||
* A selector indicating the source text input control. Must be unique within the whole DOM
|
||||
* context of the slug and source controls. The most common use in authentik is the default:
|
||||
* slugifying the "name" of something.
|
||||
*/
|
||||
@property({ type: String })
|
||||
source = "";
|
||||
public source = "[name='name']";
|
||||
|
||||
origin?: HTMLInputElement | null;
|
||||
@property({ type: String, reflect: true })
|
||||
public value = "";
|
||||
|
||||
@query("input")
|
||||
input!: HTMLInputElement;
|
||||
private input!: HTMLInputElement;
|
||||
|
||||
touched: boolean = false;
|
||||
#origin?: HTMLInputElement | null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.slugify = this.slugify.bind(this);
|
||||
this.handleTouch = this.handleTouch.bind(this);
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
this.input.addEventListener("input", this.handleTouch);
|
||||
}
|
||||
#touched: boolean = false;
|
||||
|
||||
// Do not stop propagation of this event; it must be sent up the tree so that a parent
|
||||
// component, such as a custom forms manager, may receive it.
|
||||
handleTouch(ev: Event) {
|
||||
this.input.value = formatSlug(this.input.value);
|
||||
this.value = this.input.value;
|
||||
protected handleTouch(ev: Event) {
|
||||
this.value = this.input.value = slugify(this.input.value);
|
||||
|
||||
if (this.origin && this.origin.value === "" && this.input.value === "") {
|
||||
this.touched = false;
|
||||
// Reset 'touched' status if the slug & target have been reset
|
||||
if (this.#origin && this.#origin.value === "" && this.input.value === "") {
|
||||
this.#touched = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev && ev.target && ev.target instanceof HTMLInputElement) {
|
||||
this.touched = true;
|
||||
this.#touched = true;
|
||||
}
|
||||
}
|
||||
|
||||
slugify(ev: Event) {
|
||||
@bound
|
||||
protected slugify(ev: Event) {
|
||||
if (!(ev && ev.target && ev.target instanceof HTMLInputElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset 'touched' status if the slug & target have been reset
|
||||
if (ev.target.value === "" && this.input.value === "") {
|
||||
this.touched = false;
|
||||
this.#touched = false;
|
||||
}
|
||||
|
||||
// Don't proceed if the user has hand-modified the slug
|
||||
if (this.touched) {
|
||||
// Don't proceed if the user has hand-modified the slug. (Note the order of statements: if
|
||||
// the user hand modified the slug to be empty as part of resetting the slug/source
|
||||
// relationship, that's a "not-touched" condition and falls through.)
|
||||
if (this.#touched) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -67,7 +92,7 @@ export class AkSlugInput extends HorizontalLightComponent<string> {
|
||||
// "any event which adds or removes a character but leaves the rest of the slug looking like
|
||||
// the previous iteration, set it to the current iteration."
|
||||
|
||||
const newSlug = formatSlug(ev.target.value);
|
||||
const newSlug = slugify(ev.target.value);
|
||||
const oldSlug = this.input.value;
|
||||
const [shorter, longer] =
|
||||
newSlug.length < oldSlug.length ? [newSlug, oldSlug] : [oldSlug, newSlug];
|
||||
@ -81,7 +106,6 @@ export class AkSlugInput extends HorizontalLightComponent<string> {
|
||||
// to listeners, both the name and value of the host must match those of the target
|
||||
// input. The name is already handled since it's both required and automatically
|
||||
// forwarded to our templated input, but the value must also be set.
|
||||
|
||||
this.value = this.input.value = newSlug;
|
||||
this.dispatchEvent(
|
||||
new Event("input", {
|
||||
@ -91,38 +115,36 @@ export class AkSlugInput extends HorizontalLightComponent<string> {
|
||||
);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
// Set up listener on source element, so we can slugify the content.
|
||||
setTimeout(() => {
|
||||
if (this.source) {
|
||||
const rootNode = this.getRootNode();
|
||||
if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
|
||||
this.origin = rootNode.querySelector(this.source);
|
||||
}
|
||||
if (this.origin) {
|
||||
this.origin.addEventListener("input", this.slugify);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.origin) {
|
||||
this.origin.removeEventListener("input", this.slugify);
|
||||
public override disconnectedCallback() {
|
||||
if (this.#origin) {
|
||||
this.#origin.removeEventListener("input", this.slugify);
|
||||
}
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
renderControl() {
|
||||
public override renderControl() {
|
||||
return html`<input
|
||||
@input=${(ev: Event) => this.handleTouch(ev)}
|
||||
type="text"
|
||||
value=${ifDefined(this.value)}
|
||||
class="pf-c-form-control"
|
||||
?required=${this.required}
|
||||
/>`;
|
||||
}
|
||||
|
||||
public override firstUpdated() {
|
||||
if (!this.source) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rootNode = this.getRootNode();
|
||||
if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
|
||||
this.#origin = rootNode.querySelector(this.source);
|
||||
}
|
||||
if (this.#origin) {
|
||||
this.#origin.addEventListener("input", this.slugify);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AkSlugInput;
|
||||
|
@ -94,7 +94,8 @@ export class ObjectChangelog extends Table<Event> {
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
html`<ak-empty-state
|
||||
><span slot="header">${msg("No Events found.")}</span>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
|
@ -66,7 +66,8 @@ export class UserEvents extends Table<Event> {
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No Events found.")}>
|
||||
html`<ak-empty-state
|
||||
><span slot="header">${msg("No Events found.")}</span>
|
||||
<div slot="body">${msg("No matching events could be found.")}</div>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
|
@ -3,6 +3,7 @@ import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/Spinner";
|
||||
import { type SlottedTemplateResult, type Spread } from "@goauthentik/elements/types";
|
||||
import { spread } from "@open-wc/lit-helpers";
|
||||
import { SlotController } from "@patternfly/pfe-core/controllers/slot-controller.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, html, nothing } from "lit";
|
||||
@ -33,6 +34,8 @@ export class EmptyState extends AKElement implements IEmptyState {
|
||||
@property()
|
||||
header?: string;
|
||||
|
||||
slots = new SlotController(this, "header", "body", "primary");
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
PFBase,
|
||||
@ -48,6 +51,12 @@ export class EmptyState extends AKElement implements IEmptyState {
|
||||
}
|
||||
|
||||
render() {
|
||||
const showHeader = this.loading || this.slots.hasSlotted("header");
|
||||
const header = () =>
|
||||
this.slots.hasSlotted("header")
|
||||
? html`<slot name="header"></slot>`
|
||||
: html`<span>${msg("Loading")}</span>`;
|
||||
|
||||
return html`<div class="pf-c-empty-state ${this.fullHeight && "pf-m-full-height"}">
|
||||
<div class="pf-c-empty-state__content">
|
||||
${this.loading
|
||||
@ -59,15 +68,17 @@ export class EmptyState extends AKElement implements IEmptyState {
|
||||
"fa-question-circle"} pf-c-empty-state__icon"
|
||||
aria-hidden="true"
|
||||
></i>`}
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
${this.loading && this.header === undefined ? msg("Loading") : this.header}
|
||||
</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
<div class="pf-c-empty-state__primary">
|
||||
<slot name="primary"></slot>
|
||||
</div>
|
||||
${showHeader ? html` <h1 class="pf-c-title pf-m-lg">${header()}</h1>` : nothing}
|
||||
${this.slots.hasSlotted("body")
|
||||
? html` <div class="pf-c-empty-state__body">
|
||||
<slot name="body"></slot>
|
||||
</div>`
|
||||
: nothing}
|
||||
${this.slots.hasSlotted("primary")
|
||||
? html` <div class="pf-c-empty-state__primary">
|
||||
<slot name="primary"></slot>
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
@ -200,7 +200,8 @@ export abstract class AKChart<T> extends AKElement {
|
||||
<div class="container">
|
||||
${this.error
|
||||
? html`
|
||||
<ak-empty-state header="${msg("Failed to fetch data.")}" icon="fa-times">
|
||||
<ak-empty-state icon="fa-times"
|
||||
><span slot="header">${msg("Failed to fetch data.")}</span>
|
||||
<p slot="body">${pluckErrorDetail(this.error)}</p>
|
||||
</ak-empty-state>
|
||||
`
|
||||
|
@ -40,7 +40,9 @@ export class LogViewer extends Table<LogEvent> {
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state header=${msg("No log messages.")}> </ak-empty-state>`,
|
||||
html`<ak-empty-state
|
||||
><span slot="header">${msg("No log messages.")}</span>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { PreventFormSubmit } from "@goauthentik/elements/forms/helpers";
|
||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||
import { formatSlug } from "@goauthentik/elements/router/utils.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
@ -197,39 +196,6 @@ export abstract class Form<T> extends AKElement {
|
||||
return this.successMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* After rendering the form, if there is both a `name` and `slug` element within the form,
|
||||
* events the `name` element so that the slug will always have a slugified version of the
|
||||
* `name.`. This duplicates functionality within ak-form-element-horizontal.
|
||||
*/
|
||||
updated(): void {
|
||||
this.shadowRoot
|
||||
?.querySelectorAll("ak-form-element-horizontal[name=name]")
|
||||
.forEach((nameInput) => {
|
||||
const input = nameInput.firstElementChild as HTMLInputElement;
|
||||
const form = nameInput.closest("form");
|
||||
if (form === null) {
|
||||
return;
|
||||
}
|
||||
const slugFieldWrapper = form.querySelector(
|
||||
"ak-form-element-horizontal[name=slug]",
|
||||
);
|
||||
if (!slugFieldWrapper) {
|
||||
return;
|
||||
}
|
||||
const slugField = slugFieldWrapper.firstElementChild as HTMLInputElement;
|
||||
// Only attach handler if the slug is already equal to the name
|
||||
// if not, they are probably completely different and shouldn't update
|
||||
// each other
|
||||
if (formatSlug(input.value) !== slugField.value) {
|
||||
return;
|
||||
}
|
||||
nameInput.addEventListener("input", () => {
|
||||
slugField.value = formatSlug(input.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
resetForm(): void {
|
||||
const form = this.shadowRoot?.querySelector<HTMLFormElement>("form");
|
||||
form?.reset();
|
||||
|
@ -77,9 +77,6 @@ export class HorizontalFormElement extends AKElement {
|
||||
@property({ attribute: false })
|
||||
errorMessages: string[] | string[][] = [];
|
||||
|
||||
@property({ type: Boolean })
|
||||
slugMode = false;
|
||||
|
||||
_invalid = false;
|
||||
|
||||
/* If this property changes, we want to make sure the parent control is "opened" so
|
||||
@ -109,13 +106,6 @@ export class HorizontalFormElement extends AKElement {
|
||||
this.querySelectorAll<HTMLInputElement>("input[autofocus]").forEach((input) => {
|
||||
input.focus();
|
||||
});
|
||||
if (this.name === "slug" || this.slugMode) {
|
||||
this.querySelectorAll<HTMLInputElement>("input[type='text']").forEach((input) => {
|
||||
input.addEventListener("keyup", () => {
|
||||
input.value = formatSlug(input.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
this.querySelectorAll("*").forEach((input) => {
|
||||
if (isAkControl(input) && !input.getAttribute("name")) {
|
||||
input.setAttribute("name", this.name);
|
||||
|
@ -163,7 +163,8 @@ export class NotificationDrawer extends AKElement {
|
||||
}
|
||||
|
||||
renderEmpty() {
|
||||
return html`<ak-empty-state header=${msg("No notifications found.")}>
|
||||
return html`<ak-empty-state
|
||||
><span slot="header">${msg("No notifications found.")}</span>
|
||||
<div slot="body">${msg("You don't have any notifications currently.")}</div>
|
||||
</ak-empty-state>`;
|
||||
}
|
||||
|
@ -288,7 +288,9 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
return html`<tr role="row">
|
||||
<td role="cell" colspan="25">
|
||||
<div class="pf-l-bullseye">
|
||||
<ak-empty-state loading header=${msg("Loading")}></ak-empty-state>
|
||||
<ak-empty-state loading
|
||||
><span slot="header">${msg("Loading")}</span></ak-empty-state
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
@ -300,8 +302,9 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
<td role="cell" colspan="8">
|
||||
<div class="pf-l-bullseye">
|
||||
${inner ??
|
||||
html`<ak-empty-state header="${msg("No objects found.")}"
|
||||
><div slot="primary">${this.renderObjectCreate()}</div>
|
||||
html`<ak-empty-state
|
||||
><span slot="header">${msg("No objects found.")}</span> >
|
||||
<div slot="primary">${this.renderObjectCreate()}</div>
|
||||
</ak-empty-state>`}
|
||||
</div>
|
||||
</td>
|
||||
@ -316,7 +319,8 @@ export abstract class Table<T> extends AKElement implements TableLike {
|
||||
renderError(): SlottedTemplateResult {
|
||||
if (!this.error) return nothing;
|
||||
|
||||
return html`<ak-empty-state header="${msg("Failed to fetch objects.")}" icon="fa-ban">
|
||||
return html`<ak-empty-state icon="fa-ban"
|
||||
><span slot="header">${msg("Failed to fetch objects.")}</span>
|
||||
<div slot="body">${pluckErrorDetail(this.error)}</div>
|
||||
</ak-empty-state>`;
|
||||
}
|
||||
|
@ -19,7 +19,11 @@ describe("ak-empty-state", () => {
|
||||
});
|
||||
|
||||
it("should render the default loader", async () => {
|
||||
render(html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`);
|
||||
render(
|
||||
html`<ak-empty-state loading
|
||||
><span slot="header">${msg("Loading")}</span>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
|
||||
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
|
||||
await expect(empty).toExist();
|
||||
@ -29,7 +33,11 @@ describe("ak-empty-state", () => {
|
||||
});
|
||||
|
||||
it("should handle standard boolean", async () => {
|
||||
render(html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`);
|
||||
render(
|
||||
html`<ak-empty-state loading
|
||||
><span slot="header">${msg("Loading")}</span>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
|
||||
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
|
||||
await expect(empty).toExist();
|
||||
@ -39,7 +47,11 @@ describe("ak-empty-state", () => {
|
||||
});
|
||||
|
||||
it("should render a static empty state", async () => {
|
||||
render(html`<ak-empty-state header=${msg("No messages found")}> </ak-empty-state>`);
|
||||
render(
|
||||
html`<ak-empty-state
|
||||
><span slot="header">${msg("No messages found")}</span>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
|
||||
const empty = await $("ak-empty-state").$(">>>.pf-c-empty-state__icon");
|
||||
await expect(empty).toExist();
|
||||
@ -51,7 +63,8 @@ describe("ak-empty-state", () => {
|
||||
|
||||
it("should render a slotted message", async () => {
|
||||
render(
|
||||
html`<ak-empty-state header=${msg("No messages found")}>
|
||||
html`<ak-empty-state
|
||||
><span slot="header">${msg("No messages found")}</span>
|
||||
<p slot="body">Try again with a different filter</p>
|
||||
</ak-empty-state>`,
|
||||
);
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {
|
||||
checkWebAuthnSupport,
|
||||
transformAssertionForServer,
|
||||
transformCredentialRequestOptions,
|
||||
} from "@goauthentik/common/helpers/webauthn";
|
||||
import { checkWebAuthnSupport } from "@goauthentik/common/helpers/webauthn";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import { BaseDeviceStage } from "@goauthentik/flow/stages/authenticator_validate/base";
|
||||
|
||||
@ -38,12 +34,12 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
|
||||
async authenticate(): Promise<void> {
|
||||
// request the authenticator to create an assertion signature using the
|
||||
// credential private key
|
||||
let assertion;
|
||||
let assertion: PublicKeyCredential;
|
||||
checkWebAuthnSupport();
|
||||
try {
|
||||
assertion = await navigator.credentials.get({
|
||||
assertion = (await navigator.credentials.get({
|
||||
publicKey: this.transformedCredentialRequestOptions,
|
||||
});
|
||||
})) as PublicKeyCredential;
|
||||
if (!assertion) {
|
||||
throw new Error("Assertions is empty");
|
||||
}
|
||||
@ -51,17 +47,11 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
|
||||
throw new Error(`Error when creating credential: ${err}`);
|
||||
}
|
||||
|
||||
// we now have an authentication assertion! encode the byte arrays contained
|
||||
// in the assertion data as strings for posting to the server
|
||||
const transformedAssertionForServer = transformAssertionForServer(
|
||||
assertion as PublicKeyCredential,
|
||||
);
|
||||
|
||||
// post the assertion to the server for verification.
|
||||
try {
|
||||
await this.host?.submit(
|
||||
{
|
||||
webauthn: transformedAssertionForServer,
|
||||
webauthn: assertion.toJSON(),
|
||||
},
|
||||
{
|
||||
invisible: true,
|
||||
@ -74,12 +64,10 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("challenge") && this.challenge !== undefined) {
|
||||
// convert certain members of the PublicKeyCredentialRequestOptions into
|
||||
// byte arrays as expected by the spec.
|
||||
const credentialRequestOptions = this.deviceChallenge
|
||||
?.challenge as PublicKeyCredentialRequestOptions;
|
||||
?.challenge as unknown as PublicKeyCredentialRequestOptionsJSON;
|
||||
this.transformedCredentialRequestOptions =
|
||||
transformCredentialRequestOptions(credentialRequestOptions);
|
||||
PublicKeyCredential.parseRequestOptionsFromJSON(credentialRequestOptions);
|
||||
this.authenticateWrapper();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,4 @@
|
||||
import {
|
||||
Assertion,
|
||||
checkWebAuthnSupport,
|
||||
transformCredentialCreateOptions,
|
||||
transformNewAssertionForServer,
|
||||
} from "@goauthentik/common/helpers/webauthn";
|
||||
import { checkWebAuthnSupport } from "@goauthentik/common/helpers/webauthn";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import { BaseStage } from "@goauthentik/flow/stages/base";
|
||||
|
||||
@ -24,10 +19,6 @@ import {
|
||||
AuthenticatorWebAuthnChallengeResponseRequest,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
export interface WebAuthnAuthenticatorRegisterChallengeResponse {
|
||||
response: Assertion;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-authenticator-webauthn")
|
||||
export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
||||
AuthenticatorWebAuthnChallenge,
|
||||
@ -68,7 +59,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
||||
}
|
||||
checkWebAuthnSupport();
|
||||
// request the authenticator(s) to create a new credential keypair.
|
||||
let credential;
|
||||
let credential: PublicKeyCredential;
|
||||
try {
|
||||
credential = (await navigator.credentials.create({
|
||||
publicKey: this.publicKeyCredentialCreateOptions,
|
||||
@ -80,16 +71,12 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
||||
throw new Error(msg(str`Error creating credential: ${err}`));
|
||||
}
|
||||
|
||||
// we now have a new credential! We now need to encode the byte arrays
|
||||
// in the credential into strings, for posting to our server.
|
||||
const newAssertionForServer = transformNewAssertionForServer(credential);
|
||||
|
||||
// post the transformed credential data to the server for validation
|
||||
// and storing the public key
|
||||
try {
|
||||
await this.host?.submit(
|
||||
{
|
||||
response: newAssertionForServer,
|
||||
response: credential.toJSON(),
|
||||
},
|
||||
{
|
||||
invisible: true,
|
||||
@ -118,12 +105,10 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("challenge") && this.challenge !== undefined) {
|
||||
// convert certain members of the PublicKeyCredentialCreateOptions into
|
||||
// byte arrays as expected by the spec.
|
||||
this.publicKeyCredentialCreateOptions = transformCredentialCreateOptions(
|
||||
this.challenge?.registration as PublicKeyCredentialCreationOptions,
|
||||
this.challenge?.registration.user.id,
|
||||
);
|
||||
this.publicKeyCredentialCreateOptions =
|
||||
PublicKeyCredential.parseCreationOptionsFromJSON(
|
||||
this.challenge?.registration as PublicKeyCredentialCreationOptionsJSON,
|
||||
);
|
||||
this.registerWrapper();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import "construct-style-sheets-polyfill";
|
||||
import "@webcomponents/webcomponentsjs";
|
||||
import "lit/polyfill-support.js";
|
||||
import "core-js/actual";
|
||||
import "webauthn-polyfills";
|
||||
|
||||
import "@formatjs/intl-listformat/polyfill";
|
||||
import "@formatjs/intl-listformat/locale-data/en";
|
||||
|
2
web/types/node.d.ts
vendored
2
web/types/node.d.ts
vendored
@ -14,7 +14,7 @@ declare module "module" {
|
||||
* const relativeDirname = dirname(fileURLToPath(import.meta.url));
|
||||
* ```
|
||||
*/
|
||||
// eslint-disable-next-line no-var
|
||||
|
||||
var __dirname: string;
|
||||
}
|
||||
}
|
||||
|
@ -9245,6 +9245,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7753,6 +7753,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9305,6 +9305,9 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9874,6 +9874,9 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9857,6 +9857,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9213,6 +9213,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9117,6 +9117,9 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9540,6 +9540,9 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9549,4 +9549,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
@ -9632,6 +9632,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9604,6 +9604,9 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -6368,6 +6368,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -9878,12 +9878,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9686d31d28fcf7d">
|
||||
<source>Show field content</source>
|
||||
<target>显示字段内容</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1b05a7573ab618c">
|
||||
<source>Hide field content</source>
|
||||
<target>隐藏字段内容</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
<target>使用 Plex 重新验证身份</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7453,6 +7453,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -3011,11 +3011,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<source>Load servers</source>
|
||||
<target>加载服务器</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s24f405197ede5ebb">
|
||||
<source>Re-authenticate with plex</source>
|
||||
<target>使用 Plex 重新验证身份</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc297b2e13c28ecf9">
|
||||
<source>Allow friends to authenticate via Plex, even if you don't share any servers</source>
|
||||
@ -9880,6 +9875,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sb3d5c0a0501669df">
|
||||
<source>Generate New Certificate-Key Pair</source>
|
||||
<target>生成新的证书密钥对</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sf9686d31d28fcf7d">
|
||||
<source>Show field content</source>
|
||||
<target>显示字段内容</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb1b05a7573ab618c">
|
||||
<source>Hide field content</source>
|
||||
<target>隐藏字段内容</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
<target>使用 Plex 重新验证身份</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -9192,6 +9192,9 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4f820625804ed29b">
|
||||
<source>Re-authenticate with Plex</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0433d667ea6eec1a">
|
||||
<source>The name of an invitation must be a slug: only lower case letters, numbers, and the hyphen are permitted here.</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -70,9 +70,6 @@ To check if your config has been applied correctly, you can run the following co
|
||||
- `AUTHENTIK_POSTGRESQL__USER`: Database user
|
||||
- `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432
|
||||
- `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
|
||||
{/* TODO: Temporarily deactivated feature, see https://github.com/goauthentik/authentik/issues/14320 */}
|
||||
{/* - `AUTHENTIK_POSTGRESQL__USE_POOL`: Use a [connection pool](https://docs.djangoproject.com/en/stable/ref/databases/#connection-pool) for PostgreSQL connections. Defaults to `false`. :ak-version[2025.4] */}
|
||||
{/* - `AUTHENTIK_POSTGRESQL__POOL_OPTIONS`: Extra configuration to pass to the [ConnectionPool object](https://www.psycopg.org/psycopg3/docs/api/pool.html#psycopg_pool.ConnectionPool) when it is created. Must be a base64-encoded JSON dictionary. Ignored when `USE_POOL` is set to `false`. :ak-version[2025.4] */}
|
||||
- `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer. Deprecated, see below
|
||||
- `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool. Deprecated, see below
|
||||
- `AUTHENTIK_POSTGRESQL__SSLMODE`: Strictness of ssl verification. Defaults to `"verify-ca"`
|
||||
@ -85,7 +82,7 @@ To check if your config has been applied correctly, you can run the following co
|
||||
|
||||
The PostgreSQL settings `HOST`, `PORT`, `USER`, and `PASSWORD` support hot-reloading. Adding and removing read replicas doesn't support hot-reloading.
|
||||
|
||||
- `AUTHENTIK_POSTGRESQL__DEFAULT_SCHEMA`:ak-version[2024.12]
|
||||
- `AUTHENTIK_POSTGRESQL__DEFAULT_SCHEMA` :ak-version[2024.12]
|
||||
|
||||
The name of the schema used by default in the database. Defaults to `public`.
|
||||
|
||||
|
@ -142,6 +142,17 @@ helm upgrade authentik authentik/authentik -f values.yaml --version ^2025.6
|
||||
- tenants: fix tenant aware celery scheduler (cherry-pick #14921)
|
||||
- web/user: fix user settings flow not loading (cherry-pick #14911) (#14930)
|
||||
|
||||
## Fixed in 2025.6.2
|
||||
|
||||
- brands: fix custom_css being escaped (cherry-pick #14994) (#14996)
|
||||
- core: bump django from 5.1.10 to 5.1.11 (cherry-pick #14997) (#15010)
|
||||
- core: bump django from 5.1.9 to 5.1.10 (cherry-pick #14951) (#15008)
|
||||
- internal/outpost: fix incorrect usage of golang SHA API (cherry-pick #14981) (#14982)
|
||||
- providers/rac: fixes prompt data not being merged with connection_settings (cherry-pick #15037) (#15038)
|
||||
- stages/email: Only attach logo to email if used (cherry-pick #14835) (#14969)
|
||||
- web/elements: fix dual select without sortBy (cherry-pick #14977) (#14979)
|
||||
- web/elements: fix typo in localeComparator (cherry-pick #15054) (#15055)
|
||||
|
||||
## API Changes
|
||||
|
||||
#### What's New
|
||||
|
@ -27,3 +27,29 @@ uv run ak create_recovery_key 10 akadmin
|
||||
```
|
||||
|
||||
This will output a link, that can be used to instantly gain access to authentik as the user specified above. The link is valid for amount of years specified above, in this case, 10 years.
|
||||
|
||||
## Can't access initial setup flow during installation steps
|
||||
|
||||
If you're unable to access the initial setup flow (`/if/flow/initial-setup/`) immediately after installing authentik, first try restarting the containers because this often resolves temporary issues.
|
||||
|
||||
However, if the issue persists after restarting, you can directly set the admin password using the following commands:
|
||||
|
||||
Docker Compose deployments:
|
||||
|
||||
```bash
|
||||
docker compose exec server ak changepassword akadmin
|
||||
```
|
||||
|
||||
Kubernetes deployments:
|
||||
|
||||
```bash
|
||||
kubectl exec -it deployment/authentik-server -c server -- ak changepassword akadmin
|
||||
```
|
||||
|
||||
After following the prompts to set a new password, you can then login via: `https://authentik.company/if/flow/default-authentication-flow/?next=%2F`
|
||||
|
||||
After logging in, you can set the email address and other settings for the account by navigating to **Directory** > **Users** and editing the user account.
|
||||
|
||||
:::note
|
||||
This method bypasses the initial setup flow and should only be used as a last resort. The initial setup flow is the recommended method to configure the administrator user.
|
||||
:::
|
||||
|
@ -49,7 +49,7 @@ After you are connected, execute these commands to create a database backup:
|
||||
cd /bitnami/postgresql/
|
||||
|
||||
# Set the PostgreSQL password from environment variable
|
||||
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD
|
||||
export PGPASSWORD=$(cat $POSTGRES_PASSWORD_FILE)
|
||||
|
||||
# Create a full database dump
|
||||
pg_dump -U $POSTGRES_USER $POSTGRES_DB > /bitnami/postgresql/dump.sql
|
||||
@ -117,7 +117,7 @@ cd /bitnami/postgresql/
|
||||
ls -lh dump.sql
|
||||
|
||||
# Set the PostgreSQL password
|
||||
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD
|
||||
export PGPASSWORD=$(cat $POSTGRES_PASSWORD_FILE)
|
||||
|
||||
# Import the database dump
|
||||
psql -U $POSTGRES_USER $POSTGRES_DB < dump.sql
|
||||
|
@ -81,7 +81,7 @@ If you want to control user storage and designate Nextcloud administrators, you
|
||||
- **Create Scope Mapping**:
|
||||
|
||||
- **Name**: `Nextcloud Profile`
|
||||
- **Scope name**: `profile`
|
||||
- **Scope name**: `nextcloud`
|
||||
- **Expression**:
|
||||
|
||||
```python
|
||||
@ -146,7 +146,7 @@ Depending on your Nextcloud configuration, you may need to use `https://nextclou
|
||||
- **Client ID**: Client ID from authentik
|
||||
- **Client secret**: Client secret from authentik
|
||||
- **Discovery endpoint**: `https://authentik.company/application/o/<application_slug>/.well-known/openid-configuration`
|
||||
- **Scope**: `email profile openid`
|
||||
- **Scope**: `email nextcloud openid`
|
||||
- Under **Attribute mappings**:
|
||||
|
||||
- **User ID mapping**: `sub` (or `user_id` for existing users)
|
||||
@ -161,6 +161,10 @@ Depending on your Nextcloud configuration, you may need to use `https://nextclou
|
||||
|
||||
- **Use unique user ID**: If this option is disabled, Nextcloud will use the mapped user ID as the Federated Cloud ID.
|
||||
|
||||
:::note
|
||||
If authentik and Nextcloud are running on the same host, you will need to add `'allow_local_remote_servers' => true` to your nextcloud `config.php` file. This setting allows remote servers with local addresses.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
To avoid a hashed Federated Cloud ID, deselect **Use unique user ID** and use `user_id` for the User ID mapping.
|
||||
:::
|
||||
|
@ -40,6 +40,7 @@ To support the integration of Zipline with authentik, you need to create an appl
|
||||
- Note the **Client ID** and **Client Secret** values because they will be required later.
|
||||
- Set a `Strict` redirect URI to `https://zipline.company/api/auth/oauth/oidc`.
|
||||
- Select any available signing key.
|
||||
- Under **Advanced Protocol Settings** > **Scopes**, add `authentik default OAuth Mapping: OpenID 'offline_access'` to the **Selected Scopes** list.
|
||||
- **Configure Bindings** _(optional)_: Create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
||||
|
||||
3. Click **Submit** to save the new application and provider.
|
||||
|
224
website/package-lock.json
generated
224
website/package-lock.json
generated
@ -19,11 +19,10 @@
|
||||
"@goauthentik/docusaurus-config": "^1.1.0",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@swc/html-linux-x64-gnu": "1.12.1",
|
||||
"clsx": "^2.1.1",
|
||||
"docusaurus-plugin-openapi-docs": "^4.4.0",
|
||||
"docusaurus-theme-openapi-docs": "^4.4.0",
|
||||
"postcss": "^8.5.5",
|
||||
"postcss": "^8.5.6",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"react": "^18.3.1",
|
||||
"react-before-after-slider-component": "^1.1.8",
|
||||
@ -36,26 +35,26 @@
|
||||
"@docusaurus/module-type-aliases": "^3.7.0",
|
||||
"@docusaurus/tsconfig": "^3.7.0",
|
||||
"@docusaurus/types": "^3.7.0",
|
||||
"@eslint/js": "^9.28.0",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@goauthentik/eslint-config": "^1.0.5",
|
||||
"@goauthentik/prettier-config": "^1.0.5",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/lodash": "^4.17.17",
|
||||
"@types/node": "^24.0.1",
|
||||
"@types/lodash": "^4.17.18",
|
||||
"@types/node": "^24.0.3",
|
||||
"@types/postman-collection": "^3.5.11",
|
||||
"@types/react": "^18.3.22",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
||||
"@typescript-eslint/parser": "^8.34.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.34.1",
|
||||
"@typescript-eslint/parser": "^8.34.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint": "^9.29.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-packagejson": "^2.5.15",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.34.0"
|
||||
"typescript-eslint": "^8.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.14.0"
|
||||
@ -4209,9 +4208,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array": {
|
||||
"version": "0.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
|
||||
"integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
|
||||
"version": "0.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz",
|
||||
"integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@ -4308,9 +4307,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz",
|
||||
"integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==",
|
||||
"version": "9.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz",
|
||||
"integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -6581,9 +6580,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz",
|
||||
"integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==",
|
||||
"version": "4.17.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.18.tgz",
|
||||
"integrity": "sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -6615,9 +6614,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz",
|
||||
"integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==",
|
||||
"version": "24.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz",
|
||||
"integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.0"
|
||||
@ -6831,17 +6830,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz",
|
||||
"integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz",
|
||||
"integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.0",
|
||||
"@typescript-eslint/type-utils": "8.34.0",
|
||||
"@typescript-eslint/utils": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.1",
|
||||
"@typescript-eslint/type-utils": "8.34.1",
|
||||
"@typescript-eslint/utils": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
@ -6855,7 +6854,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.34.0",
|
||||
"@typescript-eslint/parser": "^8.34.1",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
@ -6871,16 +6870,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz",
|
||||
"integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz",
|
||||
"integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.34.0",
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/typescript-estree": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.1",
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/typescript-estree": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -6896,14 +6895,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz",
|
||||
"integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz",
|
||||
"integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.34.0",
|
||||
"@typescript-eslint/types": "^8.34.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.34.1",
|
||||
"@typescript-eslint/types": "^8.34.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -6918,14 +6917,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz",
|
||||
"integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz",
|
||||
"integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0"
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -6936,9 +6935,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz",
|
||||
"integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz",
|
||||
"integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -6953,14 +6952,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz",
|
||||
"integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz",
|
||||
"integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.34.0",
|
||||
"@typescript-eslint/utils": "8.34.0",
|
||||
"@typescript-eslint/typescript-estree": "8.34.1",
|
||||
"@typescript-eslint/utils": "8.34.1",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
@ -6977,9 +6976,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz",
|
||||
"integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz",
|
||||
"integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -6991,16 +6990,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz",
|
||||
"integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz",
|
||||
"integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.34.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.34.0",
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/visitor-keys": "8.34.0",
|
||||
"@typescript-eslint/project-service": "8.34.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.34.1",
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/visitor-keys": "8.34.1",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -7020,9 +7019,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -7046,16 +7045,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz",
|
||||
"integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz",
|
||||
"integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.34.0",
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"@typescript-eslint/typescript-estree": "8.34.0"
|
||||
"@typescript-eslint/scope-manager": "8.34.1",
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"@typescript-eslint/typescript-estree": "8.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -7070,14 +7069,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz",
|
||||
"integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz",
|
||||
"integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.34.0",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
"@typescript-eslint/types": "8.34.1",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -7291,9 +7290,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@ -12218,19 +12218,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.28.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz",
|
||||
"integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==",
|
||||
"version": "9.29.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz",
|
||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.20.0",
|
||||
"@eslint/config-array": "^0.20.1",
|
||||
"@eslint/config-helpers": "^0.2.1",
|
||||
"@eslint/core": "^0.14.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.28.0",
|
||||
"@eslint/js": "9.29.0",
|
||||
"@eslint/plugin-kit": "^0.3.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@ -12242,9 +12242,9 @@
|
||||
"cross-spawn": "^7.0.6",
|
||||
"debug": "^4.3.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^8.3.0",
|
||||
"eslint-visitor-keys": "^4.2.0",
|
||||
"espree": "^10.3.0",
|
||||
"eslint-scope": "^8.4.0",
|
||||
"eslint-visitor-keys": "^4.2.1",
|
||||
"espree": "^10.4.0",
|
||||
"esquery": "^1.5.0",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@ -12558,9 +12558,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/eslint-scope": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
|
||||
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
|
||||
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
@ -12575,9 +12575,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
|
||||
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -12706,15 +12706,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
|
||||
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
|
||||
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
"acorn": "^8.15.0",
|
||||
"acorn-jsx": "^5.3.2",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -12724,9 +12724,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/espree/node_modules/eslint-visitor-keys": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
|
||||
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@ -20672,9 +20672,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
|
||||
"integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -26387,15 +26387,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz",
|
||||
"integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==",
|
||||
"version": "8.34.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.1.tgz",
|
||||
"integrity": "sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.34.0",
|
||||
"@typescript-eslint/parser": "8.34.0",
|
||||
"@typescript-eslint/utils": "8.34.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.34.1",
|
||||
"@typescript-eslint/parser": "8.34.1",
|
||||
"@typescript-eslint/utils": "8.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -37,7 +37,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"docusaurus-plugin-openapi-docs": "^4.4.0",
|
||||
"docusaurus-theme-openapi-docs": "^4.4.0",
|
||||
"postcss": "^8.5.5",
|
||||
"postcss": "^8.5.6",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"react": "^18.3.1",
|
||||
"react-before-after-slider-component": "^1.1.8",
|
||||
@ -50,26 +50,26 @@
|
||||
"@docusaurus/module-type-aliases": "^3.7.0",
|
||||
"@docusaurus/tsconfig": "^3.7.0",
|
||||
"@docusaurus/types": "^3.7.0",
|
||||
"@eslint/js": "^9.28.0",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@goauthentik/eslint-config": "^1.0.5",
|
||||
"@goauthentik/prettier-config": "^1.0.5",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/lodash": "^4.17.17",
|
||||
"@types/node": "^24.0.1",
|
||||
"@types/lodash": "^4.17.18",
|
||||
"@types/node": "^24.0.3",
|
||||
"@types/postman-collection": "^3.5.11",
|
||||
"@types/react": "^18.3.22",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
||||
"@typescript-eslint/parser": "^8.34.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.34.1",
|
||||
"@typescript-eslint/parser": "^8.34.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint": "^9.29.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-packagejson": "^2.5.15",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.34.0"
|
||||
"typescript-eslint": "^8.34.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rspack/binding-darwin-arm64": "1.3.15",
|
||||
|
Reference in New Issue
Block a user