react-native-dpop 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -36
- package/ReactNativeDPoP.podspec +1 -1
- package/android/src/main/java/com/reactnativedpop/DPoPKeyStore.kt +49 -0
- package/android/src/main/java/com/reactnativedpop/DPoPModule.kt +63 -10
- package/android/src/main/java/com/reactnativedpop/DPoPPackage.kt +7 -7
- package/ios/DPoPKeyStore.swift +55 -11
- package/ios/DPoPModule.swift +64 -6
- package/ios/DPoPModuleBridge.mm +1 -1
- package/ios/SecureEnclaveKeyStore.swift +15 -0
- package/lib/module/NativeReactNativeDPoP.js +3 -1
- package/lib/module/NativeReactNativeDPoP.js.map +1 -1
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/examples/shared/DPoPExampleContent.d.ts +2 -0
- package/lib/typescript/examples/shared/DPoPExampleContent.d.ts.map +1 -0
- package/lib/typescript/examples/v0.75/App.d.ts +2 -0
- package/lib/typescript/examples/v0.75/App.d.ts.map +1 -0
- package/lib/typescript/examples/v0.83/App.d.ts +2 -0
- package/lib/typescript/examples/v0.83/App.d.ts.map +1 -0
- package/lib/typescript/src/NativeReactNativeDPoP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +20 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +10 -5
- package/src/NativeReactNativeDPoP.ts +2 -1
- package/src/index.tsx +23 -5
package/README.md
CHANGED
|
@@ -4,18 +4,19 @@ React Native library for DPoP proof generation and key management.
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- Generate DPoP proofs (`dpop+jwt`) signed with ES256
|
|
8
|
-
- Manage key pairs in the
|
|
9
|
-
- Export public key
|
|
10
|
-
- Calculate JWK
|
|
11
|
-
- Verify
|
|
12
|
-
- Retrieve non-sensitive key metadata
|
|
13
|
-
-
|
|
7
|
+
- Generate DPoP proofs (`dpop+jwt`) signed with ES256
|
|
8
|
+
- Manage key pairs in the platform keystore
|
|
9
|
+
- Export the public key as `JWK`, `DER`, or `RAW`
|
|
10
|
+
- Calculate JWK thumbprints (`SHA-256`, base64url)
|
|
11
|
+
- Verify whether a proof is bound to a given key alias
|
|
12
|
+
- Retrieve non-sensitive key metadata, including secure hardware details
|
|
13
|
+
- Use Secure Enclave on iOS when available, with Keychain fallback
|
|
14
|
+
- Prefer StrongBox on Android when available, with hardware-backed fallback
|
|
14
15
|
|
|
15
|
-
## Platform
|
|
16
|
+
## Platform support
|
|
16
17
|
|
|
17
|
-
- Android
|
|
18
|
-
- iOS
|
|
18
|
+
- Android
|
|
19
|
+
- iOS
|
|
19
20
|
|
|
20
21
|
## Installation
|
|
21
22
|
|
|
@@ -23,13 +24,13 @@ React Native library for DPoP proof generation and key management.
|
|
|
23
24
|
npm install react-native-dpop
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
For iOS
|
|
27
|
+
For iOS:
|
|
27
28
|
|
|
28
29
|
```sh
|
|
29
30
|
cd ios && pod install
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
## Quick
|
|
33
|
+
## Quick start
|
|
33
34
|
|
|
34
35
|
```ts
|
|
35
36
|
import { DPoP } from 'react-native-dpop';
|
|
@@ -38,26 +39,18 @@ const dpop = await DPoP.generateProof({
|
|
|
38
39
|
htu: 'https://api.example.com/token',
|
|
39
40
|
htm: 'POST',
|
|
40
41
|
accessToken: 'ACCESS_TOKEN',
|
|
41
|
-
nonce: '
|
|
42
|
+
nonce: 'SERVER_NONCE',
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
const proof = dpop.proof;
|
|
45
46
|
const thumbprint = await dpop.calculateThumbprint();
|
|
46
47
|
const publicJwk = await dpop.getPublicKey('JWK');
|
|
47
|
-
const
|
|
48
|
+
const keyInfo = await DPoP.getKeyInfo();
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
## API
|
|
51
52
|
|
|
52
|
-
###
|
|
53
|
-
|
|
54
|
-
- `GenerateProofInput`
|
|
55
|
-
- `DPoPProofContext`
|
|
56
|
-
- `DPoPKeyInfo`
|
|
57
|
-
- `PublicJwk`
|
|
58
|
-
- `PublicKeyFormat = 'JWK' | 'DER' | 'RAW'`
|
|
59
|
-
|
|
60
|
-
### `DPoP` static methods
|
|
53
|
+
### Static methods
|
|
61
54
|
|
|
62
55
|
- `DPoP.generateProof(input): Promise<DPoP>`
|
|
63
56
|
- `DPoP.assertHardwareBacked(alias?): Promise<void>`
|
|
@@ -66,22 +59,97 @@ const isBound = await dpop.isBoundToAlias();
|
|
|
66
59
|
- `DPoP.hasKeyPair(alias?): Promise<boolean>`
|
|
67
60
|
- `DPoP.rotateKeyPair(alias?): Promise<void>`
|
|
68
61
|
|
|
69
|
-
###
|
|
62
|
+
### Instance members
|
|
70
63
|
|
|
71
64
|
- `proof: string`
|
|
72
65
|
- `proofContext: DPoPProofContext`
|
|
73
66
|
- `alias?: string`
|
|
74
|
-
|
|
75
|
-
### `DPoP` instance methods
|
|
76
|
-
|
|
77
67
|
- `calculateThumbprint(): Promise<string>`
|
|
78
68
|
- `getPublicKey(format): Promise<PublicJwk | string>`
|
|
79
69
|
- `signWithDpopPrivateKey(payload): Promise<string>`
|
|
80
70
|
- `isBoundToAlias(alias?): Promise<boolean>`
|
|
81
71
|
|
|
82
|
-
|
|
72
|
+
### Main types
|
|
83
73
|
|
|
84
|
-
|
|
74
|
+
- `GenerateProofInput`
|
|
75
|
+
- `DPoPProofContext`
|
|
76
|
+
- `DPoPKeyInfo`
|
|
77
|
+
- `PublicJwk`
|
|
78
|
+
- `PublicKeyFormat = 'JWK' | 'DER' | 'RAW'`
|
|
79
|
+
- `SecureHardwareFallbackReason = 'UNAVAILABLE' | 'PROVIDER_ERROR' | 'POLICY_REJECTED' | 'UNKNOWN'`
|
|
80
|
+
- `AndroidSecurityLevelName = 'SOFTWARE' | 'TRUSTED_ENVIRONMENT' | 'STRONGBOX'`
|
|
81
|
+
- `IOSSecurityLevelName = 'SOFTWARE' | 'SECURE_ENCLAVE'`
|
|
82
|
+
|
|
83
|
+
## `getKeyInfo()`
|
|
84
|
+
|
|
85
|
+
`getKeyInfo()` returns shared fields plus platform-specific hardware metadata.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
type DPoPKeyInfo = {
|
|
89
|
+
alias: string;
|
|
90
|
+
hasKeyPair: boolean;
|
|
91
|
+
algorithm?: string;
|
|
92
|
+
curve?: string;
|
|
93
|
+
insideSecureHardware?: boolean;
|
|
94
|
+
hardware?: {
|
|
95
|
+
android?: {
|
|
96
|
+
strongBoxAvailable: boolean;
|
|
97
|
+
strongBoxBacked: boolean;
|
|
98
|
+
securityLevel?: number;
|
|
99
|
+
securityLevelName?: 'SOFTWARE' | 'TRUSTED_ENVIRONMENT' | 'STRONGBOX';
|
|
100
|
+
strongBoxFallbackReason?: 'UNAVAILABLE' | 'PROVIDER_ERROR' | 'POLICY_REJECTED' | 'UNKNOWN' | null;
|
|
101
|
+
};
|
|
102
|
+
ios?: {
|
|
103
|
+
secureEnclaveAvailable: boolean;
|
|
104
|
+
secureEnclaveBacked: boolean;
|
|
105
|
+
securityLevel?: number | null;
|
|
106
|
+
securityLevelName?: 'SOFTWARE' | 'SECURE_ENCLAVE';
|
|
107
|
+
secureEnclaveFallbackReason?: 'UNAVAILABLE' | 'PROVIDER_ERROR' | 'POLICY_REJECTED' | 'UNKNOWN' | null;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Security level semantics
|
|
114
|
+
|
|
115
|
+
- `securityLevel = 1`
|
|
116
|
+
Software-backed key material
|
|
117
|
+
- `securityLevel = 2`
|
|
118
|
+
Hardware-backed key material
|
|
119
|
+
On Android this usually means TEE
|
|
120
|
+
On iOS this means Secure Enclave
|
|
121
|
+
- `securityLevel = 3`
|
|
122
|
+
Android StrongBox-backed key
|
|
123
|
+
- `securityLevel = null`
|
|
124
|
+
No key material available, or the native platform did not report a numeric level
|
|
125
|
+
|
|
126
|
+
### Fallback semantics
|
|
127
|
+
|
|
128
|
+
- On Android, the library tries StrongBox first when available
|
|
129
|
+
- On iOS, the library tries Secure Enclave first when available
|
|
130
|
+
- Fallback reasons are sanitized enums rather than raw native errors
|
|
131
|
+
- On iOS Simulator, `secureEnclaveFallbackReason` is expected to be `UNAVAILABLE`
|
|
132
|
+
|
|
133
|
+
## Notes
|
|
134
|
+
|
|
135
|
+
- Default alias: `react-native-dpop`
|
|
136
|
+
- `htm` is normalized to uppercase
|
|
137
|
+
- `ath` is derived from `accessToken` when provided
|
|
138
|
+
- `jti` and `iat` are auto-generated when omitted
|
|
139
|
+
- For React Native 0.75 on Android, the library ensures `iat` is sent as a number to avoid an older bridge nullability issue with `Double`
|
|
140
|
+
|
|
141
|
+
## Example apps
|
|
142
|
+
|
|
143
|
+
This repository includes two example apps:
|
|
144
|
+
|
|
145
|
+
- `examples/v0.75`
|
|
146
|
+
- `examples/v0.83`
|
|
147
|
+
|
|
148
|
+
The root `example` script points to `examples/v0.83`.
|
|
149
|
+
|
|
150
|
+
## Errors
|
|
151
|
+
|
|
152
|
+
Native rejections use codes such as:
|
|
85
153
|
|
|
86
154
|
- `ERR_DPOP_GENERATE_PROOF`
|
|
87
155
|
- `ERR_DPOP_CALCULATE_THUMBPRINT`
|
|
@@ -94,13 +162,6 @@ Native errors are rejected with codes such as:
|
|
|
94
162
|
- `ERR_DPOP_ASSERT_HARDWARE_BACKED`
|
|
95
163
|
- `ERR_DPOP_IS_BOUND_TO_ALIAS`
|
|
96
164
|
|
|
97
|
-
## Notes
|
|
98
|
-
|
|
99
|
-
- If no alias is provided, the default alias is `react-native-dpop`.
|
|
100
|
-
- `htm` is normalized to uppercase in proof generation.
|
|
101
|
-
- `ath` is derived from `accessToken` (`SHA-256`, base64url) when provided.
|
|
102
|
-
- `jti` and `iat` are auto-generated when omitted.
|
|
103
|
-
|
|
104
165
|
## Contributing
|
|
105
166
|
|
|
106
167
|
- [Development workflow](CONTRIBUTING.md#development-workflow)
|
package/ReactNativeDPoP.podspec
CHANGED
|
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.license = package["license"]
|
|
12
12
|
s.authors = package["author"]
|
|
13
13
|
|
|
14
|
-
s.platforms = { :ios =>
|
|
14
|
+
s.platforms = { :ios => "14.0" }
|
|
15
15
|
s.source = { :git => "https://github.com/Cirilord/react-native-dpop.git", :tag => "#{s.version}" }
|
|
16
16
|
|
|
17
17
|
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
@@ -28,6 +28,7 @@ internal data class KeyStoreKeyInfo(
|
|
|
28
28
|
val curve: String,
|
|
29
29
|
val insideSecureHardware: Boolean,
|
|
30
30
|
val securityLevel: Int?,
|
|
31
|
+
val securityLevelName: String,
|
|
31
32
|
val strongBoxAvailable: Boolean,
|
|
32
33
|
val strongBoxBacked: Boolean
|
|
33
34
|
)
|
|
@@ -36,6 +37,14 @@ internal class DPoPKeyStore(private val context: Context) {
|
|
|
36
37
|
companion object {
|
|
37
38
|
private const val KEYSTORE_PROVIDER = "AndroidKeyStore"
|
|
38
39
|
private const val EC_CURVE = "secp256r1"
|
|
40
|
+
private const val META_PREFS = "react_native_dpop_keystore_meta"
|
|
41
|
+
private const val META_STRONGBOX_FALLBACK_PREFIX = "strongbox_fallback_reason_"
|
|
42
|
+
private const val REASON_UNAVAILABLE = "UNAVAILABLE"
|
|
43
|
+
private const val REASON_PROVIDER_ERROR = "PROVIDER_ERROR"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private val metadataPrefs by lazy {
|
|
47
|
+
context.getSharedPreferences(META_PREFS, Context.MODE_PRIVATE)
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
private val keyStore: KeyStore by lazy {
|
|
@@ -46,6 +55,7 @@ internal class DPoPKeyStore(private val context: Context) {
|
|
|
46
55
|
if (keyStore.containsAlias(alias)) {
|
|
47
56
|
keyStore.deleteEntry(alias)
|
|
48
57
|
}
|
|
58
|
+
clearStrongBoxFallbackReason(alias)
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
fun generateKeyPair(alias: String): Boolean {
|
|
@@ -56,16 +66,20 @@ internal class DPoPKeyStore(private val context: Context) {
|
|
|
56
66
|
if (keyStore.containsAlias(alias)) {
|
|
57
67
|
keyStore.deleteEntry(alias)
|
|
58
68
|
}
|
|
69
|
+
clearStrongBoxFallbackReason(alias)
|
|
59
70
|
|
|
60
71
|
val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, KEYSTORE_PROVIDER)
|
|
61
72
|
if (isStrongBoxEnabled()) {
|
|
62
73
|
try {
|
|
63
74
|
generator.initialize(buildSpec(alias, useStrongBox = true))
|
|
64
75
|
generator.generateKeyPair()
|
|
76
|
+
clearStrongBoxFallbackReason(alias)
|
|
65
77
|
return true
|
|
66
78
|
} catch (_: StrongBoxUnavailableException) {
|
|
79
|
+
storeStrongBoxFallbackReason(alias, REASON_UNAVAILABLE)
|
|
67
80
|
// Fallback to hardware-backed keystore when StrongBox is unavailable.
|
|
68
81
|
} catch (_: ProviderException) {
|
|
82
|
+
storeStrongBoxFallbackReason(alias, REASON_PROVIDER_ERROR)
|
|
69
83
|
// Some devices expose StrongBox but fail during generation.
|
|
70
84
|
}
|
|
71
85
|
}
|
|
@@ -89,6 +103,12 @@ internal class DPoPKeyStore(private val context: Context) {
|
|
|
89
103
|
return privateKey != null && publicKey != null
|
|
90
104
|
}
|
|
91
105
|
|
|
106
|
+
fun isStrongBoxAvailable(): Boolean = isStrongBoxEnabled()
|
|
107
|
+
|
|
108
|
+
fun getStrongBoxFallbackReason(alias: String): String? {
|
|
109
|
+
return metadataPrefs.getString(strongBoxFallbackKey(alias), null)
|
|
110
|
+
}
|
|
111
|
+
|
|
92
112
|
fun getKeyInfo(alias: String): KeyStoreKeyInfo {
|
|
93
113
|
val keyPair = getKeyPair(alias)
|
|
94
114
|
val keyFactory = KeyFactory.getInstance(keyPair.privateKey.algorithm, KEYSTORE_PROVIDER)
|
|
@@ -100,6 +120,7 @@ internal class DPoPKeyStore(private val context: Context) {
|
|
|
100
120
|
curve = "P-256",
|
|
101
121
|
insideSecureHardware = keyInfo.isInsideSecureHardware,
|
|
102
122
|
securityLevel = readSecurityLevel(keyInfo),
|
|
123
|
+
securityLevelName = readSecurityLevelName(keyInfo),
|
|
103
124
|
strongBoxAvailable = isStrongBoxEnabled(),
|
|
104
125
|
strongBoxBacked = readStrongBoxBacked(keyInfo)
|
|
105
126
|
)
|
|
@@ -153,6 +174,24 @@ internal class DPoPKeyStore(private val context: Context) {
|
|
|
153
174
|
}
|
|
154
175
|
}
|
|
155
176
|
|
|
177
|
+
private fun readSecurityLevelName(keyInfo: KeyInfo): String {
|
|
178
|
+
val strongBoxBacked = readStrongBoxBacked(keyInfo)
|
|
179
|
+
val securityLevel = readSecurityLevel(keyInfo)
|
|
180
|
+
|
|
181
|
+
return when (securityLevel) {
|
|
182
|
+
1 -> "SOFTWARE"
|
|
183
|
+
2 -> "TRUSTED_ENVIRONMENT"
|
|
184
|
+
3 -> "STRONGBOX"
|
|
185
|
+
else -> {
|
|
186
|
+
when {
|
|
187
|
+
strongBoxBacked -> "STRONGBOX"
|
|
188
|
+
keyInfo.isInsideSecureHardware -> "TRUSTED_ENVIRONMENT"
|
|
189
|
+
else -> "SOFTWARE"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
156
195
|
private fun readStrongBoxBacked(keyInfo: KeyInfo): Boolean {
|
|
157
196
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
|
158
197
|
return false
|
|
@@ -165,4 +204,14 @@ internal class DPoPKeyStore(private val context: Context) {
|
|
|
165
204
|
false
|
|
166
205
|
}
|
|
167
206
|
}
|
|
207
|
+
|
|
208
|
+
private fun storeStrongBoxFallbackReason(alias: String, reason: String) {
|
|
209
|
+
metadataPrefs.edit().putString(strongBoxFallbackKey(alias), reason).apply()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private fun clearStrongBoxFallbackReason(alias: String) {
|
|
213
|
+
metadataPrefs.edit().remove(strongBoxFallbackKey(alias)).apply()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private fun strongBoxFallbackKey(alias: String): String = "$META_STRONGBOX_FALLBACK_PREFIX$alias"
|
|
168
217
|
}
|
|
@@ -12,10 +12,32 @@ class DPoPModule(reactContext: ReactApplicationContext) :
|
|
|
12
12
|
NativeReactNativeDPoPSpec(reactContext) {
|
|
13
13
|
private val keyStore = DPoPKeyStore(reactContext)
|
|
14
14
|
|
|
15
|
+
companion object {
|
|
16
|
+
private const val DEFAULT_ALIAS = "react-native-dpop"
|
|
17
|
+
const val NAME = NativeReactNativeDPoPSpec.NAME
|
|
18
|
+
private const val UNKNOWN_STRONGBOX_FALLBACK_REASON = "UNKNOWN"
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
private fun resolveAlias(alias: String?): String {
|
|
16
22
|
return alias ?: DEFAULT_ALIAS
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
private fun resolveStrongBoxFallbackReason(
|
|
26
|
+
strongBoxAvailable: Boolean,
|
|
27
|
+
strongBoxBacked: Boolean,
|
|
28
|
+
fallbackReason: String?
|
|
29
|
+
): String? {
|
|
30
|
+
if (fallbackReason != null) {
|
|
31
|
+
return fallbackReason
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return if (strongBoxAvailable && !strongBoxBacked) {
|
|
35
|
+
UNKNOWN_STRONGBOX_FALLBACK_REASON
|
|
36
|
+
} else {
|
|
37
|
+
null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
19
41
|
override fun assertHardwareBacked(alias: String?, promise: Promise) {
|
|
20
42
|
try {
|
|
21
43
|
val effectiveAlias = resolveAlias(alias)
|
|
@@ -70,26 +92,62 @@ class DPoPModule(reactContext: ReactApplicationContext) :
|
|
|
70
92
|
try {
|
|
71
93
|
val effectiveAlias = resolveAlias(alias)
|
|
72
94
|
if (!keyStore.hasKeyPair(effectiveAlias)) {
|
|
95
|
+
val strongBoxAvailable = keyStore.isStrongBoxAvailable()
|
|
96
|
+
val fallbackReason = resolveStrongBoxFallbackReason(
|
|
97
|
+
strongBoxAvailable = strongBoxAvailable,
|
|
98
|
+
strongBoxBacked = false,
|
|
99
|
+
fallbackReason = keyStore.getStrongBoxFallbackReason(effectiveAlias)
|
|
100
|
+
)
|
|
101
|
+
val hardwareAndroid = Arguments.createMap().apply {
|
|
102
|
+
putBoolean("strongBoxAvailable", strongBoxAvailable)
|
|
103
|
+
putBoolean("strongBoxBacked", false)
|
|
104
|
+
if (fallbackReason != null) {
|
|
105
|
+
putString("strongBoxFallbackReason", fallbackReason)
|
|
106
|
+
} else {
|
|
107
|
+
putNull("strongBoxFallbackReason")
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
val hardware = Arguments.createMap().apply {
|
|
111
|
+
putMap("android", hardwareAndroid)
|
|
112
|
+
}
|
|
73
113
|
val result = Arguments.createMap().apply {
|
|
74
114
|
putString("alias", effectiveAlias)
|
|
75
115
|
putBoolean("hasKeyPair", false)
|
|
116
|
+
putMap("hardware", hardware)
|
|
76
117
|
}
|
|
77
118
|
promise.resolve(result)
|
|
78
119
|
return
|
|
79
120
|
}
|
|
80
121
|
|
|
81
122
|
val keyInfo = keyStore.getKeyInfo(effectiveAlias)
|
|
123
|
+
val fallbackReason = resolveStrongBoxFallbackReason(
|
|
124
|
+
strongBoxAvailable = keyInfo.strongBoxAvailable,
|
|
125
|
+
strongBoxBacked = keyInfo.strongBoxBacked,
|
|
126
|
+
fallbackReason = keyStore.getStrongBoxFallbackReason(effectiveAlias)
|
|
127
|
+
)
|
|
128
|
+
val hardwareAndroid = Arguments.createMap().apply {
|
|
129
|
+
putBoolean("strongBoxAvailable", keyInfo.strongBoxAvailable)
|
|
130
|
+
putBoolean("strongBoxBacked", keyInfo.strongBoxBacked)
|
|
131
|
+
if (keyInfo.securityLevel != null) {
|
|
132
|
+
putInt("securityLevel", keyInfo.securityLevel)
|
|
133
|
+
}
|
|
134
|
+
putString("securityLevelName", keyInfo.securityLevelName)
|
|
135
|
+
if (fallbackReason != null) {
|
|
136
|
+
putString("strongBoxFallbackReason", fallbackReason)
|
|
137
|
+
} else {
|
|
138
|
+
putNull("strongBoxFallbackReason")
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
val hardware = Arguments.createMap().apply {
|
|
142
|
+
putMap("android", hardwareAndroid)
|
|
143
|
+
}
|
|
82
144
|
val result = Arguments.createMap().apply {
|
|
83
145
|
putString("alias", keyInfo.alias)
|
|
84
146
|
putString("algorithm", keyInfo.algorithm)
|
|
85
147
|
putString("curve", keyInfo.curve)
|
|
86
148
|
putBoolean("hasKeyPair", true)
|
|
87
149
|
putBoolean("insideSecureHardware", keyInfo.insideSecureHardware)
|
|
88
|
-
|
|
89
|
-
putBoolean("strongBoxBacked", keyInfo.strongBoxBacked)
|
|
90
|
-
if (keyInfo.securityLevel != null) {
|
|
91
|
-
putInt("securityLevel", keyInfo.securityLevel)
|
|
92
|
-
}
|
|
150
|
+
putMap("hardware", hardware)
|
|
93
151
|
}
|
|
94
152
|
promise.resolve(result)
|
|
95
153
|
} catch (e: Exception) {
|
|
@@ -294,9 +352,4 @@ class DPoPModule(reactContext: ReactApplicationContext) :
|
|
|
294
352
|
promise.reject("ERR_DPOP_GENERATE_PROOF", e.message, e)
|
|
295
353
|
}
|
|
296
354
|
}
|
|
297
|
-
|
|
298
|
-
companion object {
|
|
299
|
-
private const val DEFAULT_ALIAS = "react-native-dpop"
|
|
300
|
-
const val NAME = NativeReactNativeDPoPSpec.NAME
|
|
301
|
-
}
|
|
302
355
|
}
|
|
@@ -5,7 +5,6 @@ import com.facebook.react.bridge.NativeModule
|
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
6
|
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
7
|
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
-
import java.util.HashMap
|
|
9
8
|
|
|
10
9
|
class DPoPPackage : BaseReactPackage() {
|
|
11
10
|
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
@@ -19,12 +18,13 @@ class DPoPPackage : BaseReactPackage() {
|
|
|
19
18
|
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
|
|
20
19
|
mapOf(
|
|
21
20
|
DPoPModule.NAME to ReactModuleInfo(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
DPoPModule.NAME,
|
|
22
|
+
DPoPModule::class.java.name,
|
|
23
|
+
false,
|
|
24
|
+
false,
|
|
25
|
+
false,
|
|
26
|
+
false,
|
|
27
|
+
true
|
|
28
28
|
)
|
|
29
29
|
)
|
|
30
30
|
}
|
package/ios/DPoPKeyStore.swift
CHANGED
|
@@ -12,20 +12,24 @@ struct DPoPKeyInfo {
|
|
|
12
12
|
let curve: String
|
|
13
13
|
let hasKeyPair: Bool
|
|
14
14
|
let insideSecureHardware: Bool
|
|
15
|
-
let strongBoxAvailable: Bool
|
|
16
|
-
let strongBoxBacked: Bool
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
final class DPoPKeyStore {
|
|
20
18
|
private let secureEnclave = SecureEnclaveKeyStore()
|
|
21
19
|
private let keychain = KeychainKeyStore()
|
|
20
|
+
private let fallbackReasonDefaults = UserDefaults.standard
|
|
21
|
+
private let fallbackReasonPrefix = "react_native_dpop_secure_enclave_fallback_reason_"
|
|
22
|
+
private let unavailableFallbackReason = "UNAVAILABLE"
|
|
23
|
+
private lazy var secureEnclaveAvailable = secureEnclave.isAvailable()
|
|
22
24
|
|
|
23
25
|
func generateKeyPair(alias: String) throws {
|
|
24
26
|
try deleteKeyPair(alias: alias)
|
|
25
27
|
|
|
26
28
|
do {
|
|
27
29
|
try secureEnclave.generateKeyPair(alias: alias)
|
|
30
|
+
clearSecureEnclaveFallbackReason(alias: alias)
|
|
28
31
|
} catch {
|
|
32
|
+
storeSecureEnclaveFallbackReason(alias: alias, reason: mapSecureEnclaveFallbackReason(error))
|
|
29
33
|
try keychain.generateKeyPair(alias: alias)
|
|
30
34
|
}
|
|
31
35
|
}
|
|
@@ -33,6 +37,7 @@ final class DPoPKeyStore {
|
|
|
33
37
|
func deleteKeyPair(alias: String) throws {
|
|
34
38
|
try secureEnclave.deleteKeyPair(alias: alias)
|
|
35
39
|
try keychain.deleteKeyPair(alias: alias)
|
|
40
|
+
clearSecureEnclaveFallbackReason(alias: alias)
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
func hasKeyPair(alias: String) -> Bool {
|
|
@@ -64,9 +69,7 @@ final class DPoPKeyStore {
|
|
|
64
69
|
algorithm: "EC",
|
|
65
70
|
curve: "P-256",
|
|
66
71
|
hasKeyPair: true,
|
|
67
|
-
insideSecureHardware: true
|
|
68
|
-
strongBoxAvailable: false,
|
|
69
|
-
strongBoxBacked: false
|
|
72
|
+
insideSecureHardware: true
|
|
70
73
|
)
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -76,9 +79,7 @@ final class DPoPKeyStore {
|
|
|
76
79
|
algorithm: "EC",
|
|
77
80
|
curve: "P-256",
|
|
78
81
|
hasKeyPair: true,
|
|
79
|
-
insideSecureHardware: false
|
|
80
|
-
strongBoxAvailable: false,
|
|
81
|
-
strongBoxBacked: false
|
|
82
|
+
insideSecureHardware: false
|
|
82
83
|
)
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -87,13 +88,56 @@ final class DPoPKeyStore {
|
|
|
87
88
|
algorithm: "EC",
|
|
88
89
|
curve: "P-256",
|
|
89
90
|
hasKeyPair: false,
|
|
90
|
-
insideSecureHardware: false
|
|
91
|
-
strongBoxAvailable: false,
|
|
92
|
-
strongBoxBacked: false
|
|
91
|
+
insideSecureHardware: false
|
|
93
92
|
)
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
func isHardwareBacked(alias: String) -> Bool {
|
|
97
96
|
secureEnclave.isHardwareBacked(alias: alias)
|
|
98
97
|
}
|
|
98
|
+
|
|
99
|
+
func isSecureEnclaveAvailable() -> Bool {
|
|
100
|
+
secureEnclaveAvailable
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func getSecureEnclaveFallbackReason(alias: String) -> String? {
|
|
104
|
+
if !secureEnclaveAvailable && keychain.hasKeyPair(alias: alias) {
|
|
105
|
+
return unavailableFallbackReason
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if let storedReason = fallbackReasonDefaults.string(forKey: fallbackReasonKey(alias: alias)) {
|
|
109
|
+
return storedReason
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return nil
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private func storeSecureEnclaveFallbackReason(alias: String, reason: String) {
|
|
116
|
+
fallbackReasonDefaults.set(reason, forKey: fallbackReasonKey(alias: alias))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private func clearSecureEnclaveFallbackReason(alias: String) {
|
|
120
|
+
fallbackReasonDefaults.removeObject(forKey: fallbackReasonKey(alias: alias))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private func fallbackReasonKey(alias: String) -> String {
|
|
124
|
+
"\(fallbackReasonPrefix)\(alias)"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private func mapSecureEnclaveFallbackReason(_ error: Error) -> String {
|
|
128
|
+
let nsError = error as NSError
|
|
129
|
+
|
|
130
|
+
if nsError.domain == NSOSStatusErrorDomain {
|
|
131
|
+
switch nsError.code {
|
|
132
|
+
case Int(errSecNotAvailable), Int(errSecUnimplemented):
|
|
133
|
+
return "UNAVAILABLE"
|
|
134
|
+
case Int(errSecAuthFailed), Int(errSecInteractionNotAllowed), Int(errSecUserCanceled):
|
|
135
|
+
return "POLICY_REJECTED"
|
|
136
|
+
default:
|
|
137
|
+
return "PROVIDER_ERROR"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return "UNKNOWN"
|
|
142
|
+
}
|
|
99
143
|
}
|
package/ios/DPoPModule.swift
CHANGED
|
@@ -7,6 +7,8 @@ final class DPoPModule {
|
|
|
7
7
|
|
|
8
8
|
private let keyStore = DPoPKeyStore()
|
|
9
9
|
private let defaultAlias = "react-native-dpop"
|
|
10
|
+
private let unknownSecureEnclaveFallbackReason = "UNKNOWN"
|
|
11
|
+
private let unavailableSecureEnclaveFallbackReason = "UNAVAILABLE"
|
|
10
12
|
|
|
11
13
|
private init() {}
|
|
12
14
|
|
|
@@ -44,26 +46,75 @@ final class DPoPModule {
|
|
|
44
46
|
|
|
45
47
|
func getKeyInfo(alias: String?) -> [String: Any] {
|
|
46
48
|
let effectiveAlias = resolveAlias(alias)
|
|
49
|
+
let secureEnclaveAvailable = keyStore.isSecureEnclaveAvailable()
|
|
47
50
|
let keyInfo = keyStore.getKeyInfo(alias: effectiveAlias)
|
|
51
|
+
let secureEnclaveBacked = secureEnclaveAvailable && keyInfo.insideSecureHardware
|
|
52
|
+
let fallbackReason = resolveSecureEnclaveFallbackReason(
|
|
53
|
+
secureEnclaveAvailable: secureEnclaveAvailable,
|
|
54
|
+
secureEnclaveBacked: secureEnclaveBacked,
|
|
55
|
+
hasKeyPair: keyInfo.hasKeyPair,
|
|
56
|
+
fallbackReason: keyStore.getSecureEnclaveFallbackReason(alias: effectiveAlias)
|
|
57
|
+
)
|
|
58
|
+
let secureEnclaveFallbackReason: Any = fallbackReason ?? NSNull()
|
|
48
59
|
|
|
49
60
|
if !keyInfo.hasKeyPair {
|
|
50
61
|
return [
|
|
51
62
|
"alias": effectiveAlias,
|
|
52
|
-
"hasKeyPair": false
|
|
63
|
+
"hasKeyPair": false,
|
|
64
|
+
"hardware": [
|
|
65
|
+
"ios": [
|
|
66
|
+
"secureEnclaveAvailable": secureEnclaveAvailable,
|
|
67
|
+
"secureEnclaveBacked": false,
|
|
68
|
+
"securityLevel": NSNull(),
|
|
69
|
+
"securityLevelName": "SOFTWARE",
|
|
70
|
+
"secureEnclaveFallbackReason": secureEnclaveFallbackReason
|
|
71
|
+
]
|
|
72
|
+
]
|
|
53
73
|
]
|
|
54
74
|
}
|
|
55
75
|
|
|
76
|
+
let securityLevel = secureEnclaveBacked ? 2 : 1
|
|
77
|
+
let securityLevelName = secureEnclaveBacked ? "SECURE_ENCLAVE" : "SOFTWARE"
|
|
78
|
+
|
|
56
79
|
return [
|
|
57
80
|
"alias": keyInfo.alias,
|
|
58
81
|
"algorithm": keyInfo.algorithm,
|
|
59
82
|
"curve": keyInfo.curve,
|
|
60
83
|
"hasKeyPair": true,
|
|
61
|
-
"insideSecureHardware":
|
|
62
|
-
"
|
|
63
|
-
|
|
84
|
+
"insideSecureHardware": secureEnclaveBacked,
|
|
85
|
+
"hardware": [
|
|
86
|
+
"ios": [
|
|
87
|
+
"secureEnclaveAvailable": secureEnclaveAvailable,
|
|
88
|
+
"secureEnclaveBacked": secureEnclaveBacked,
|
|
89
|
+
"securityLevel": securityLevel,
|
|
90
|
+
"securityLevelName": securityLevelName,
|
|
91
|
+
"secureEnclaveFallbackReason": secureEnclaveFallbackReason
|
|
92
|
+
]
|
|
93
|
+
]
|
|
64
94
|
]
|
|
65
95
|
}
|
|
66
96
|
|
|
97
|
+
private func resolveSecureEnclaveFallbackReason(
|
|
98
|
+
secureEnclaveAvailable: Bool,
|
|
99
|
+
secureEnclaveBacked: Bool,
|
|
100
|
+
hasKeyPair: Bool,
|
|
101
|
+
fallbackReason: String?
|
|
102
|
+
) -> String? {
|
|
103
|
+
if let fallbackReason {
|
|
104
|
+
return fallbackReason
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if hasKeyPair && !secureEnclaveAvailable {
|
|
108
|
+
return unavailableSecureEnclaveFallbackReason
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if hasKeyPair && !secureEnclaveBacked {
|
|
112
|
+
return unknownSecureEnclaveFallbackReason
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return nil
|
|
116
|
+
}
|
|
117
|
+
|
|
67
118
|
func getPublicKeyDer(alias: String?) throws -> String {
|
|
68
119
|
let effectiveAlias = resolveAlias(alias)
|
|
69
120
|
if !keyStore.hasKeyPair(alias: effectiveAlias) {
|
|
@@ -327,12 +378,19 @@ final class DPoPModule {
|
|
|
327
378
|
additional: [String: Any]?,
|
|
328
379
|
kid: String?,
|
|
329
380
|
jti: String?,
|
|
330
|
-
iat:
|
|
381
|
+
iat: Any?,
|
|
331
382
|
alias: String?,
|
|
332
383
|
resolve: @escaping RCTPromiseResolveBlock,
|
|
333
384
|
reject: @escaping RCTPromiseRejectBlock
|
|
334
385
|
) {
|
|
335
386
|
do {
|
|
387
|
+
let normalizedIat: NSNumber?
|
|
388
|
+
if iat is NSNull {
|
|
389
|
+
normalizedIat = nil
|
|
390
|
+
} else {
|
|
391
|
+
normalizedIat = iat as? NSNumber
|
|
392
|
+
}
|
|
393
|
+
|
|
336
394
|
resolve(
|
|
337
395
|
try DPoPModule.shared.generateProof(
|
|
338
396
|
htu: htu,
|
|
@@ -342,7 +400,7 @@ final class DPoPModule {
|
|
|
342
400
|
additional: additional,
|
|
343
401
|
kid: kid,
|
|
344
402
|
jti: jti,
|
|
345
|
-
iat:
|
|
403
|
+
iat: normalizedIat,
|
|
346
404
|
alias: alias
|
|
347
405
|
)
|
|
348
406
|
)
|
package/ios/DPoPModuleBridge.mm
CHANGED
|
@@ -70,7 +70,7 @@ RCT_EXTERN_METHOD(generateProof:(NSString *)htu
|
|
|
70
70
|
additional:(NSDictionary * _Nullable)additional
|
|
71
71
|
kid:(NSString * _Nullable)kid
|
|
72
72
|
jti:(NSString * _Nullable)jti
|
|
73
|
-
iat:(
|
|
73
|
+
iat:(id _Nullable)iat
|
|
74
74
|
alias:(NSString * _Nullable)alias
|
|
75
75
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
76
76
|
reject:(RCTPromiseRejectBlock)reject)
|
|
@@ -4,6 +4,21 @@ import Security
|
|
|
4
4
|
final class SecureEnclaveKeyStore {
|
|
5
5
|
private let service = "com.dpop.secureenclave"
|
|
6
6
|
|
|
7
|
+
func isAvailable() -> Bool {
|
|
8
|
+
#if targetEnvironment(simulator)
|
|
9
|
+
return false
|
|
10
|
+
#else
|
|
11
|
+
let probeAlias = "__secure_enclave_probe_\(UUID().uuidString)"
|
|
12
|
+
do {
|
|
13
|
+
try generateKeyPair(alias: probeAlias)
|
|
14
|
+
try deleteKeyPair(alias: probeAlias)
|
|
15
|
+
return true
|
|
16
|
+
} catch {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
#endif
|
|
20
|
+
}
|
|
21
|
+
|
|
7
22
|
func generateKeyPair(alias: String) throws {
|
|
8
23
|
try deleteKeyPair(alias: alias)
|
|
9
24
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { NativeModules, TurboModuleRegistry } from 'react-native';
|
|
4
|
-
const nativeDpopModule =
|
|
4
|
+
const nativeDpopModule =
|
|
5
|
+
// eslint-disable-next-line dot-notation -- required by noPropertyAccessFromIndexSignature from @tsconfig/strictest
|
|
6
|
+
TurboModuleRegistry.get('ReactNativeDPoP') ?? NativeModules['ReactNativeDPoP'];
|
|
5
7
|
export default nativeDpopModule;
|
|
6
8
|
//# sourceMappingURL=NativeReactNativeDPoP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeModules","TurboModuleRegistry","nativeDpopModule","get"
|
|
1
|
+
{"version":3,"names":["NativeModules","TurboModuleRegistry","nativeDpopModule","get"],"sourceRoot":"../../src","sources":["NativeReactNativeDPoP.ts"],"mappings":";;AACA,SAASA,aAAa,EAAEC,mBAAmB,QAAQ,cAAc;AA4BjE,MAAMC,gBAAgB;AACpB;AACAD,mBAAmB,CAACE,GAAG,CAAO,iBAAiB,CAAC,IAAKH,aAAa,CAAC,iBAAiB,CAAsB;AAE5G,eAAeE,gBAAgB","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -26,7 +26,9 @@ export class DPoP {
|
|
|
26
26
|
return NativeReactNativeDPoP.isBoundToAlias(this.proof, alias ?? this.alias ?? null);
|
|
27
27
|
}
|
|
28
28
|
static async generateProof(input) {
|
|
29
|
-
const result = await NativeReactNativeDPoP.generateProof(input.htu, input.htm, input.nonce ?? null, input.accessToken ?? null, input.additional ?? null, input.kid ?? null, input.jti ?? null,
|
|
29
|
+
const result = await NativeReactNativeDPoP.generateProof(input.htu, input.htm, input.nonce ?? null, input.accessToken ?? null, input.additional ?? null, input.kid ?? null, input.jti ?? null,
|
|
30
|
+
// RN 0.75 Android bridge can crash when a nullable Double arrives as null.
|
|
31
|
+
input.iat ?? Math.floor(Date.now() / 1000), input.alias ?? null);
|
|
30
32
|
return new DPoP(result.proof, result.proofContext, input.alias);
|
|
31
33
|
}
|
|
32
34
|
static async assertHardwareBacked(alias) {
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeReactNativeDPoP","DPoP","constructor","proof","proofContext","alias","calculateThumbprint","getPublicKey","format","getPublicKeyDer","getPublicKeyRaw","getPublicKeyJwk","signWithDpopPrivateKey","payload","isBoundToAlias","generateProof","input","result","htu","htm","nonce","accessToken","additional","kid","jti","iat","assertHardwareBacked","deleteKeyPair","getKeyInfo","hasKeyPair","rotateKeyPair"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,qBAAqB,MAAM,4BAAyB;
|
|
1
|
+
{"version":3,"names":["NativeReactNativeDPoP","DPoP","constructor","proof","proofContext","alias","calculateThumbprint","getPublicKey","format","getPublicKeyDer","getPublicKeyRaw","getPublicKeyJwk","signWithDpopPrivateKey","payload","isBoundToAlias","generateProof","input","result","htu","htm","nonce","accessToken","additional","kid","jti","iat","Math","floor","Date","now","assertHardwareBacked","deleteKeyPair","getKeyInfo","hasKeyPair","rotateKeyPair"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,qBAAqB,MAAM,4BAAyB;AAqE3D,OAAO,MAAMC,IAAI,CAAC;EAKRC,WAAWA,CAACC,KAAa,EAAEC,YAA8B,EAAEC,KAAc,EAAE;IACjF,IAAI,CAACF,KAAK,GAAGA,KAAK;IAClB,IAAI,CAACC,YAAY,GAAGA,YAAY;IAChC,IAAI,CAACC,KAAK,GAAGA,KAAK;EACpB;EAEA,MAAaC,mBAAmBA,CAAA,EAAoB;IAClD,OAAON,qBAAqB,CAACM,mBAAmB,CAAC,IAAI,CAACD,KAAK,IAAI,IAAI,CAAC;EACtE;EAEA,MAAaE,YAAYA,CAACC,MAAuB,EAA+B;IAC9E,IAAIA,MAAM,KAAK,KAAK,EAAE;MACpB,OAAOR,qBAAqB,CAACS,eAAe,CAAC,IAAI,CAACJ,KAAK,IAAI,IAAI,CAAC;IAClE;IACA,IAAIG,MAAM,KAAK,KAAK,EAAE;MACpB,OAAOR,qBAAqB,CAACU,eAAe,CAAC,IAAI,CAACL,KAAK,IAAI,IAAI,CAAC;IAClE;IAEA,OAAOL,qBAAqB,CAACW,eAAe,CAAC,IAAI,CAACN,KAAK,IAAI,IAAI,CAAC;EAClE;EAEA,MAAaO,sBAAsBA,CAACC,OAAe,EAAmB;IACpE,OAAOb,qBAAqB,CAACY,sBAAsB,CAACC,OAAO,EAAE,IAAI,CAACR,KAAK,IAAI,IAAI,CAAC;EAClF;EAEA,MAAaS,cAAcA,CAACT,KAAc,EAAoB;IAC5D,OAAOL,qBAAqB,CAACc,cAAc,CAAC,IAAI,CAACX,KAAK,EAAEE,KAAK,IAAI,IAAI,CAACA,KAAK,IAAI,IAAI,CAAC;EACtF;EAEA,aAAoBU,aAAaA,CAACC,KAAyB,EAAiB;IAC1E,MAAMC,MAAM,GAAI,MAAMjB,qBAAqB,CAACe,aAAa,CACvDC,KAAK,CAACE,GAAG,EACTF,KAAK,CAACG,GAAG,EACTH,KAAK,CAACI,KAAK,IAAI,IAAI,EACnBJ,KAAK,CAACK,WAAW,IAAI,IAAI,EACzBL,KAAK,CAACM,UAAU,IAAI,IAAI,EACxBN,KAAK,CAACO,GAAG,IAAI,IAAI,EACjBP,KAAK,CAACQ,GAAG,IAAI,IAAI;IACjB;IACAR,KAAK,CAACS,GAAG,IAAIC,IAAI,CAACC,KAAK,CAACC,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1Cb,KAAK,CAACX,KAAK,IAAI,IACjB,CAAyB;IAEzB,OAAO,IAAIJ,IAAI,CAACgB,MAAM,CAACd,KAAK,EAAEc,MAAM,CAACb,YAAY,EAAEY,KAAK,CAACX,KAAK,CAAC;EACjE;EAEA,aAAoByB,oBAAoBA,CAACzB,KAAc,EAAiB;IACtE,MAAML,qBAAqB,CAAC8B,oBAAoB,CAACzB,KAAK,IAAI,IAAI,CAAC;EACjE;EAEA,aAAoB0B,aAAaA,CAAC1B,KAAc,EAAiB;IAC/D,MAAML,qBAAqB,CAAC+B,aAAa,CAAC1B,KAAK,IAAI,IAAI,CAAC;EAC1D;EAEA,aAAoB2B,UAAUA,CAAC3B,KAAc,EAAwB;IACnE,OAAOL,qBAAqB,CAACgC,UAAU,CAAC3B,KAAK,IAAI,IAAI,CAAC;EACxD;EAEA,aAAoB4B,UAAUA,CAAC5B,KAAc,EAAoB;IAC/D,OAAOL,qBAAqB,CAACiC,UAAU,CAAC5B,KAAK,IAAI,IAAI,CAAC;EACxD;EAEA,aAAoB6B,aAAaA,CAAC7B,KAAc,EAAiB;IAC/D,MAAML,qBAAqB,CAACkC,aAAa,CAAC7B,KAAK,IAAI,IAAI,CAAC;EAC1D;AACF","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DPoPExampleContent.d.ts","sourceRoot":"","sources":["../../../../examples/shared/DPoPExampleContent.tsx"],"names":[],"mappings":"AAIA,MAAM,CAAC,OAAO,UAAU,kBAAkB,4CA0BzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../examples/v0.75/App.tsx"],"names":[],"mappings":"AAIA,MAAM,CAAC,OAAO,UAAU,GAAG,4CAM1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../examples/v0.83/App.tsx"],"names":[],"mappings":"AAKA,MAAM,CAAC,OAAO,UAAU,GAAG,4CAM1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeReactNativeDPoP.d.ts","sourceRoot":"","sources":["../../../src/NativeReactNativeDPoP.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAE9E,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACxD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7D,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/E,aAAa,CACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,UAAU,EAAE,YAAY,GAAG,IAAI,EAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"NativeReactNativeDPoP.d.ts","sourceRoot":"","sources":["../../../src/NativeReactNativeDPoP.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAE9E,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACxD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7D,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/E,aAAa,CACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,UAAU,EAAE,YAAY,GAAG,IAAI,EAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1B;wBAMkC,IAAI;AAAvC,wBAAwC"}
|
|
@@ -6,15 +6,31 @@ export type PublicJwk = {
|
|
|
6
6
|
y: string;
|
|
7
7
|
};
|
|
8
8
|
export type PublicKeyFormat = 'JWK' | 'DER' | 'RAW';
|
|
9
|
+
export type SecureHardwareFallbackReason = 'UNAVAILABLE' | 'PROVIDER_ERROR' | 'POLICY_REJECTED' | 'UNKNOWN';
|
|
10
|
+
export type AndroidSecurityLevelName = 'SOFTWARE' | 'TRUSTED_ENVIRONMENT' | 'STRONGBOX';
|
|
11
|
+
export type IOSSecurityLevelName = 'SOFTWARE' | 'SECURE_ENCLAVE';
|
|
9
12
|
export type DPoPKeyInfo = {
|
|
10
13
|
alias: string;
|
|
11
14
|
hasKeyPair: boolean;
|
|
12
15
|
algorithm?: string;
|
|
13
16
|
curve?: string;
|
|
14
17
|
insideSecureHardware?: boolean;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
hardware?: {
|
|
19
|
+
android?: {
|
|
20
|
+
strongBoxAvailable: boolean;
|
|
21
|
+
strongBoxBacked: boolean;
|
|
22
|
+
securityLevel?: number;
|
|
23
|
+
securityLevelName?: AndroidSecurityLevelName;
|
|
24
|
+
strongBoxFallbackReason?: SecureHardwareFallbackReason | null;
|
|
25
|
+
};
|
|
26
|
+
ios?: {
|
|
27
|
+
secureEnclaveAvailable: boolean;
|
|
28
|
+
secureEnclaveBacked: boolean;
|
|
29
|
+
securityLevel?: number | null;
|
|
30
|
+
securityLevelName?: IOSSecurityLevelName;
|
|
31
|
+
secureEnclaveFallbackReason?: SecureHardwareFallbackReason | null;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
18
34
|
};
|
|
19
35
|
export type GenerateProofInput = {
|
|
20
36
|
htu: string;
|
|
@@ -39,7 +55,7 @@ export type DPoPProofContext = {
|
|
|
39
55
|
};
|
|
40
56
|
export declare class DPoP {
|
|
41
57
|
readonly proof: string;
|
|
42
|
-
readonly alias
|
|
58
|
+
readonly alias: string | undefined;
|
|
43
59
|
readonly proofContext: DPoPProofContext;
|
|
44
60
|
private constructor();
|
|
45
61
|
calculateThumbprint(): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,EAAE,OAAO,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAEpD,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,EAAE,OAAO,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAEpD,MAAM,MAAM,4BAA4B,GAAG,aAAa,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAC5G,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG,qBAAqB,GAAG,WAAW,CAAC;AACxF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE;YACR,kBAAkB,EAAE,OAAO,CAAC;YAC5B,eAAe,EAAE,OAAO,CAAC;YACzB,aAAa,CAAC,EAAE,MAAM,CAAC;YACvB,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;YAC7C,uBAAuB,CAAC,EAAE,4BAA4B,GAAG,IAAI,CAAC;SAC/D,CAAC;QACF,GAAG,CAAC,EAAE;YACJ,sBAAsB,EAAE,OAAO,CAAC;YAChC,mBAAmB,EAAE,OAAO,CAAC;YAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAC9B,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;YACzC,2BAA2B,CAAC,EAAE,4BAA4B,GAAG,IAAI,CAAC;SACnE,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAOF,qBAAa,IAAI;IACf,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,SAAgB,YAAY,EAAE,gBAAgB,CAAC;IAE/C,OAAO;IAMM,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IAItC,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC;IAWlE,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIxD,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;WAIzC,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;WAiBvD,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAInD,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAI5C,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;WAIhD,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;WAI5C,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGjE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-dpop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "React Native library for DPoP proof generation and key management.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"android",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"author": "Pedro Cirilo <phscirilo123@gmail.com> (https://github.com/Cirilord)",
|
|
20
20
|
"main": "./lib/module/index.js",
|
|
21
|
+
"react-native": "./src/index.tsx",
|
|
21
22
|
"types": "./lib/typescript/src/index.d.ts",
|
|
22
23
|
"exports": {
|
|
23
24
|
".": {
|
|
@@ -47,11 +48,12 @@
|
|
|
47
48
|
"!**/.*"
|
|
48
49
|
],
|
|
49
50
|
"scripts": {
|
|
50
|
-
"clean": "del-cli android/build
|
|
51
|
-
"example": "yarn workspace react-native-dpop-example",
|
|
51
|
+
"clean": "del-cli android/build examples/v0.83/android/build examples/v0.83/android/app/build examples/v0.83/ios/build lib",
|
|
52
|
+
"example": "yarn workspace react-native-dpop-example-v0.83",
|
|
52
53
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
53
54
|
"prepare": "bob build",
|
|
54
|
-
"release": "release-it
|
|
55
|
+
"release": "release-it",
|
|
56
|
+
"release:version": "release-it --only-version",
|
|
55
57
|
"test": "jest",
|
|
56
58
|
"typecheck": "tsc"
|
|
57
59
|
},
|
|
@@ -70,12 +72,14 @@
|
|
|
70
72
|
"@react-native/babel-preset": "0.83.0",
|
|
71
73
|
"@react-native/eslint-config": "0.83.0",
|
|
72
74
|
"@release-it/conventional-changelog": "^10.0.1",
|
|
75
|
+
"@tsconfig/strictest": "^2.0.8",
|
|
73
76
|
"@types/jest": "^29.5.14",
|
|
74
77
|
"@types/react": "^19.2.0",
|
|
75
78
|
"commitlint": "^19.8.1",
|
|
76
79
|
"del-cli": "^6.0.0",
|
|
77
80
|
"eslint": "^9.35.0",
|
|
78
81
|
"eslint-config-prettier": "^10.1.8",
|
|
82
|
+
"eslint-plugin-import": "^2.32.0",
|
|
79
83
|
"eslint-plugin-prettier": "^5.5.4",
|
|
80
84
|
"jest": "^29.7.0",
|
|
81
85
|
"lefthook": "^2.0.3",
|
|
@@ -88,7 +92,8 @@
|
|
|
88
92
|
"typescript": "^5.9.2"
|
|
89
93
|
},
|
|
90
94
|
"workspaces": [
|
|
91
|
-
"
|
|
95
|
+
"examples/v0.75",
|
|
96
|
+
"examples/v0.83"
|
|
92
97
|
],
|
|
93
98
|
"packageManager": "yarn@4.11.0",
|
|
94
99
|
"react-native-builder-bob": {
|
|
@@ -28,6 +28,7 @@ export interface Spec extends TurboModule {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const nativeDpopModule =
|
|
31
|
-
|
|
31
|
+
// eslint-disable-next-line dot-notation -- required by noPropertyAccessFromIndexSignature from @tsconfig/strictest
|
|
32
|
+
TurboModuleRegistry.get<Spec>('ReactNativeDPoP') ?? (NativeModules['ReactNativeDPoP'] as Spec | undefined);
|
|
32
33
|
|
|
33
34
|
export default nativeDpopModule as Spec;
|
package/src/index.tsx
CHANGED
|
@@ -11,15 +11,32 @@ export type PublicJwk = {
|
|
|
11
11
|
|
|
12
12
|
export type PublicKeyFormat = 'JWK' | 'DER' | 'RAW';
|
|
13
13
|
|
|
14
|
+
export type SecureHardwareFallbackReason = 'UNAVAILABLE' | 'PROVIDER_ERROR' | 'POLICY_REJECTED' | 'UNKNOWN';
|
|
15
|
+
export type AndroidSecurityLevelName = 'SOFTWARE' | 'TRUSTED_ENVIRONMENT' | 'STRONGBOX';
|
|
16
|
+
export type IOSSecurityLevelName = 'SOFTWARE' | 'SECURE_ENCLAVE';
|
|
17
|
+
|
|
14
18
|
export type DPoPKeyInfo = {
|
|
15
19
|
alias: string;
|
|
16
20
|
hasKeyPair: boolean;
|
|
17
21
|
algorithm?: string;
|
|
18
22
|
curve?: string;
|
|
19
23
|
insideSecureHardware?: boolean;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
hardware?: {
|
|
25
|
+
android?: {
|
|
26
|
+
strongBoxAvailable: boolean;
|
|
27
|
+
strongBoxBacked: boolean;
|
|
28
|
+
securityLevel?: number;
|
|
29
|
+
securityLevelName?: AndroidSecurityLevelName;
|
|
30
|
+
strongBoxFallbackReason?: SecureHardwareFallbackReason | null;
|
|
31
|
+
};
|
|
32
|
+
ios?: {
|
|
33
|
+
secureEnclaveAvailable: boolean;
|
|
34
|
+
secureEnclaveBacked: boolean;
|
|
35
|
+
securityLevel?: number | null;
|
|
36
|
+
securityLevelName?: IOSSecurityLevelName;
|
|
37
|
+
secureEnclaveFallbackReason?: SecureHardwareFallbackReason | null;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
23
40
|
};
|
|
24
41
|
|
|
25
42
|
export type GenerateProofInput = {
|
|
@@ -52,7 +69,7 @@ type GenerateProofResult = {
|
|
|
52
69
|
|
|
53
70
|
export class DPoP {
|
|
54
71
|
public readonly proof: string;
|
|
55
|
-
public readonly alias
|
|
72
|
+
public readonly alias: string | undefined;
|
|
56
73
|
public readonly proofContext: DPoPProofContext;
|
|
57
74
|
|
|
58
75
|
private constructor(proof: string, proofContext: DPoPProofContext, alias?: string) {
|
|
@@ -93,7 +110,8 @@ export class DPoP {
|
|
|
93
110
|
input.additional ?? null,
|
|
94
111
|
input.kid ?? null,
|
|
95
112
|
input.jti ?? null,
|
|
96
|
-
|
|
113
|
+
// RN 0.75 Android bridge can crash when a nullable Double arrives as null.
|
|
114
|
+
input.iat ?? Math.floor(Date.now() / 1000),
|
|
97
115
|
input.alias ?? null
|
|
98
116
|
)) as GenerateProofResult;
|
|
99
117
|
|