authentik.enterprise.endpoints.connectors.agent.tests.test_apple_token

  1from cryptography.hazmat.primitives import serialization
  2from cryptography.hazmat.primitives.asymmetric import ec
  3from django.test import TestCase
  4from django.urls import reverse
  5from jwt import encode
  6
  7from authentik.blueprints.tests import reconcile_app
  8from authentik.core.tests.utils import create_test_cert, create_test_user
  9from authentik.crypto.builder import PrivateKeyAlg
 10from authentik.endpoints.connectors.agent.models import (
 11    AgentConnector,
 12    AgentDeviceConnection,
 13    AgentDeviceUserBinding,
 14    AppleIndependentSecureEnclave,
 15    AppleNonce,
 16    DeviceToken,
 17    EnrollmentToken,
 18)
 19from authentik.endpoints.models import Device
 20from authentik.events.models import Event, EventAction
 21from authentik.lib.generators import generate_id
 22from authentik.providers.oauth2.models import JWTAlgorithms
 23
 24
 25class TestAppleToken(TestCase):
 26
 27    def setUp(self):
 28        self.apple_sign_key = create_test_cert(PrivateKeyAlg.ECDSA)
 29        self.sign_key_pem = self.apple_sign_key.public_key.public_bytes(
 30            encoding=serialization.Encoding.PEM,
 31            format=serialization.PublicFormat.SubjectPublicKeyInfo,
 32        ).decode()
 33
 34        self.enc_key = ec.generate_private_key(curve=ec.SECP256R1())
 35        self.enc_pub = (
 36            self.enc_key.public_key()
 37            .public_bytes(
 38                encoding=serialization.Encoding.PEM,
 39                format=serialization.PublicFormat.SubjectPublicKeyInfo,
 40            )
 41            .decode()
 42        )
 43
 44        self.connector = AgentConnector.objects.create(name=generate_id())
 45        self.token = EnrollmentToken.objects.create(name=generate_id(), connector=self.connector)
 46        self.device = Device.objects.create(
 47            name=generate_id(),
 48            identifier=generate_id(),
 49        )
 50        self.connection = AgentDeviceConnection.objects.create(
 51            device=self.device,
 52            connector=self.connector,
 53            apple_sign_key_id=self.apple_sign_key.kid,
 54            apple_signing_key=self.sign_key_pem,
 55            apple_encryption_key=self.enc_pub,
 56        )
 57        self.user = create_test_user()
 58        AgentDeviceUserBinding.objects.create(
 59            target=self.device,
 60            user=self.user,
 61            order=0,
 62            apple_enclave_key_id=self.apple_sign_key.kid,
 63            apple_secure_enclave_key=self.sign_key_pem,
 64        )
 65        self.device_token = DeviceToken.objects.create(device=self.connection)
 66
 67    @reconcile_app("authentik_crypto")
 68    def test_token(self):
 69        nonce = generate_id()
 70        AppleNonce.objects.create(
 71            device_token=self.device_token,
 72            nonce=nonce,
 73        )
 74        embedded = encode(
 75            {"iss": str(self.connector.pk), "aud": str(self.device.pk), "request_nonce": nonce},
 76            self.apple_sign_key.private_key,
 77            headers={
 78                "kid": self.apple_sign_key.kid,
 79            },
 80            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
 81        )
 82        assertion = encode(
 83            {
 84                "iss": str(self.connector.pk),
 85                "aud": "http://testserver/endpoints/agent/psso/token/",
 86                "request_nonce": nonce,
 87                "assertion": embedded,
 88                "jwe_crypto": {
 89                    "apv": (
 90                        "AAAABUFwcGxlAAAAQQTFgZOospN6KbkhXhx1lfa-AKYxjEfJhTJrkpdEY_srMmkPzS7VN0Bzt2AtNBEXE"
 91                        "aphDONiP2Mq6Oxytv5JKOxHAAAAJDgyOThERkY5LTVFMUUtNEUwMS04OEUwLUI3QkQzOUM4QjA3Qw"
 92                    )
 93                },
 94            },
 95            self.apple_sign_key.private_key,
 96            headers={
 97                "kid": self.apple_sign_key.kid,
 98            },
 99            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
100        )
101        res = self.client.post(
102            reverse("authentik_enterprise_endpoints_connectors_agent:psso-token"),
103            data={
104                "assertion": assertion,
105                "platform_sso_version": "1.0",
106                "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
107            },
108        )
109
110        self.assertEqual(res.status_code, 200)
111        event = Event.objects.filter(
112            action=EventAction.LOGIN,
113            app="authentik.endpoints.connectors.agent",
114        ).first()
115        self.assertIsNotNone(event)
116        self.assertEqual(event.context["device"]["name"], self.device.name)
117
118    @reconcile_app("authentik_crypto")
119    def test_token_independent(self):
120        nonce = generate_id()
121
122        AgentDeviceUserBinding.objects.all().delete()
123        AppleIndependentSecureEnclave.objects.create(
124            user=self.user,
125            apple_enclave_key_id=self.apple_sign_key.kid,
126            apple_secure_enclave_key=self.sign_key_pem,
127        )
128
129        AppleNonce.objects.create(
130            device_token=self.device_token,
131            nonce=nonce,
132        )
133        embedded = encode(
134            {"iss": str(self.connector.pk), "aud": str(self.device.pk), "request_nonce": nonce},
135            self.apple_sign_key.private_key,
136            headers={
137                "kid": self.apple_sign_key.kid,
138            },
139            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
140        )
141        assertion = encode(
142            {
143                "iss": str(self.connector.pk),
144                "aud": "http://testserver/endpoints/agent/psso/token/",
145                "request_nonce": nonce,
146                "assertion": embedded,
147                "jwe_crypto": {
148                    "apv": (
149                        "AAAABUFwcGxlAAAAQQTFgZOospN6KbkhXhx1lfa-AKYxjEfJhTJrkpdEY_srMmkPzS7VN0Bzt2AtNBEXE"
150                        "aphDONiP2Mq6Oxytv5JKOxHAAAAJDgyOThERkY5LTVFMUUtNEUwMS04OEUwLUI3QkQzOUM4QjA3Qw"
151                    )
152                },
153            },
154            self.apple_sign_key.private_key,
155            headers={
156                "kid": self.apple_sign_key.kid,
157            },
158            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
159        )
160        res = self.client.post(
161            reverse("authentik_enterprise_endpoints_connectors_agent:psso-token"),
162            data={
163                "assertion": assertion,
164                "platform_sso_version": "1.0",
165                "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
166            },
167        )
168
169        self.assertEqual(res.status_code, 200)
170        event = Event.objects.filter(
171            action=EventAction.LOGIN,
172            app="authentik.endpoints.connectors.agent",
173        ).first()
174        self.assertIsNotNone(event)
175        self.assertEqual(event.context["device"]["name"], self.device.name)
class TestAppleToken(django.test.testcases.TestCase):
 26class TestAppleToken(TestCase):
 27
 28    def setUp(self):
 29        self.apple_sign_key = create_test_cert(PrivateKeyAlg.ECDSA)
 30        self.sign_key_pem = self.apple_sign_key.public_key.public_bytes(
 31            encoding=serialization.Encoding.PEM,
 32            format=serialization.PublicFormat.SubjectPublicKeyInfo,
 33        ).decode()
 34
 35        self.enc_key = ec.generate_private_key(curve=ec.SECP256R1())
 36        self.enc_pub = (
 37            self.enc_key.public_key()
 38            .public_bytes(
 39                encoding=serialization.Encoding.PEM,
 40                format=serialization.PublicFormat.SubjectPublicKeyInfo,
 41            )
 42            .decode()
 43        )
 44
 45        self.connector = AgentConnector.objects.create(name=generate_id())
 46        self.token = EnrollmentToken.objects.create(name=generate_id(), connector=self.connector)
 47        self.device = Device.objects.create(
 48            name=generate_id(),
 49            identifier=generate_id(),
 50        )
 51        self.connection = AgentDeviceConnection.objects.create(
 52            device=self.device,
 53            connector=self.connector,
 54            apple_sign_key_id=self.apple_sign_key.kid,
 55            apple_signing_key=self.sign_key_pem,
 56            apple_encryption_key=self.enc_pub,
 57        )
 58        self.user = create_test_user()
 59        AgentDeviceUserBinding.objects.create(
 60            target=self.device,
 61            user=self.user,
 62            order=0,
 63            apple_enclave_key_id=self.apple_sign_key.kid,
 64            apple_secure_enclave_key=self.sign_key_pem,
 65        )
 66        self.device_token = DeviceToken.objects.create(device=self.connection)
 67
 68    @reconcile_app("authentik_crypto")
 69    def test_token(self):
 70        nonce = generate_id()
 71        AppleNonce.objects.create(
 72            device_token=self.device_token,
 73            nonce=nonce,
 74        )
 75        embedded = encode(
 76            {"iss": str(self.connector.pk), "aud": str(self.device.pk), "request_nonce": nonce},
 77            self.apple_sign_key.private_key,
 78            headers={
 79                "kid": self.apple_sign_key.kid,
 80            },
 81            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
 82        )
 83        assertion = encode(
 84            {
 85                "iss": str(self.connector.pk),
 86                "aud": "http://testserver/endpoints/agent/psso/token/",
 87                "request_nonce": nonce,
 88                "assertion": embedded,
 89                "jwe_crypto": {
 90                    "apv": (
 91                        "AAAABUFwcGxlAAAAQQTFgZOospN6KbkhXhx1lfa-AKYxjEfJhTJrkpdEY_srMmkPzS7VN0Bzt2AtNBEXE"
 92                        "aphDONiP2Mq6Oxytv5JKOxHAAAAJDgyOThERkY5LTVFMUUtNEUwMS04OEUwLUI3QkQzOUM4QjA3Qw"
 93                    )
 94                },
 95            },
 96            self.apple_sign_key.private_key,
 97            headers={
 98                "kid": self.apple_sign_key.kid,
 99            },
100            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
101        )
102        res = self.client.post(
103            reverse("authentik_enterprise_endpoints_connectors_agent:psso-token"),
104            data={
105                "assertion": assertion,
106                "platform_sso_version": "1.0",
107                "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
108            },
109        )
110
111        self.assertEqual(res.status_code, 200)
112        event = Event.objects.filter(
113            action=EventAction.LOGIN,
114            app="authentik.endpoints.connectors.agent",
115        ).first()
116        self.assertIsNotNone(event)
117        self.assertEqual(event.context["device"]["name"], self.device.name)
118
119    @reconcile_app("authentik_crypto")
120    def test_token_independent(self):
121        nonce = generate_id()
122
123        AgentDeviceUserBinding.objects.all().delete()
124        AppleIndependentSecureEnclave.objects.create(
125            user=self.user,
126            apple_enclave_key_id=self.apple_sign_key.kid,
127            apple_secure_enclave_key=self.sign_key_pem,
128        )
129
130        AppleNonce.objects.create(
131            device_token=self.device_token,
132            nonce=nonce,
133        )
134        embedded = encode(
135            {"iss": str(self.connector.pk), "aud": str(self.device.pk), "request_nonce": nonce},
136            self.apple_sign_key.private_key,
137            headers={
138                "kid": self.apple_sign_key.kid,
139            },
140            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
141        )
142        assertion = encode(
143            {
144                "iss": str(self.connector.pk),
145                "aud": "http://testserver/endpoints/agent/psso/token/",
146                "request_nonce": nonce,
147                "assertion": embedded,
148                "jwe_crypto": {
149                    "apv": (
150                        "AAAABUFwcGxlAAAAQQTFgZOospN6KbkhXhx1lfa-AKYxjEfJhTJrkpdEY_srMmkPzS7VN0Bzt2AtNBEXE"
151                        "aphDONiP2Mq6Oxytv5JKOxHAAAAJDgyOThERkY5LTVFMUUtNEUwMS04OEUwLUI3QkQzOUM4QjA3Qw"
152                    )
153                },
154            },
155            self.apple_sign_key.private_key,
156            headers={
157                "kid": self.apple_sign_key.kid,
158            },
159            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
160        )
161        res = self.client.post(
162            reverse("authentik_enterprise_endpoints_connectors_agent:psso-token"),
163            data={
164                "assertion": assertion,
165                "platform_sso_version": "1.0",
166                "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
167            },
168        )
169
170        self.assertEqual(res.status_code, 200)
171        event = Event.objects.filter(
172            action=EventAction.LOGIN,
173            app="authentik.endpoints.connectors.agent",
174        ).first()
175        self.assertIsNotNone(event)
176        self.assertEqual(event.context["device"]["name"], self.device.name)

Similar to TransactionTestCase, but use transaction.atomic() to achieve test isolation.

In most situations, TestCase should be preferred to TransactionTestCase as it allows faster execution. However, there are some situations where using TransactionTestCase might be necessary (e.g. testing some transactional behavior).

On database backends with no transaction support, TestCase behaves as TransactionTestCase.

def setUp(self):
28    def setUp(self):
29        self.apple_sign_key = create_test_cert(PrivateKeyAlg.ECDSA)
30        self.sign_key_pem = self.apple_sign_key.public_key.public_bytes(
31            encoding=serialization.Encoding.PEM,
32            format=serialization.PublicFormat.SubjectPublicKeyInfo,
33        ).decode()
34
35        self.enc_key = ec.generate_private_key(curve=ec.SECP256R1())
36        self.enc_pub = (
37            self.enc_key.public_key()
38            .public_bytes(
39                encoding=serialization.Encoding.PEM,
40                format=serialization.PublicFormat.SubjectPublicKeyInfo,
41            )
42            .decode()
43        )
44
45        self.connector = AgentConnector.objects.create(name=generate_id())
46        self.token = EnrollmentToken.objects.create(name=generate_id(), connector=self.connector)
47        self.device = Device.objects.create(
48            name=generate_id(),
49            identifier=generate_id(),
50        )
51        self.connection = AgentDeviceConnection.objects.create(
52            device=self.device,
53            connector=self.connector,
54            apple_sign_key_id=self.apple_sign_key.kid,
55            apple_signing_key=self.sign_key_pem,
56            apple_encryption_key=self.enc_pub,
57        )
58        self.user = create_test_user()
59        AgentDeviceUserBinding.objects.create(
60            target=self.device,
61            user=self.user,
62            order=0,
63            apple_enclave_key_id=self.apple_sign_key.kid,
64            apple_secure_enclave_key=self.sign_key_pem,
65        )
66        self.device_token = DeviceToken.objects.create(device=self.connection)

Hook method for setting up the test fixture before exercising it.

@reconcile_app('authentik_crypto')
def test_token(self):
 68    @reconcile_app("authentik_crypto")
 69    def test_token(self):
 70        nonce = generate_id()
 71        AppleNonce.objects.create(
 72            device_token=self.device_token,
 73            nonce=nonce,
 74        )
 75        embedded = encode(
 76            {"iss": str(self.connector.pk), "aud": str(self.device.pk), "request_nonce": nonce},
 77            self.apple_sign_key.private_key,
 78            headers={
 79                "kid": self.apple_sign_key.kid,
 80            },
 81            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
 82        )
 83        assertion = encode(
 84            {
 85                "iss": str(self.connector.pk),
 86                "aud": "http://testserver/endpoints/agent/psso/token/",
 87                "request_nonce": nonce,
 88                "assertion": embedded,
 89                "jwe_crypto": {
 90                    "apv": (
 91                        "AAAABUFwcGxlAAAAQQTFgZOospN6KbkhXhx1lfa-AKYxjEfJhTJrkpdEY_srMmkPzS7VN0Bzt2AtNBEXE"
 92                        "aphDONiP2Mq6Oxytv5JKOxHAAAAJDgyOThERkY5LTVFMUUtNEUwMS04OEUwLUI3QkQzOUM4QjA3Qw"
 93                    )
 94                },
 95            },
 96            self.apple_sign_key.private_key,
 97            headers={
 98                "kid": self.apple_sign_key.kid,
 99            },
100            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
101        )
102        res = self.client.post(
103            reverse("authentik_enterprise_endpoints_connectors_agent:psso-token"),
104            data={
105                "assertion": assertion,
106                "platform_sso_version": "1.0",
107                "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
108            },
109        )
110
111        self.assertEqual(res.status_code, 200)
112        event = Event.objects.filter(
113            action=EventAction.LOGIN,
114            app="authentik.endpoints.connectors.agent",
115        ).first()
116        self.assertIsNotNone(event)
117        self.assertEqual(event.context["device"]["name"], self.device.name)
@reconcile_app('authentik_crypto')
def test_token_independent(self):
119    @reconcile_app("authentik_crypto")
120    def test_token_independent(self):
121        nonce = generate_id()
122
123        AgentDeviceUserBinding.objects.all().delete()
124        AppleIndependentSecureEnclave.objects.create(
125            user=self.user,
126            apple_enclave_key_id=self.apple_sign_key.kid,
127            apple_secure_enclave_key=self.sign_key_pem,
128        )
129
130        AppleNonce.objects.create(
131            device_token=self.device_token,
132            nonce=nonce,
133        )
134        embedded = encode(
135            {"iss": str(self.connector.pk), "aud": str(self.device.pk), "request_nonce": nonce},
136            self.apple_sign_key.private_key,
137            headers={
138                "kid": self.apple_sign_key.kid,
139            },
140            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
141        )
142        assertion = encode(
143            {
144                "iss": str(self.connector.pk),
145                "aud": "http://testserver/endpoints/agent/psso/token/",
146                "request_nonce": nonce,
147                "assertion": embedded,
148                "jwe_crypto": {
149                    "apv": (
150                        "AAAABUFwcGxlAAAAQQTFgZOospN6KbkhXhx1lfa-AKYxjEfJhTJrkpdEY_srMmkPzS7VN0Bzt2AtNBEXE"
151                        "aphDONiP2Mq6Oxytv5JKOxHAAAAJDgyOThERkY5LTVFMUUtNEUwMS04OEUwLUI3QkQzOUM4QjA3Qw"
152                    )
153                },
154            },
155            self.apple_sign_key.private_key,
156            headers={
157                "kid": self.apple_sign_key.kid,
158            },
159            algorithm=JWTAlgorithms.from_private_key(self.apple_sign_key.private_key),
160        )
161        res = self.client.post(
162            reverse("authentik_enterprise_endpoints_connectors_agent:psso-token"),
163            data={
164                "assertion": assertion,
165                "platform_sso_version": "1.0",
166                "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
167            },
168        )
169
170        self.assertEqual(res.status_code, 200)
171        event = Event.objects.filter(
172            action=EventAction.LOGIN,
173            app="authentik.endpoints.connectors.agent",
174        ).first()
175        self.assertIsNotNone(event)
176        self.assertEqual(event.context["device"]["name"], self.device.name)