Skip to content

Commit fa62a46

Browse files
committed
[wip]
1 parent d895490 commit fa62a46

1 file changed

Lines changed: 116 additions & 16 deletions

File tree

docs/kratos/passwordless/08_deviceauthn.mdx

Lines changed: 116 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Currently, this authentication strategy can only be used as a second factor. It
1616

1717
## Short summary
1818

19-
- This is implemented with the strategy `DeviceAuthn`, in spirit similar to `WebAuthn`
19+
- This is implemented in the OEL version with the strategy `DeviceAuthn`, in spirit similar to `WebAuthn`
2020
- The settings flow is used to manage keys (create, delete)
2121
- The login flow is used to step-up the AAL to AAL2, or in the future AAL3
2222
- Using the admin API, it is possible to delete all keys for a device on behalf of the user in case of theft or loss
@@ -35,7 +35,107 @@ Currently, this authentication strategy can only be used as a second factor. It
3535
- TEE: Trusted Execution Environment
3636
- CA: Certificate Authority
3737

38-
## Enrollment
38+
## Guides
39+
40+
### How to implement Device Binding in your Android application
41+
42+
We recommend using the Ory Java SDK to communicate with Kratos, although this is not required. Code snippets here use this SDK, and are written in Kotlin.
43+
44+
Since Device Binding only is supported on native devices (not in the browser), all corresponding API calls should be done using the endpoints for native apps, to avoid having to pass cookies around manually.
45+
46+
1. Ensure that the `DeviceAuthn` strategy is enabled in the Kratos configuration. This strategy implements the settings and login flow. This is done so:
47+
```yaml
48+
selfservice:
49+
methods:
50+
deviceauthn:
51+
enabled: true
52+
```
53+
1. Implement a runtime check for the Android version. If is lower than 24, Device Binding may not be used, and a fallback should be found, for example using passkeys.
54+
1. Device Binding is (currently) only a second factor, the UI should only show existing Device Binding keys and related buttons (e.g. to add a key) if the user is currently logged in. This can be confirmed with a `whoami` call.
55+
1. Create a [settings flow for native apps](https://www.ory.com/docs/reference/api#tag/frontend/operation/updateSettingsFlow). The response contains the list of existing Device Binding keys.
56+
1. To delete an existing key, [complete the settings flow](https://www.ory.com/docs/reference/api#tag/frontend/operation/updateSettingsFlow) like this:
57+
```kotlin
58+
val clientKeyIdToDelete = "..."
59+
60+
val apiClient = Configuration.getDefaultApiClient()
61+
val apiInstance = FrontendApi(apiClien)
62+
val body = UpdateSettingsFlowBody()
63+
val method = UpdateSettingsFlowWithDeviceAuthnMethod()
64+
method.method = "deviceauthn"
65+
method.delete = UpdateSettingsFlowWithDeviceAuthnMethodDelete()
66+
method.delete!!.clientKeyId = clientKeyIdToDelete
67+
body.actualInstance = method
68+
val updatedFlow = apiInstance.updateSettingsFlow(settingsFlow?.id, body, sessionToken, "")
69+
```
70+
Once the key has been deleted server-side, it is fine (although not required) to also delete it on the device using the KeyStore API.
71+
1. To add a new key, [complete the settings flow](https://www.ory.com/docs/reference/api#tag/frontend/operation/updateSettingsFlow) like this:
72+
```kotlin
73+
val apiClient = Configuration.getDefaultApiClient()
74+
val withStrongbox = false // Better: Detect the presence of StrongBox at runtime.
75+
76+
val keyAlias = UUID.randomUUID().toString()
77+
val nonce = extractNonceFromUiNodes(settingsFlow?.ui?.nodes ?: emptyList())
78+
if (nonce == null) {
79+
throw Exception("No nonce found in UI. Is DeviceAuthn enabled server-side?")
80+
}
81+
val keyCertChain = Api.create().createKeyPair(keyAlias, nonce, withStrongbox)
82+
val apiInstance = FrontendApi(apiClient)
83+
val body = UpdateSettingsFlowBody()
84+
val method = UpdateSettingsFlowWithDeviceAuthnMethod()
85+
method.method = "deviceauthn"
86+
method.add = UpdateSettingsFlowWithDeviceAuthnMethodAdd()
87+
method.add!!.deviceName = "My work phone"
88+
method.add!!.clientKeyId = keyAlias
89+
method.add!!.certificateChainAndroid = keyCertChain.map { it.encoded }.toList()
90+
body.actualInstance = method
91+
val updatedFlow = apiInstance.updateSettingsFlow(settingsFlow?.id, body, sessionToken, "")
92+
```
93+
Once a key is created, the KeyStore APIs can be used to list all keys, query a key using its id, etc. However we recommend that the application keeps track of what keys were created to know which ones can be used on the device, compared to which keys belong to the same identity but reside on other devices.
94+
Note that there is a maximum number of keys that can be created for an identity, and there is no point to create multiple keys for the same user on the same device, even though the server allows it.
95+
1. To use a key to step-up the AAL, [complete the settings flow](https://www.ory.com/docs/reference/api#tag/frontend/operation/updateSettingsFlow) like this:
96+
```kotlin
97+
val clientKeyId = "..."
98+
val nonce = extractNonceFromUiNodes(flow?.ui?.nodes ?: emptyList())
99+
if (nonce == null) {
100+
throw Exception("No nonce found in UI")
101+
}
102+
103+
val updateMethod = UpdateLoginFlowWithDeviceAuthnMethod()
104+
updateMethod.clientKeyId = clientKeyId
105+
updateMethod.method = "deviceauthn"
106+
updateMethod.signature = Api.create().launchBiometricSigner(
107+
context as FragmentActivity,
108+
clientKeyId,
109+
nonce,
110+
"Confirm",
111+
"Cancel"
112+
)
113+
114+
val updateBody = UpdateLoginFlowBody()
115+
updateBody.actualInstance = updateMethod
116+
117+
val apiClient = Configuration.getDefaultApiClient()
118+
119+
withContext(Dispatchers.IO) {
120+
val apiInstance = FrontendApi(apiClient)
121+
val res = apiInstance.updateLoginFlow(
122+
/* flow = */ flow.id,
123+
/* updateLoginFlowBody = */ updateBody,
124+
/* xSessionToken = */ sessionToken,
125+
/* cookie = */ ""
126+
)
127+
}
128+
```
129+
130+
When running Kratos in development mode, some server-side checks are relaxed, which allows for using the Android emulator to create and use keys. The Android emulator create keys in software.
131+
132+
When running Kratos in production mode, only hardware-resident keys are accepted, and thus the Android emulator cannot be used to create or use keys.
133+
134+
135+
136+
## Reference
137+
138+
### Enrollment
39139

40140
1. The `DeviceAuthn` strategy is enabled in the Kratos configuration. This strategy implements the settings and login flow. This is done so:
41141
```yaml
@@ -63,35 +163,35 @@ Currently, this authentication strategy can only be used as a second factor. It
63163

64164
At this point the key is enrolled for the identity.
65165

66-
## Proof of device enrollment
166+
### Proof of device enrollment
67167

68168
1. When the user creates the login flow with the DeviceAuthn strategy, the client receives a server challenge.
69169
2. Using the private key in the hardware of the device, the client signs the server challenge using ECDSA. The signature is only emitted after a biometric/PIN prompt has been passed. The client then sends the signature to the server using the login flow update endpoint.
70170
4. The server:
71-
2. Checks that the signature is valid using the recorded public key in the database
72-
3. Checks that no CA in the certificate chain (when the device has been enrolled) has been revoked
73-
9. Erases the challenge value in the database to prevent re-use.
74-
6. Replies with 200 with a fresh session token and a higher AAL e.g. AAL2 or AAL3
171+
1. Checks that the signature is valid using the recorded public key in the database
172+
1. Checks that no CA in the certificate chain (when the device has been enrolled) has been revoked
173+
1. Erases the challenge value in the database to prevent re-use.
174+
1. Replies with 200 with a fresh session token and a higher AAL e.g. AAL2 or AAL3
75175

76-
## Key Revocation
176+
### Key Revocation
77177

78178
- The user can revoke a key themselves (e.g. because the device is stolen, lost, broken, etc) using the settings flow.
79179
This action can be done from any device (e.g. from the browser), as it is the case for other methods e.g. WebAuthn.
80180
- An admin using the admin API can revoke all keys on a device on behalf of the user. This is useful when the user only owns one device which is the one that should be revoked (e.g. one mobile phone) and which has been lost/stolen
81181

82182
Revocation is done by removing the key from the database.
83183

84-
## Device list
184+
### Device list
85185

86186
The settings flow contains all keys for the identity. This is used to present the list of keys (including device name) in the UI.
87187

88-
## Key lifecycle on the device
188+
### Key lifecycle on the device
89189

90190
- Creation: When the device enrollment process is started for the user
91191
- Deletion:
92192
- When the app is uninstalled or when the phone is reset, the mobile OSes automatically remove all keys for the app. This means that if the device was enrolled, the public key subsists server-side but the private key does not exist anymore, so no one can sign any challenge for this public key. This database entry is thus useless, but poses no security risks.
93193

94-
## Cryptography
194+
### Cryptography
95195

96196
The security of this design relies on a chain of trust anchored in hardware and standard cryptographic primitives.
97197

@@ -101,7 +201,7 @@ The security of this design relies on a chain of trust anchored in hardware and
101201
- **Certificate Chains**: **X.509 certificates** are used to establish the chain of trust. The device's attestation is signed by a key that is, in turn, certified by a platform authority (Apple or Google), ensuring the attestation's authenticity.
102202
- **No configurability**: Intentionally, for simplicity, performance, auditability, and to avoid downgrade attacks, all cryptographic primitives are fixed.
103203

104-
## Attack Surface and Mitigations
204+
### Attack Surface and Mitigations
105205

106206

107207

@@ -159,15 +259,15 @@ The security of this design relies on a chain of trust anchored in hardware and
159259
- **Mitigation**:
160260
- **Challenge bound to the identity id**: The challenge is bound to the identity in the database (stored in the same row). Since the identity is detected from the session token, an attacker cannot tamper with the identity id (unless they steal the session token, at which point they *are* the user, from the server perspective).
161261

162-
## Comparison with WebAuthn and Passkeys
262+
### Comparison with WebAuthn and Passkeys
163263

164264
It is useful to compare this custom implementation with the FIDO WebAuthn standard and the user-facing concept of Passkeys. While they share core cryptographic principles, their goals and scope are fundamentally different.
165265

166-
### Similarities
266+
#### Similarities
167267

168268
- **Core Cryptography**: Both approaches are built on public-key cryptography (typically ECDSA), and use a challenge-response protocol
169269

170-
### Key Differences
270+
#### Key Differences
171271

172272
- **Standard vs. Proprietary**
173273
- **WebAuthn/Passkeys**: An open, interoperable standard from the W3C and FIDO Alliance, designed to work across different websites, apps, browsers, and operating systems.
@@ -179,7 +279,7 @@ It is useful to compare this custom implementation with the FIDO WebAuthn standa
179279
- **WebAuthn/Passkeys**: Attestation is an **optional** feature. While a server can request it to verify the properties of an authenticator, many services skip it in favor of a simpler user experience. The focus is on proving possession of the key, not on scrutinizing the device itself.
180280
- **This Design**: Attestation is **mandatory and central** to the entire security model. The main purpose of the enrollment ceremony is for the server to validate the device's hardware and software integrity.
181281

182-
## References
282+
### Reference documentation
183283

184284
- Android: [https://developer.android.com/privacy-and-security/security-key-attestation](https://developer.android.com/privacy-and-security/security-key-attestation)
185285
- iOS: [https://developer.apple.com/documentation/devicecheck/validating-apps-that-connect-to-your-server](https://developer.apple.com/documentation/devicecheck/validating-apps-that-connect-to-your-server) and [https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)

0 commit comments

Comments
 (0)