Compare commits
8 Commits
sources/oa
...
enterprise
Author | SHA1 | Date | |
---|---|---|---|
42a99e3672 | |||
4e501f2fbf | |||
1cca629464 | |||
4efdc3113e | |||
5a9b0f7b7a | |||
395ccc5af1 | |||
c8ac4fcdd6 | |||
53c36394e9 |
@ -43,6 +43,7 @@ from authentik.core.models import (
|
|||||||
)
|
)
|
||||||
from authentik.enterprise.license import LicenseKey
|
from authentik.enterprise.license import LicenseKey
|
||||||
from authentik.enterprise.models import LicenseUsage
|
from authentik.enterprise.models import LicenseUsage
|
||||||
|
from authentik.enterprise.providers.apple_psso.models import AppleNonce
|
||||||
from authentik.enterprise.providers.google_workspace.models import (
|
from authentik.enterprise.providers.google_workspace.models import (
|
||||||
GoogleWorkspaceProviderGroup,
|
GoogleWorkspaceProviderGroup,
|
||||||
GoogleWorkspaceProviderUser,
|
GoogleWorkspaceProviderUser,
|
||||||
@ -135,6 +136,7 @@ def excluded_models() -> list[type[Model]]:
|
|||||||
EndpointDeviceConnection,
|
EndpointDeviceConnection,
|
||||||
DeviceToken,
|
DeviceToken,
|
||||||
StreamEvent,
|
StreamEvent,
|
||||||
|
AppleNonce,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
32
authentik/enterprise/providers/apple_psso/api/providers.py
Normal file
32
authentik/enterprise/providers/apple_psso/api/providers.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
"""Apple Platform SSO Provider API Views"""
|
||||||
|
|
||||||
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
|
from authentik.core.api.providers import ProviderSerializer
|
||||||
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
|
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||||
|
from authentik.enterprise.providers.apple_psso.models import ApplePlatformSSOProvider
|
||||||
|
|
||||||
|
|
||||||
|
class ApplePlatformSSOProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
|
||||||
|
"""ApplePlatformSSOProvider Serializer"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ApplePlatformSSOProvider
|
||||||
|
fields = [
|
||||||
|
"pk",
|
||||||
|
"name",
|
||||||
|
]
|
||||||
|
extra_kwargs = {}
|
||||||
|
|
||||||
|
|
||||||
|
class ApplePlatformSSOProviderViewSet(UsedByMixin, ModelViewSet):
|
||||||
|
"""ApplePlatformSSOProvider Viewset"""
|
||||||
|
|
||||||
|
queryset = ApplePlatformSSOProvider.objects.all()
|
||||||
|
serializer_class = ApplePlatformSSOProviderSerializer
|
||||||
|
filterset_fields = [
|
||||||
|
"name",
|
||||||
|
]
|
||||||
|
search_fields = ["name"]
|
||||||
|
ordering = ["name"]
|
13
authentik/enterprise/providers/apple_psso/apps.py
Normal file
13
authentik/enterprise/providers/apple_psso/apps.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from authentik.enterprise.apps import EnterpriseConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AuthentikEnterpriseProviderApplePSSOConfig(EnterpriseConfig):
|
||||||
|
|
||||||
|
name = "authentik.enterprise.providers.apple_psso"
|
||||||
|
label = "authentik_providers_apple_psso"
|
||||||
|
verbose_name = "authentik Enterprise.Providers.Apple Platform SSO"
|
||||||
|
default = True
|
||||||
|
mountpoints = {
|
||||||
|
"authentik.enterprise.providers.apple_psso.urls": "endpoint/apple/sso/",
|
||||||
|
"authentik.enterprise.providers.apple_psso.urls_root": "",
|
||||||
|
}
|
118
authentik/enterprise/providers/apple_psso/http.py
Normal file
118
authentik/enterprise/providers/apple_psso/http.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
from base64 import urlsafe_b64encode
|
||||||
|
from json import dumps
|
||||||
|
from secrets import token_bytes
|
||||||
|
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives import hashes, serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||||
|
from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from jwcrypto.common import base64url_decode, base64url_encode
|
||||||
|
|
||||||
|
from authentik.enterprise.providers.apple_psso.models import AppleDevice
|
||||||
|
|
||||||
|
|
||||||
|
def length_prefixed(data: bytes) -> bytes:
|
||||||
|
length = len(data)
|
||||||
|
return length.to_bytes(4, "big") + data
|
||||||
|
|
||||||
|
|
||||||
|
def build_apu(public_key: ec.EllipticCurvePublicKey):
|
||||||
|
# X9.63 representation: 0x04 || X || Y
|
||||||
|
public_numbers = public_key.public_numbers()
|
||||||
|
|
||||||
|
x_bytes = public_numbers.x.to_bytes(32, "big")
|
||||||
|
y_bytes = public_numbers.y.to_bytes(32, "big")
|
||||||
|
|
||||||
|
x963 = bytes([0x04]) + x_bytes + y_bytes
|
||||||
|
|
||||||
|
result = length_prefixed(b"APPLE") + length_prefixed(x963)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt_token_with_a256_gcm(body: dict, device_encryption_key: str, apv: bytes) -> str:
|
||||||
|
ephemeral_key = ec.generate_private_key(curve=ec.SECP256R1())
|
||||||
|
device_public_key = serialization.load_pem_public_key(
|
||||||
|
device_encryption_key.encode(), backend=default_backend()
|
||||||
|
)
|
||||||
|
|
||||||
|
shared_secret_z = ephemeral_key.exchange(ec.ECDH(), device_public_key)
|
||||||
|
|
||||||
|
apu = build_apu(ephemeral_key.public_key())
|
||||||
|
|
||||||
|
jwe_header = {
|
||||||
|
"enc": "A256GCM",
|
||||||
|
"kid": "ephemeralKey",
|
||||||
|
"epk": {
|
||||||
|
"x": base64url_encode(
|
||||||
|
ephemeral_key.public_key().public_numbers().x.to_bytes(32, "big")
|
||||||
|
),
|
||||||
|
"y": base64url_encode(
|
||||||
|
ephemeral_key.public_key().public_numbers().y.to_bytes(32, "big")
|
||||||
|
),
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
},
|
||||||
|
"typ": "platformsso-login-response+jwt",
|
||||||
|
"alg": "ECDH-ES",
|
||||||
|
"apu": base64url_encode(apu),
|
||||||
|
"apv": base64url_encode(apv),
|
||||||
|
}
|
||||||
|
|
||||||
|
party_u_info = length_prefixed(apu)
|
||||||
|
party_v_info = length_prefixed(apv)
|
||||||
|
supp_pub_info = (256).to_bytes(4, "big")
|
||||||
|
|
||||||
|
other_info = length_prefixed(b"A256GCM") + party_u_info + party_v_info + supp_pub_info
|
||||||
|
|
||||||
|
ckdf = ConcatKDFHash(
|
||||||
|
algorithm=hashes.SHA256(),
|
||||||
|
length=32,
|
||||||
|
otherinfo=other_info,
|
||||||
|
)
|
||||||
|
|
||||||
|
derived_key = ckdf.derive(shared_secret_z)
|
||||||
|
|
||||||
|
nonce = token_bytes(12)
|
||||||
|
|
||||||
|
header_json = dumps(jwe_header, separators=(",", ":")).encode()
|
||||||
|
aad = urlsafe_b64encode(header_json).rstrip(b"=")
|
||||||
|
|
||||||
|
aesgcm = AESGCM(derived_key)
|
||||||
|
ciphertext = aesgcm.encrypt(nonce, dumps(body).encode(), aad)
|
||||||
|
|
||||||
|
ciphertext_body = ciphertext[:-16]
|
||||||
|
tag = ciphertext[-16:]
|
||||||
|
|
||||||
|
# base64url encoding
|
||||||
|
protected_b64 = urlsafe_b64encode(header_json).rstrip(b"=")
|
||||||
|
iv_b64 = urlsafe_b64encode(nonce).rstrip(b"=")
|
||||||
|
ciphertext_b64 = urlsafe_b64encode(ciphertext_body).rstrip(b"=")
|
||||||
|
tag_b64 = urlsafe_b64encode(tag).rstrip(b"=")
|
||||||
|
|
||||||
|
jwe_compact = b".".join(
|
||||||
|
[
|
||||||
|
protected_b64,
|
||||||
|
b"",
|
||||||
|
iv_b64,
|
||||||
|
ciphertext_b64,
|
||||||
|
tag_b64,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return jwe_compact.decode()
|
||||||
|
|
||||||
|
|
||||||
|
class JWEResponse(HttpResponse):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
data: dict,
|
||||||
|
device: AppleDevice,
|
||||||
|
apv: str,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
content=encrypt_token_with_a256_gcm(data, device.encryption_key, base64url_decode(apv)),
|
||||||
|
content_type="application/platformsso-login-response+jwt",
|
||||||
|
)
|
@ -0,0 +1,36 @@
|
|||||||
|
# Generated by Django 5.1.11 on 2025-06-28 00:12
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_providers_oauth2", "0028_migrate_session"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ApplePlatformSSOProvider",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"oauth2provider_ptr",
|
||||||
|
models.OneToOneField(
|
||||||
|
auto_created=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
parent_link=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
to="authentik_providers_oauth2.oauth2provider",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
bases=("authentik_providers_oauth2.oauth2provider",),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,94 @@
|
|||||||
|
# Generated by Django 5.1.11 on 2025-06-28 15:50
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_providers_apple_psso", "0001_initial"),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="AppleDevice",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"endpoint_uuid",
|
||||||
|
models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
("signing_key", models.TextField()),
|
||||||
|
("encryption_key", models.TextField()),
|
||||||
|
("key_exchange_key", models.TextField()),
|
||||||
|
("sign_key_id", models.TextField()),
|
||||||
|
("enc_key_id", models.TextField()),
|
||||||
|
("creation_time", models.DateTimeField(auto_now_add=True)),
|
||||||
|
(
|
||||||
|
"provider",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="authentik_providers_apple_psso.appleplatformssoprovider",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="AppleDeviceUser",
|
||||||
|
fields=[
|
||||||
|
("uuid", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||||
|
("signing_key", models.TextField()),
|
||||||
|
("encryption_key", models.TextField()),
|
||||||
|
("sign_key_id", models.TextField()),
|
||||||
|
("enc_key_id", models.TextField()),
|
||||||
|
(
|
||||||
|
"device",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="authentik_providers_apple_psso.appledevice",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="appledevice",
|
||||||
|
name="users",
|
||||||
|
field=models.ManyToManyField(
|
||||||
|
through="authentik_providers_apple_psso.AppleDeviceUser",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="AppleNonce",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("expires", models.DateTimeField(default=None, null=True)),
|
||||||
|
("expiring", models.BooleanField(default=True)),
|
||||||
|
("nonce", models.TextField()),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
"indexes": [
|
||||||
|
models.Index(fields=["expires"], name="authentik_p_expires_47d534_idx"),
|
||||||
|
models.Index(fields=["expiring"], name="authentik_p_expirin_87253e_idx"),
|
||||||
|
models.Index(
|
||||||
|
fields=["expiring", "expires"], name="authentik_p_expirin_20a7c9_idx"
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 5.1.11 on 2025-06-28 22:18
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
(
|
||||||
|
"authentik_providers_apple_psso",
|
||||||
|
"0002_appledevice_appledeviceuser_appledevice_users_and_more",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="appledeviceuser",
|
||||||
|
old_name="sign_key_id",
|
||||||
|
new_name="enclave_key_id",
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="appledeviceuser",
|
||||||
|
old_name="signing_key",
|
||||||
|
new_name="secure_enclave_key",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="appledeviceuser",
|
||||||
|
name="enc_key_id",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="appledeviceuser",
|
||||||
|
name="encryption_key",
|
||||||
|
),
|
||||||
|
]
|
85
authentik/enterprise/providers/apple_psso/models.py
Normal file
85
authentik/enterprise/providers/apple_psso/models.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from rest_framework.serializers import Serializer
|
||||||
|
|
||||||
|
from authentik.core.models import ExpiringModel, User
|
||||||
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
|
from authentik.providers.oauth2.models import (
|
||||||
|
ClientTypes,
|
||||||
|
IssuerMode,
|
||||||
|
OAuth2Provider,
|
||||||
|
RedirectURI,
|
||||||
|
RedirectURIMatchingMode,
|
||||||
|
ScopeMapping,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ApplePlatformSSOProvider(OAuth2Provider):
|
||||||
|
"""Integrate with Apple Platform SSO"""
|
||||||
|
|
||||||
|
def set_oauth_defaults(self):
|
||||||
|
"""Ensure all OAuth2-related settings are correct"""
|
||||||
|
self.issuer_mode = IssuerMode.PER_PROVIDER
|
||||||
|
self.client_type = ClientTypes.PUBLIC
|
||||||
|
self.signing_key = CertificateKeyPair.objects.get(name="authentik Self-signed Certificate")
|
||||||
|
self.include_claims_in_id_token = True
|
||||||
|
scopes = ScopeMapping.objects.filter(
|
||||||
|
managed__in=[
|
||||||
|
"goauthentik.io/providers/oauth2/scope-openid",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-profile",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-email",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-offline_access",
|
||||||
|
"goauthentik.io/providers/oauth2/scope-authentik_api",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.property_mappings.add(*list(scopes))
|
||||||
|
self.redirect_uris = [
|
||||||
|
RedirectURI(RedirectURIMatchingMode.STRICT, "io.goauthentik.endpoint:/oauth2redirect"),
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def component(self) -> str:
|
||||||
|
return "ak-provider-apple-psso-form"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serializer(self) -> type[Serializer]:
|
||||||
|
from authentik.enterprise.providers.apple_psso.api.providers import (
|
||||||
|
ApplePlatformSSOProviderSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
return ApplePlatformSSOProviderSerializer
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Apple Platform SSO Provider")
|
||||||
|
verbose_name_plural = _("Apple Platform SSO Providers")
|
||||||
|
|
||||||
|
|
||||||
|
class AppleDevice(models.Model):
|
||||||
|
|
||||||
|
endpoint_uuid = models.UUIDField(default=uuid4, primary_key=True)
|
||||||
|
|
||||||
|
signing_key = models.TextField()
|
||||||
|
encryption_key = models.TextField()
|
||||||
|
key_exchange_key = models.TextField()
|
||||||
|
sign_key_id = models.TextField()
|
||||||
|
enc_key_id = models.TextField()
|
||||||
|
creation_time = models.DateTimeField(auto_now_add=True)
|
||||||
|
provider = models.ForeignKey(ApplePlatformSSOProvider, on_delete=models.CASCADE)
|
||||||
|
users = models.ManyToManyField(User, through="AppleDeviceUser")
|
||||||
|
|
||||||
|
|
||||||
|
class AppleDeviceUser(models.Model):
|
||||||
|
|
||||||
|
uuid = models.UUIDField(default=uuid4, primary_key=True)
|
||||||
|
|
||||||
|
device = models.ForeignKey(AppleDevice, on_delete=models.CASCADE)
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
secure_enclave_key = models.TextField()
|
||||||
|
enclave_key_id = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
|
class AppleNonce(ExpiringModel):
|
||||||
|
nonce = models.TextField()
|
15
authentik/enterprise/providers/apple_psso/urls.py
Normal file
15
authentik/enterprise/providers/apple_psso/urls.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from authentik.enterprise.providers.apple_psso.views.nonce import NonceView
|
||||||
|
from authentik.enterprise.providers.apple_psso.views.register import (
|
||||||
|
RegisterDeviceView,
|
||||||
|
RegisterUserView,
|
||||||
|
)
|
||||||
|
from authentik.enterprise.providers.apple_psso.views.token import TokenView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("token/", TokenView.as_view(), name="token"),
|
||||||
|
path("nonce/", NonceView.as_view(), name="nonce"),
|
||||||
|
path("register/device/", RegisterDeviceView.as_view(), name="register-device"),
|
||||||
|
path("register/user/", RegisterUserView.as_view(), name="register-user"),
|
||||||
|
]
|
7
authentik/enterprise/providers/apple_psso/urls_root.py
Normal file
7
authentik/enterprise/providers/apple_psso/urls_root.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from authentik.enterprise.providers.apple_psso.views.site_association import AppleAppSiteAssociation
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path(".well-known/apple-app-site-association", AppleAppSiteAssociation.as_view(), name="asa"),
|
||||||
|
]
|
25
authentik/enterprise/providers/apple_psso/views/nonce.py
Normal file
25
authentik/enterprise/providers/apple_psso/views/nonce.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from base64 import b64encode
|
||||||
|
from datetime import timedelta
|
||||||
|
from secrets import token_bytes
|
||||||
|
|
||||||
|
from django.http import HttpRequest, JsonResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.views import View
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
from authentik.enterprise.providers.apple_psso.models import AppleNonce
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name="dispatch")
|
||||||
|
class NonceView(View):
|
||||||
|
|
||||||
|
def post(self, request: HttpRequest, *args, **kwargs):
|
||||||
|
nonce = AppleNonce.objects.create(
|
||||||
|
nonce=b64encode(token_bytes(32)).decode(), expires=now() + timedelta(minutes=5)
|
||||||
|
)
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
"Nonce": nonce.nonce,
|
||||||
|
}
|
||||||
|
)
|
92
authentik/enterprise/providers/apple_psso/views/register.py
Normal file
92
authentik/enterprise/providers/apple_psso/views/register.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from rest_framework.authentication import BaseAuthentication
|
||||||
|
from rest_framework.fields import CharField
|
||||||
|
from rest_framework.request import Request
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from authentik.api.authentication import TokenAuthentication
|
||||||
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
|
from authentik.core.models import User
|
||||||
|
from authentik.enterprise.providers.apple_psso.models import (
|
||||||
|
AppleDevice,
|
||||||
|
AppleDeviceUser,
|
||||||
|
ApplePlatformSSOProvider,
|
||||||
|
)
|
||||||
|
from authentik.lib.generators import generate_key
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceRegisterAuth(BaseAuthentication):
|
||||||
|
def authenticate(self, request):
|
||||||
|
# very temporary, lol
|
||||||
|
return (User(), None)
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterDeviceView(APIView):
|
||||||
|
|
||||||
|
class DeviceRegistration(PassiveSerializer):
|
||||||
|
|
||||||
|
device_uuid = CharField()
|
||||||
|
client_id = CharField()
|
||||||
|
device_signing_key = CharField()
|
||||||
|
device_encryption_key = CharField()
|
||||||
|
sign_key_id = CharField()
|
||||||
|
enc_key_id = CharField()
|
||||||
|
|
||||||
|
permission_classes = []
|
||||||
|
pagination_class = None
|
||||||
|
filter_backends = []
|
||||||
|
serializer_class = DeviceRegistration
|
||||||
|
authentication_classes = [DeviceRegisterAuth, TokenAuthentication]
|
||||||
|
|
||||||
|
def post(self, request: Request) -> Response:
|
||||||
|
data = self.DeviceRegistration(data=request.data)
|
||||||
|
data.is_valid(raise_exception=True)
|
||||||
|
provider = get_object_or_404(
|
||||||
|
ApplePlatformSSOProvider, client_id=data.validated_data["client_id"]
|
||||||
|
)
|
||||||
|
AppleDevice.objects.update_or_create(
|
||||||
|
endpoint_uuid=data.validated_data["device_uuid"],
|
||||||
|
defaults={
|
||||||
|
"signing_key": data.validated_data["device_signing_key"],
|
||||||
|
"encryption_key": data.validated_data["device_encryption_key"],
|
||||||
|
"sign_key_id": data.validated_data["sign_key_id"],
|
||||||
|
"enc_key_id": data.validated_data["enc_key_id"],
|
||||||
|
"key_exchange_key": generate_key(),
|
||||||
|
"provider": provider,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return Response()
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterUserView(APIView):
|
||||||
|
|
||||||
|
class UserRegistration(PassiveSerializer):
|
||||||
|
|
||||||
|
device_uuid = CharField()
|
||||||
|
user_secure_enclave_key = CharField()
|
||||||
|
enclave_key_id = CharField()
|
||||||
|
|
||||||
|
permission_classes = []
|
||||||
|
pagination_class = None
|
||||||
|
filter_backends = []
|
||||||
|
serializer_class = UserRegistration
|
||||||
|
authentication_classes = [TokenAuthentication]
|
||||||
|
|
||||||
|
def post(self, request: Request) -> Response:
|
||||||
|
data = self.UserRegistration(data=request.data)
|
||||||
|
data.is_valid(raise_exception=True)
|
||||||
|
device = get_object_or_404(AppleDevice, endpoint_uuid=data.validated_data["device_uuid"])
|
||||||
|
AppleDeviceUser.objects.update_or_create(
|
||||||
|
device=device,
|
||||||
|
user=request.user,
|
||||||
|
defaults={
|
||||||
|
"secure_enclave_key": data.validated_data["user_secure_enclave_key"],
|
||||||
|
"enclave_key_id": data.validated_data["enclave_key_id"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
"username": request.user.username,
|
||||||
|
}
|
||||||
|
)
|
@ -0,0 +1,16 @@
|
|||||||
|
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
|
|
||||||
|
class AppleAppSiteAssociation(View):
|
||||||
|
def get(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
"authsrv": {
|
||||||
|
"apps": [
|
||||||
|
"232G855Y8N.io.goauthentik.endpoint",
|
||||||
|
"232G855Y8N.io.goauthentik.endpoint.psso",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
140
authentik/enterprise/providers/apple_psso/views/token.py
Normal file
140
authentik/enterprise/providers/apple_psso/views/token.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.http import Http404, HttpRequest, HttpResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.views import View
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from jwt import PyJWT, decode
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
|
from authentik.core.models import AuthenticatedSession, Session, User
|
||||||
|
from authentik.core.sessions import SessionStore
|
||||||
|
from authentik.enterprise.providers.apple_psso.http import JWEResponse
|
||||||
|
from authentik.enterprise.providers.apple_psso.models import (
|
||||||
|
AppleDevice,
|
||||||
|
AppleDeviceUser,
|
||||||
|
AppleNonce,
|
||||||
|
ApplePlatformSSOProvider,
|
||||||
|
)
|
||||||
|
from authentik.events.models import Event, EventAction
|
||||||
|
from authentik.events.signals import SESSION_LOGIN_EVENT
|
||||||
|
from authentik.providers.oauth2.constants import TOKEN_TYPE
|
||||||
|
from authentik.providers.oauth2.id_token import IDToken
|
||||||
|
from authentik.providers.oauth2.models import RefreshToken
|
||||||
|
from authentik.root.middleware import SessionMiddleware
|
||||||
|
|
||||||
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name="dispatch")
|
||||||
|
class TokenView(View):
|
||||||
|
|
||||||
|
device: AppleDevice
|
||||||
|
provider: ApplePlatformSSOProvider
|
||||||
|
|
||||||
|
def post(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
version = request.POST.get("platform_sso_version")
|
||||||
|
assertion = request.POST.get("assertion", request.POST.get("request"))
|
||||||
|
if not assertion:
|
||||||
|
return HttpResponse(status=400)
|
||||||
|
|
||||||
|
decode_unvalidated = PyJWT().decode_complete(assertion, options={"verify_signature": False})
|
||||||
|
LOGGER.debug(decode_unvalidated["header"])
|
||||||
|
expected_kid = decode_unvalidated["header"]["kid"]
|
||||||
|
|
||||||
|
self.device = AppleDevice.objects.filter(sign_key_id=expected_kid).first()
|
||||||
|
if not self.device:
|
||||||
|
raise Http404
|
||||||
|
self.provider = self.device.provider
|
||||||
|
|
||||||
|
# Properly decode the JWT with the key from the device
|
||||||
|
decoded = decode(
|
||||||
|
assertion, self.device.signing_key, algorithms=["ES256"], options={"verify_aud": False}
|
||||||
|
)
|
||||||
|
LOGGER.debug(decoded)
|
||||||
|
|
||||||
|
LOGGER.debug("got device", device=self.device)
|
||||||
|
|
||||||
|
# Check that the nonce hasn't been used before
|
||||||
|
nonce = AppleNonce.objects.filter(nonce=decoded["request_nonce"]).first()
|
||||||
|
if not nonce:
|
||||||
|
return HttpResponse(status=400)
|
||||||
|
nonce.delete()
|
||||||
|
|
||||||
|
handler_func = (
|
||||||
|
f"handle_v{version}_{decode_unvalidated["header"]["typ"]}".replace("-", "_")
|
||||||
|
.replace("+", "_")
|
||||||
|
.replace(".", "_")
|
||||||
|
)
|
||||||
|
handler = getattr(self, handler_func, None)
|
||||||
|
if not handler:
|
||||||
|
LOGGER.debug("Handler not found", handler=handler_func)
|
||||||
|
return HttpResponse(status=400)
|
||||||
|
LOGGER.debug("sending to handler", handler=handler_func)
|
||||||
|
return handler(decoded)
|
||||||
|
|
||||||
|
def validate_device_user_response(self, assertion: str) -> tuple[AppleDeviceUser, dict] | None:
|
||||||
|
"""Decode an embedded assertion and validate it by looking up the matching device user"""
|
||||||
|
decode_unvalidated = PyJWT().decode_complete(assertion, options={"verify_signature": False})
|
||||||
|
expected_kid = decode_unvalidated["header"]["kid"]
|
||||||
|
|
||||||
|
device_user = AppleDeviceUser.objects.filter(
|
||||||
|
device=self.device, enclave_key_id=expected_kid
|
||||||
|
).first()
|
||||||
|
if not device_user:
|
||||||
|
return None
|
||||||
|
return device_user, decode(
|
||||||
|
assertion,
|
||||||
|
device_user.secure_enclave_key,
|
||||||
|
audience="apple-platform-sso",
|
||||||
|
algorithms=["ES256"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_auth_session(self, user: User):
|
||||||
|
event = Event.new(EventAction.LOGIN).from_http(self.request, user=user)
|
||||||
|
store = SessionStore()
|
||||||
|
store[SESSION_LOGIN_EVENT] = event
|
||||||
|
store.save()
|
||||||
|
session = Session.objects.filter(session_key=store.session_key).first()
|
||||||
|
AuthenticatedSession.objects.create(session=session, user=user)
|
||||||
|
session = SessionMiddleware.encode_session(store.session_key, user)
|
||||||
|
return session
|
||||||
|
|
||||||
|
def handle_v1_0_platformsso_login_request_jwt(self, decoded: dict):
|
||||||
|
user = None
|
||||||
|
if decoded["grant_type"] == "urn:ietf:params:oauth:grant-type:jwt-bearer":
|
||||||
|
# Decode and validate inner assertion
|
||||||
|
user, inner = self.validate_device_user_response(decoded["assertion"])
|
||||||
|
if inner["nonce"] != decoded["nonce"]:
|
||||||
|
LOGGER.warning("Mis-matched nonce to outer assertion")
|
||||||
|
raise ValidationError("Invalid request")
|
||||||
|
|
||||||
|
refresh_token = RefreshToken(
|
||||||
|
user=user.user,
|
||||||
|
scope=decoded["scope"],
|
||||||
|
expires=now() + timedelta(hours=8),
|
||||||
|
provider=self.provider,
|
||||||
|
auth_time=now(),
|
||||||
|
session=None,
|
||||||
|
)
|
||||||
|
id_token = IDToken.new(
|
||||||
|
self.provider,
|
||||||
|
refresh_token,
|
||||||
|
self.request,
|
||||||
|
)
|
||||||
|
id_token.nonce = decoded["nonce"]
|
||||||
|
refresh_token.id_token = id_token
|
||||||
|
refresh_token.save()
|
||||||
|
return JWEResponse(
|
||||||
|
{
|
||||||
|
"refresh_token": refresh_token.token,
|
||||||
|
"refresh_token_expires_in": int((refresh_token.expires - now()).total_seconds()),
|
||||||
|
"id_token": refresh_token.id_token.to_jwt(self.provider),
|
||||||
|
"token_type": TOKEN_TYPE,
|
||||||
|
"session_key": self.create_auth_session(user.user),
|
||||||
|
},
|
||||||
|
device=self.device,
|
||||||
|
apv=decoded["jwe_crypto"]["apv"],
|
||||||
|
)
|
@ -15,6 +15,7 @@ CELERY_BEAT_SCHEDULE = {
|
|||||||
TENANT_APPS = [
|
TENANT_APPS = [
|
||||||
"authentik.enterprise.audit",
|
"authentik.enterprise.audit",
|
||||||
"authentik.enterprise.policies.unique_password",
|
"authentik.enterprise.policies.unique_password",
|
||||||
|
"authentik.enterprise.providers.apple_psso",
|
||||||
"authentik.enterprise.providers.google_workspace",
|
"authentik.enterprise.providers.google_workspace",
|
||||||
"authentik.enterprise.providers.microsoft_entra",
|
"authentik.enterprise.providers.microsoft_entra",
|
||||||
"authentik.enterprise.providers.ssf",
|
"authentik.enterprise.providers.ssf",
|
||||||
|
@ -555,6 +555,8 @@ class TokenView(View):
|
|||||||
|
|
||||||
provider: OAuth2Provider | None = None
|
provider: OAuth2Provider | None = None
|
||||||
params: TokenParams | None = None
|
params: TokenParams | None = None
|
||||||
|
params_class = TokenParams
|
||||||
|
provider_class = OAuth2Provider
|
||||||
|
|
||||||
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
||||||
response = super().dispatch(request, *args, **kwargs)
|
response = super().dispatch(request, *args, **kwargs)
|
||||||
@ -574,12 +576,14 @@ class TokenView(View):
|
|||||||
op="authentik.providers.oauth2.post.parse",
|
op="authentik.providers.oauth2.post.parse",
|
||||||
):
|
):
|
||||||
client_id, client_secret = extract_client_auth(request)
|
client_id, client_secret = extract_client_auth(request)
|
||||||
self.provider = OAuth2Provider.objects.filter(client_id=client_id).first()
|
self.provider = self.provider_class.objects.filter(client_id=client_id).first()
|
||||||
if not self.provider:
|
if not self.provider:
|
||||||
LOGGER.warning("OAuth2Provider does not exist", client_id=client_id)
|
LOGGER.warning("OAuth2Provider does not exist", client_id=client_id)
|
||||||
raise TokenError("invalid_client")
|
raise TokenError("invalid_client")
|
||||||
CTX_AUTH_VIA.set("oauth_client_secret")
|
CTX_AUTH_VIA.set("oauth_client_secret")
|
||||||
self.params = TokenParams.parse(request, self.provider, client_id, client_secret)
|
self.params = self.params_class.parse(
|
||||||
|
request, self.provider, client_id, client_secret
|
||||||
|
)
|
||||||
|
|
||||||
with start_span(
|
with start_span(
|
||||||
op="authentik.providers.oauth2.post.response",
|
op="authentik.providers.oauth2.post.response",
|
||||||
|
@ -61,6 +61,22 @@ class SessionMiddleware(UpstreamSessionMiddleware):
|
|||||||
pass
|
pass
|
||||||
return session_key
|
return session_key
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def encode_session(session_key: str, user: User):
|
||||||
|
payload = {
|
||||||
|
"sid": session_key,
|
||||||
|
"iss": "authentik",
|
||||||
|
"sub": "anonymous",
|
||||||
|
"authenticated": user.is_authenticated,
|
||||||
|
"acr": ACR_AUTHENTIK_SESSION,
|
||||||
|
}
|
||||||
|
if user.is_authenticated:
|
||||||
|
payload["sub"] = user.uid
|
||||||
|
value = encode(payload=payload, key=SIGNING_HASH)
|
||||||
|
if settings.TEST:
|
||||||
|
value = session_key
|
||||||
|
return value
|
||||||
|
|
||||||
def process_request(self, request: HttpRequest):
|
def process_request(self, request: HttpRequest):
|
||||||
raw_session = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
|
raw_session = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
|
||||||
session_key = SessionMiddleware.decode_session_key(raw_session)
|
session_key = SessionMiddleware.decode_session_key(raw_session)
|
||||||
@ -117,21 +133,9 @@ class SessionMiddleware(UpstreamSessionMiddleware):
|
|||||||
"request completed. The user may have logged "
|
"request completed. The user may have logged "
|
||||||
"out in a concurrent request, for example."
|
"out in a concurrent request, for example."
|
||||||
) from None
|
) from None
|
||||||
payload = {
|
|
||||||
"sid": request.session.session_key,
|
|
||||||
"iss": "authentik",
|
|
||||||
"sub": "anonymous",
|
|
||||||
"authenticated": request.user.is_authenticated,
|
|
||||||
"acr": ACR_AUTHENTIK_SESSION,
|
|
||||||
}
|
|
||||||
if request.user.is_authenticated:
|
|
||||||
payload["sub"] = request.user.uid
|
|
||||||
value = encode(payload=payload, key=SIGNING_HASH)
|
|
||||||
if settings.TEST:
|
|
||||||
value = request.session.session_key
|
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
settings.SESSION_COOKIE_NAME,
|
settings.SESSION_COOKIE_NAME,
|
||||||
value,
|
SessionMiddleware.encode_session(request.session.session_key, request.user),
|
||||||
max_age=max_age,
|
max_age=max_age,
|
||||||
expires=expires,
|
expires=expires,
|
||||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||||
|
@ -496,6 +496,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"model",
|
||||||
|
"identifiers"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"model": {
|
||||||
|
"const": "authentik_providers_apple_psso.appleplatformssoprovider"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"absent",
|
||||||
|
"created",
|
||||||
|
"must_created",
|
||||||
|
"present"
|
||||||
|
],
|
||||||
|
"default": "present"
|
||||||
|
},
|
||||||
|
"conditions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"$ref": "#/$defs/model_authentik_providers_apple_psso.appleplatformssoprovider_permissions"
|
||||||
|
},
|
||||||
|
"attrs": {
|
||||||
|
"$ref": "#/$defs/model_authentik_providers_apple_psso.appleplatformssoprovider"
|
||||||
|
},
|
||||||
|
"identifiers": {
|
||||||
|
"$ref": "#/$defs/model_authentik_providers_apple_psso.appleplatformssoprovider"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -5028,6 +5068,22 @@
|
|||||||
"authentik_policies_unique_password.delete_userpasswordhistory",
|
"authentik_policies_unique_password.delete_userpasswordhistory",
|
||||||
"authentik_policies_unique_password.view_uniquepasswordpolicy",
|
"authentik_policies_unique_password.view_uniquepasswordpolicy",
|
||||||
"authentik_policies_unique_password.view_userpasswordhistory",
|
"authentik_policies_unique_password.view_userpasswordhistory",
|
||||||
|
"authentik_providers_apple_psso.add_appledevice",
|
||||||
|
"authentik_providers_apple_psso.add_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.add_applenonce",
|
||||||
|
"authentik_providers_apple_psso.add_appleplatformssoprovider",
|
||||||
|
"authentik_providers_apple_psso.change_appledevice",
|
||||||
|
"authentik_providers_apple_psso.change_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.change_applenonce",
|
||||||
|
"authentik_providers_apple_psso.change_appleplatformssoprovider",
|
||||||
|
"authentik_providers_apple_psso.delete_appledevice",
|
||||||
|
"authentik_providers_apple_psso.delete_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.delete_applenonce",
|
||||||
|
"authentik_providers_apple_psso.delete_appleplatformssoprovider",
|
||||||
|
"authentik_providers_apple_psso.view_appledevice",
|
||||||
|
"authentik_providers_apple_psso.view_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.view_applenonce",
|
||||||
|
"authentik_providers_apple_psso.view_appleplatformssoprovider",
|
||||||
"authentik_providers_google_workspace.add_googleworkspaceprovider",
|
"authentik_providers_google_workspace.add_googleworkspaceprovider",
|
||||||
"authentik_providers_google_workspace.add_googleworkspaceprovidergroup",
|
"authentik_providers_google_workspace.add_googleworkspaceprovidergroup",
|
||||||
"authentik_providers_google_workspace.add_googleworkspaceprovidermapping",
|
"authentik_providers_google_workspace.add_googleworkspaceprovidermapping",
|
||||||
@ -5599,6 +5655,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"model_authentik_providers_apple_psso.appleplatformssoprovider": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"title": "Name"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
|
"model_authentik_providers_apple_psso.appleplatformssoprovider_permissions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"permission"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"permission": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"add_appleplatformssoprovider",
|
||||||
|
"change_appleplatformssoprovider",
|
||||||
|
"delete_appleplatformssoprovider",
|
||||||
|
"view_appleplatformssoprovider"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"model_authentik_providers_google_workspace.googleworkspaceprovider": {
|
"model_authentik_providers_google_workspace.googleworkspaceprovider": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -7342,6 +7435,7 @@
|
|||||||
"authentik.enterprise",
|
"authentik.enterprise",
|
||||||
"authentik.enterprise.audit",
|
"authentik.enterprise.audit",
|
||||||
"authentik.enterprise.policies.unique_password",
|
"authentik.enterprise.policies.unique_password",
|
||||||
|
"authentik.enterprise.providers.apple_psso",
|
||||||
"authentik.enterprise.providers.google_workspace",
|
"authentik.enterprise.providers.google_workspace",
|
||||||
"authentik.enterprise.providers.microsoft_entra",
|
"authentik.enterprise.providers.microsoft_entra",
|
||||||
"authentik.enterprise.providers.ssf",
|
"authentik.enterprise.providers.ssf",
|
||||||
@ -7452,6 +7546,7 @@
|
|||||||
"authentik_core.token",
|
"authentik_core.token",
|
||||||
"authentik_enterprise.license",
|
"authentik_enterprise.license",
|
||||||
"authentik_policies_unique_password.uniquepasswordpolicy",
|
"authentik_policies_unique_password.uniquepasswordpolicy",
|
||||||
|
"authentik_providers_apple_psso.appleplatformssoprovider",
|
||||||
"authentik_providers_google_workspace.googleworkspaceprovider",
|
"authentik_providers_google_workspace.googleworkspaceprovider",
|
||||||
"authentik_providers_google_workspace.googleworkspaceprovidermapping",
|
"authentik_providers_google_workspace.googleworkspaceprovidermapping",
|
||||||
"authentik_providers_microsoft_entra.microsoftentraprovider",
|
"authentik_providers_microsoft_entra.microsoftentraprovider",
|
||||||
@ -9674,6 +9769,22 @@
|
|||||||
"authentik_policies_unique_password.delete_userpasswordhistory",
|
"authentik_policies_unique_password.delete_userpasswordhistory",
|
||||||
"authentik_policies_unique_password.view_uniquepasswordpolicy",
|
"authentik_policies_unique_password.view_uniquepasswordpolicy",
|
||||||
"authentik_policies_unique_password.view_userpasswordhistory",
|
"authentik_policies_unique_password.view_userpasswordhistory",
|
||||||
|
"authentik_providers_apple_psso.add_appledevice",
|
||||||
|
"authentik_providers_apple_psso.add_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.add_applenonce",
|
||||||
|
"authentik_providers_apple_psso.add_appleplatformssoprovider",
|
||||||
|
"authentik_providers_apple_psso.change_appledevice",
|
||||||
|
"authentik_providers_apple_psso.change_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.change_applenonce",
|
||||||
|
"authentik_providers_apple_psso.change_appleplatformssoprovider",
|
||||||
|
"authentik_providers_apple_psso.delete_appledevice",
|
||||||
|
"authentik_providers_apple_psso.delete_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.delete_applenonce",
|
||||||
|
"authentik_providers_apple_psso.delete_appleplatformssoprovider",
|
||||||
|
"authentik_providers_apple_psso.view_appledevice",
|
||||||
|
"authentik_providers_apple_psso.view_appledeviceuser",
|
||||||
|
"authentik_providers_apple_psso.view_applenonce",
|
||||||
|
"authentik_providers_apple_psso.view_appleplatformssoprovider",
|
||||||
"authentik_providers_google_workspace.add_googleworkspaceprovider",
|
"authentik_providers_google_workspace.add_googleworkspaceprovider",
|
||||||
"authentik_providers_google_workspace.add_googleworkspaceprovidergroup",
|
"authentik_providers_google_workspace.add_googleworkspaceprovidergroup",
|
||||||
"authentik_providers_google_workspace.add_googleworkspaceprovidermapping",
|
"authentik_providers_google_workspace.add_googleworkspaceprovidermapping",
|
||||||
|
16
schema.yml
16
schema.yml
@ -24864,6 +24864,7 @@ paths:
|
|||||||
- authentik_policies_password.passwordpolicy
|
- authentik_policies_password.passwordpolicy
|
||||||
- authentik_policies_reputation.reputationpolicy
|
- authentik_policies_reputation.reputationpolicy
|
||||||
- authentik_policies_unique_password.uniquepasswordpolicy
|
- authentik_policies_unique_password.uniquepasswordpolicy
|
||||||
|
- authentik_providers_apple_psso.appleplatformssoprovider
|
||||||
- authentik_providers_google_workspace.googleworkspaceprovider
|
- authentik_providers_google_workspace.googleworkspaceprovider
|
||||||
- authentik_providers_google_workspace.googleworkspaceprovidermapping
|
- authentik_providers_google_workspace.googleworkspaceprovidermapping
|
||||||
- authentik_providers_ldap.ldapprovider
|
- authentik_providers_ldap.ldapprovider
|
||||||
@ -25113,6 +25114,7 @@ paths:
|
|||||||
- authentik_policies_password.passwordpolicy
|
- authentik_policies_password.passwordpolicy
|
||||||
- authentik_policies_reputation.reputationpolicy
|
- authentik_policies_reputation.reputationpolicy
|
||||||
- authentik_policies_unique_password.uniquepasswordpolicy
|
- authentik_policies_unique_password.uniquepasswordpolicy
|
||||||
|
- authentik_providers_apple_psso.appleplatformssoprovider
|
||||||
- authentik_providers_google_workspace.googleworkspaceprovider
|
- authentik_providers_google_workspace.googleworkspaceprovider
|
||||||
- authentik_providers_google_workspace.googleworkspaceprovidermapping
|
- authentik_providers_google_workspace.googleworkspaceprovidermapping
|
||||||
- authentik_providers_ldap.ldapprovider
|
- authentik_providers_ldap.ldapprovider
|
||||||
@ -41212,6 +41214,7 @@ components:
|
|||||||
- authentik.enterprise
|
- authentik.enterprise
|
||||||
- authentik.enterprise.audit
|
- authentik.enterprise.audit
|
||||||
- authentik.enterprise.policies.unique_password
|
- authentik.enterprise.policies.unique_password
|
||||||
|
- authentik.enterprise.providers.apple_psso
|
||||||
- authentik.enterprise.providers.google_workspace
|
- authentik.enterprise.providers.google_workspace
|
||||||
- authentik.enterprise.providers.microsoft_entra
|
- authentik.enterprise.providers.microsoft_entra
|
||||||
- authentik.enterprise.providers.ssf
|
- authentik.enterprise.providers.ssf
|
||||||
@ -41258,6 +41261,15 @@ components:
|
|||||||
- redirect_uri
|
- redirect_uri
|
||||||
- scope
|
- scope
|
||||||
- state
|
- state
|
||||||
|
ApplePlatformSSOProviderRequest:
|
||||||
|
type: object
|
||||||
|
description: ApplePlatformSSOProvider Serializer
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
minLength: 1
|
||||||
|
required:
|
||||||
|
- name
|
||||||
Application:
|
Application:
|
||||||
type: object
|
type: object
|
||||||
description: Application Serializer
|
description: Application Serializer
|
||||||
@ -48714,6 +48726,7 @@ components:
|
|||||||
- authentik_core.token
|
- authentik_core.token
|
||||||
- authentik_enterprise.license
|
- authentik_enterprise.license
|
||||||
- authentik_policies_unique_password.uniquepasswordpolicy
|
- authentik_policies_unique_password.uniquepasswordpolicy
|
||||||
|
- authentik_providers_apple_psso.appleplatformssoprovider
|
||||||
- authentik_providers_google_workspace.googleworkspaceprovider
|
- authentik_providers_google_workspace.googleworkspaceprovider
|
||||||
- authentik_providers_google_workspace.googleworkspaceprovidermapping
|
- authentik_providers_google_workspace.googleworkspaceprovidermapping
|
||||||
- authentik_providers_microsoft_entra.microsoftentraprovider
|
- authentik_providers_microsoft_entra.microsoftentraprovider
|
||||||
@ -56659,6 +56672,7 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
ProviderModelEnum:
|
ProviderModelEnum:
|
||||||
enum:
|
enum:
|
||||||
|
- authentik_providers_apple_psso.appleplatformssoprovider
|
||||||
- authentik_providers_google_workspace.googleworkspaceprovider
|
- authentik_providers_google_workspace.googleworkspaceprovider
|
||||||
- authentik_providers_ldap.ldapprovider
|
- authentik_providers_ldap.ldapprovider
|
||||||
- authentik_providers_microsoft_entra.microsoftentraprovider
|
- authentik_providers_microsoft_entra.microsoftentraprovider
|
||||||
@ -61871,6 +61885,7 @@ components:
|
|||||||
- worker_id
|
- worker_id
|
||||||
modelRequest:
|
modelRequest:
|
||||||
oneOf:
|
oneOf:
|
||||||
|
- $ref: '#/components/schemas/ApplePlatformSSOProviderRequest'
|
||||||
- $ref: '#/components/schemas/GoogleWorkspaceProviderRequest'
|
- $ref: '#/components/schemas/GoogleWorkspaceProviderRequest'
|
||||||
- $ref: '#/components/schemas/LDAPProviderRequest'
|
- $ref: '#/components/schemas/LDAPProviderRequest'
|
||||||
- $ref: '#/components/schemas/MicrosoftEntraProviderRequest'
|
- $ref: '#/components/schemas/MicrosoftEntraProviderRequest'
|
||||||
@ -61884,6 +61899,7 @@ components:
|
|||||||
discriminator:
|
discriminator:
|
||||||
propertyName: provider_model
|
propertyName: provider_model
|
||||||
mapping:
|
mapping:
|
||||||
|
authentik_providers_apple_psso.appleplatformssoprovider: '#/components/schemas/ApplePlatformSSOProviderRequest'
|
||||||
authentik_providers_google_workspace.googleworkspaceprovider: '#/components/schemas/GoogleWorkspaceProviderRequest'
|
authentik_providers_google_workspace.googleworkspaceprovider: '#/components/schemas/GoogleWorkspaceProviderRequest'
|
||||||
authentik_providers_ldap.ldapprovider: '#/components/schemas/LDAPProviderRequest'
|
authentik_providers_ldap.ldapprovider: '#/components/schemas/LDAPProviderRequest'
|
||||||
authentik_providers_microsoft_entra.microsoftentraprovider: '#/components/schemas/MicrosoftEntraProviderRequest'
|
authentik_providers_microsoft_entra.microsoftentraprovider: '#/components/schemas/MicrosoftEntraProviderRequest'
|
||||||
|
Reference in New Issue
Block a user