@authon/react-native-wallet 0.0.0-stage → 0.1.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.
Files changed (159) hide show
  1. package/README.md +221 -2
  2. package/android/build.gradle +40 -0
  3. package/android/consumer-rules.pro +10 -0
  4. package/android/src/main/AndroidManifest.xml +4 -0
  5. package/android/src/main/java/dev/authon/reactnative/wallet/AuthonWalletModule.kt +181 -0
  6. package/android/src/vendor/GENERATED.md +4 -0
  7. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AuthonWallet.kt +191 -0
  8. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bytes.kt +74 -0
  9. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/CredentialManagerPasskeys.kt +192 -0
  10. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DeviceStore.kt +170 -0
  11. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DialogWalletApprover.kt +141 -0
  12. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Eip712.kt +176 -0
  13. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmAccount.kt +45 -0
  14. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmTransaction.kt +180 -0
  15. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/GfTables.kt +44 -0
  16. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/HdWallet.kt +130 -0
  17. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Http.kt +82 -0
  18. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/JsValues.kt +83 -0
  19. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/KeystoreDeviceStore.kt +232 -0
  20. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Prf.kt +111 -0
  21. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Proof.kt +134 -0
  22. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Shamir.kt +106 -0
  23. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/SignSummary.kt +335 -0
  24. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletApi.kt +353 -0
  25. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletError.kt +58 -0
  26. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletOps.kt +449 -0
  27. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletTypes.kt +71 -0
  28. package/build/authonContext.d.ts +19 -0
  29. package/build/authonContext.d.ts.map +1 -0
  30. package/build/authonContext.js +20 -0
  31. package/build/authonContext.js.map +1 -0
  32. package/build/client.d.ts +76 -0
  33. package/build/client.d.ts.map +1 -0
  34. package/build/client.js +349 -0
  35. package/build/client.js.map +1 -0
  36. package/build/errors.d.ts +18 -0
  37. package/build/errors.d.ts.map +1 -0
  38. package/build/errors.js +9 -0
  39. package/build/errors.js.map +1 -0
  40. package/build/index.d.ts +7 -0
  41. package/build/index.d.ts.map +1 -0
  42. package/build/index.js +4 -0
  43. package/build/index.js.map +1 -0
  44. package/build/native.d.ts +39 -0
  45. package/build/native.d.ts.map +1 -0
  46. package/build/native.js +12 -0
  47. package/build/native.js.map +1 -0
  48. package/build/types.d.ts +121 -0
  49. package/build/types.d.ts.map +1 -0
  50. package/build/types.js +3 -0
  51. package/build/types.js.map +1 -0
  52. package/build/useAuthonWallet.d.ts +25 -0
  53. package/build/useAuthonWallet.d.ts.map +1 -0
  54. package/build/useAuthonWallet.js +77 -0
  55. package/build/useAuthonWallet.js.map +1 -0
  56. package/expo-module.config.json +9 -0
  57. package/ios/AuthonReactNativeWallet.podspec +37 -0
  58. package/ios/AuthonWalletModule.swift +190 -0
  59. package/ios/vendor/AuthonWallet/AuthonWallet.swift +212 -0
  60. package/ios/vendor/AuthonWallet/Crypto/BIP39Wordlist.swift +262 -0
  61. package/ios/vendor/AuthonWallet/Crypto/HDKey.swift +108 -0
  62. package/ios/vendor/AuthonWallet/Crypto/Secp256k1.swift +104 -0
  63. package/ios/vendor/AuthonWallet/Crypto/Shamir.swift +123 -0
  64. package/ios/vendor/AuthonWallet/EVM/Address.swift +57 -0
  65. package/ios/vendor/AuthonWallet/EVM/EvmAccount.swift +48 -0
  66. package/ios/vendor/AuthonWallet/EVM/Transaction.swift +165 -0
  67. package/ios/vendor/AuthonWallet/EVM/TypedData.swift +193 -0
  68. package/ios/vendor/AuthonWallet/Support/BigInteger.swift +220 -0
  69. package/ios/vendor/AuthonWallet/Support/Bytes.swift +89 -0
  70. package/ios/vendor/AuthonWallet/Support/JSONValue.swift +341 -0
  71. package/ios/vendor/AuthonWallet/UI/WalletApprover.swift +250 -0
  72. package/ios/vendor/AuthonWallet/Wallet/Backup.swift +69 -0
  73. package/ios/vendor/AuthonWallet/Wallet/DeviceStore.swift +351 -0
  74. package/ios/vendor/AuthonWallet/Wallet/Passkeys.swift +190 -0
  75. package/ios/vendor/AuthonWallet/Wallet/Proof.swift +143 -0
  76. package/ios/vendor/AuthonWallet/Wallet/Shares.swift +116 -0
  77. package/ios/vendor/AuthonWallet/Wallet/Summary.swift +342 -0
  78. package/ios/vendor/AuthonWallet/Wallet/WalletAPI.swift +350 -0
  79. package/ios/vendor/AuthonWallet/Wallet/WalletOps.swift +555 -0
  80. package/ios/vendor/AuthonWallet/WalletError.swift +54 -0
  81. package/ios/vendor/CKeccak/LICENSE +22 -0
  82. package/ios/vendor/CKeccak/README.md +27 -0
  83. package/ios/vendor/CKeccak/byte_order.h +59 -0
  84. package/ios/vendor/CKeccak/include/ckeccak.h +9 -0
  85. package/ios/vendor/CKeccak/memzero.c +75 -0
  86. package/ios/vendor/CKeccak/memzero.h +8 -0
  87. package/ios/vendor/CKeccak/options.h +99 -0
  88. package/ios/vendor/CKeccak/sha3.c +413 -0
  89. package/ios/vendor/CKeccak/sha3.h +89 -0
  90. package/ios/vendor/CSecp256k1/COPYING +19 -0
  91. package/ios/vendor/CSecp256k1/README.md +35 -0
  92. package/ios/vendor/CSecp256k1/build/precomputed_ecmult.c +3 -0
  93. package/ios/vendor/CSecp256k1/build/precomputed_ecmult_gen.c +3 -0
  94. package/ios/vendor/CSecp256k1/build/secp256k1.c +3 -0
  95. package/ios/vendor/CSecp256k1/include/secp256k1.h +899 -0
  96. package/ios/vendor/CSecp256k1/include/secp256k1_preallocated.h +134 -0
  97. package/ios/vendor/CSecp256k1/include/secp256k1_recovery.h +113 -0
  98. package/ios/vendor/CSecp256k1/src/assumptions.h +87 -0
  99. package/ios/vendor/CSecp256k1/src/checkmem.h +102 -0
  100. package/ios/vendor/CSecp256k1/src/ecdsa.h +21 -0
  101. package/ios/vendor/CSecp256k1/src/ecdsa_impl.h +304 -0
  102. package/ios/vendor/CSecp256k1/src/eckey.h +25 -0
  103. package/ios/vendor/CSecp256k1/src/eckey_impl.h +92 -0
  104. package/ios/vendor/CSecp256k1/src/ecmult.h +61 -0
  105. package/ios/vendor/CSecp256k1/src/ecmult_const.h +38 -0
  106. package/ios/vendor/CSecp256k1/src/ecmult_const_impl.h +399 -0
  107. package/ios/vendor/CSecp256k1/src/ecmult_gen.h +143 -0
  108. package/ios/vendor/CSecp256k1/src/ecmult_gen_impl.h +341 -0
  109. package/ios/vendor/CSecp256k1/src/ecmult_impl.h +853 -0
  110. package/ios/vendor/CSecp256k1/src/field.h +350 -0
  111. package/ios/vendor/CSecp256k1/src/field_10x26.h +57 -0
  112. package/ios/vendor/CSecp256k1/src/field_10x26_impl.h +1232 -0
  113. package/ios/vendor/CSecp256k1/src/field_5x52.h +62 -0
  114. package/ios/vendor/CSecp256k1/src/field_5x52_impl.h +522 -0
  115. package/ios/vendor/CSecp256k1/src/field_5x52_int128_impl.h +274 -0
  116. package/ios/vendor/CSecp256k1/src/field_impl.h +457 -0
  117. package/ios/vendor/CSecp256k1/src/group.h +212 -0
  118. package/ios/vendor/CSecp256k1/src/group_impl.h +974 -0
  119. package/ios/vendor/CSecp256k1/src/hash.h +44 -0
  120. package/ios/vendor/CSecp256k1/src/hash_impl.h +299 -0
  121. package/ios/vendor/CSecp256k1/src/hsort.h +33 -0
  122. package/ios/vendor/CSecp256k1/src/hsort_impl.h +125 -0
  123. package/ios/vendor/CSecp256k1/src/int128.h +90 -0
  124. package/ios/vendor/CSecp256k1/src/int128_impl.h +18 -0
  125. package/ios/vendor/CSecp256k1/src/int128_native.h +19 -0
  126. package/ios/vendor/CSecp256k1/src/int128_native_impl.h +94 -0
  127. package/ios/vendor/CSecp256k1/src/int128_struct.h +14 -0
  128. package/ios/vendor/CSecp256k1/src/int128_struct_impl.h +205 -0
  129. package/ios/vendor/CSecp256k1/src/modinv32.h +43 -0
  130. package/ios/vendor/CSecp256k1/src/modinv32_impl.h +725 -0
  131. package/ios/vendor/CSecp256k1/src/modinv64.h +47 -0
  132. package/ios/vendor/CSecp256k1/src/modinv64_impl.h +780 -0
  133. package/ios/vendor/CSecp256k1/src/modules/recovery/main_impl.h +159 -0
  134. package/ios/vendor/CSecp256k1/src/precomputed_ecmult.c +16456 -0
  135. package/ios/vendor/CSecp256k1/src/precomputed_ecmult.h +38 -0
  136. package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.c +1779 -0
  137. package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.h +26 -0
  138. package/ios/vendor/CSecp256k1/src/scalar.h +105 -0
  139. package/ios/vendor/CSecp256k1/src/scalar_4x64.h +19 -0
  140. package/ios/vendor/CSecp256k1/src/scalar_4x64_impl.h +1000 -0
  141. package/ios/vendor/CSecp256k1/src/scalar_8x32.h +19 -0
  142. package/ios/vendor/CSecp256k1/src/scalar_8x32_impl.h +816 -0
  143. package/ios/vendor/CSecp256k1/src/scalar_impl.h +321 -0
  144. package/ios/vendor/CSecp256k1/src/scalar_low.h +24 -0
  145. package/ios/vendor/CSecp256k1/src/scalar_low_impl.h +206 -0
  146. package/ios/vendor/CSecp256k1/src/scratch.h +44 -0
  147. package/ios/vendor/CSecp256k1/src/scratch_impl.h +99 -0
  148. package/ios/vendor/CSecp256k1/src/secp256k1.c +831 -0
  149. package/ios/vendor/CSecp256k1/src/selftest.h +32 -0
  150. package/ios/vendor/CSecp256k1/src/util.h +451 -0
  151. package/ios/vendor/GENERATED.md +4 -0
  152. package/package.json +65 -3
  153. package/src/authonContext.ts +30 -0
  154. package/src/client.ts +375 -0
  155. package/src/errors.ts +52 -0
  156. package/src/index.ts +21 -0
  157. package/src/native.ts +50 -0
  158. package/src/types.ts +120 -0
  159. package/src/useAuthonWallet.ts +103 -0
@@ -0,0 +1,353 @@
1
+ // GENERATED by scripts/sync-native-wallet.mjs from kotlin/authon/src/main/kotlin/dev/authon/sdk/wallet/WalletApi.kt. Do not edit: change the source and run the script.
2
+ package dev.authon.sdk.wallet
3
+
4
+ import kotlinx.serialization.json.JsonArray
5
+ import kotlinx.serialization.json.JsonElement
6
+ import kotlinx.serialization.json.JsonNull
7
+ import kotlinx.serialization.json.JsonObject
8
+ import kotlinx.serialization.json.JsonObjectBuilder
9
+ import kotlinx.serialization.json.JsonPrimitive
10
+ import kotlinx.serialization.json.booleanOrNull
11
+ import kotlinx.serialization.json.buildJsonObject
12
+ import kotlinx.serialization.json.contentOrNull
13
+ import kotlinx.serialization.json.intOrNull
14
+ import kotlinx.serialization.json.longOrNull
15
+ import kotlinx.serialization.json.put
16
+ import okhttp3.OkHttpClient
17
+ import java.io.IOException
18
+ import java.net.URLEncoder
19
+
20
+ data class WalletDevice(
21
+ val id: String,
22
+ val label: String?,
23
+ val shareVersion: Int?,
24
+ val createdAt: String?,
25
+ val lastUsedAt: String?,
26
+ val revokedAt: String?,
27
+ )
28
+
29
+ data class ServerWallet(
30
+ val id: String,
31
+ val address: String,
32
+ val status: String,
33
+ val protected: Boolean,
34
+ /** Null without a backup; false for a legacy backup that has no passkey public key. */
35
+ val backupVerifiable: Boolean?,
36
+ val shareVersion: Int,
37
+ val devices: List<WalletDevice>,
38
+ )
39
+
40
+ /** `GET /v1/wallet/app-config` for this app (platform=android, appId=package name). */
41
+ data class WalletAppConfig(
42
+ val allowed: Boolean,
43
+ val approved: Boolean,
44
+ val projectId: String?,
45
+ val enabled: Boolean,
46
+ val chainIds: List<Long>,
47
+ /** Missing means true: signing needs a confirmation. */
48
+ val confirmSigning: Boolean,
49
+ /** PRF salt version for new backups. Missing means 1. */
50
+ val prfSaltVersion: Int,
51
+ val rpId: String,
52
+ val minFrameProtocol: Int?,
53
+ )
54
+
55
+ internal data class DeviceProof(val deviceId: String, val challenge: String, val signature: String) {
56
+ fun fill(b: JsonObjectBuilder) {
57
+ b.put("deviceId", deviceId)
58
+ b.put("challenge", challenge)
59
+ b.put("signature", signature)
60
+ }
61
+ }
62
+
63
+ internal data class RestoreStart(
64
+ val backupCiphertext: String?,
65
+ val credentialId: String?,
66
+ val companyShare: String?,
67
+ val upgradeRecommended: Boolean,
68
+ val passkeyChallenge: String?,
69
+ )
70
+
71
+ internal data class CreatedWallet(val wallet: ServerWallet?, val deviceId: String, val walletId: String, val shareVersion: Int)
72
+
73
+ internal data class EnrolledDevice(val deviceId: String, val shareVersion: Int)
74
+
75
+ /** HTTP contract of `apps/wallet/src/api.ts` (protocol 2), byte for byte in paths, headers and bodies. */
76
+ internal class WalletApi(
77
+ apiUrl: String,
78
+ private val publishableKey: String,
79
+ private val client: OkHttpClient = Http.defaultClient,
80
+ ) {
81
+ private val base = apiUrl.trimEnd('/')
82
+
83
+ private suspend fun call(method: String, path: String, session: String?, body: JsonObject? = null): HttpResult {
84
+ val headers = LinkedHashMap<String, String>()
85
+ headers["x-api-key"] = publishableKey
86
+ headers["accept"] = "application/json"
87
+ if (session != null) headers["authorization"] = "Bearer $session"
88
+ if (body != null) headers["content-type"] = "application/json"
89
+ headers["user-agent"] = Http.userAgent()
90
+ return try {
91
+ Http.execute(client, Http.request("$base/v1/wallet$path", method, headers, body))
92
+ } catch (_: IOException) {
93
+ throw walletError(WalletErrorCode.API_ERROR, "network error")
94
+ }
95
+ }
96
+
97
+ private fun ensureOk(res: HttpResult): JsonObject {
98
+ if (res.ok) return res.json
99
+ val serverCode = res.json.str("code")
100
+ if (serverCode != null && serverCode in RESTORE_CODES) throw walletError(WalletErrorCode.NEEDS_RESTORE)
101
+ if (serverCode != null && serverCode in KNOWN_CODES) throw walletError(WalletErrorCode.valueOf(serverCode))
102
+ throw walletError(STATUS_CODES[res.status] ?: WalletErrorCode.API_ERROR)
103
+ }
104
+
105
+ suspend fun appConfig(packageName: String): WalletAppConfig {
106
+ val res = call("GET", "/app-config?platform=android&appId=${enc(packageName)}", null)
107
+ if (res.status == 403 || res.status == 404) {
108
+ return WalletAppConfig(false, false, null, false, emptyList(), true, 1, DEFAULT_RP_ID, null)
109
+ }
110
+ val data = ensureOk(res)
111
+ val wc = data["walletConfig"] as? JsonObject ?: JsonObject(emptyMap())
112
+ return WalletAppConfig(
113
+ allowed = data.bool("allowed") != false,
114
+ approved = data.bool("approved") == true,
115
+ projectId = data.str("projectId"),
116
+ enabled = wc.bool("enabled") != false,
117
+ chainIds = (wc["chainIds"] as? JsonArray)?.mapNotNull { (it as? JsonPrimitive)?.longOrNull } ?: emptyList(),
118
+ confirmSigning = wc.bool("confirmSigning") != false,
119
+ prfSaltVersion = if ((wc["prfSaltVersion"] as? JsonPrimitive)?.intOrNull == 2) 2 else 1,
120
+ rpId = data.str("rpId") ?: DEFAULT_RP_ID,
121
+ minFrameProtocol = (data["minFrameProtocol"] as? JsonPrimitive)?.intOrNull,
122
+ )
123
+ }
124
+
125
+ suspend fun getMe(session: String): ServerWallet? {
126
+ val res = call("GET", "/me", session)
127
+ if (res.status == 404) return null
128
+ val data = ensureOk(res)
129
+ return normalizeWallet(data["wallet"]?.takeIf { it !is JsonNull } ?: res.json)
130
+ }
131
+
132
+ suspend fun proofChallenge(session: String, kind: String): ProofNonce {
133
+ val data = ensureOk(call("POST", "/me/proof-challenge", session, buildJsonObject { put("kind", kind) }))
134
+ val nonce = data.str("nonce")
135
+ val expiresAt = (data["expiresAt"] as? JsonPrimitive)?.takeIf { !it.isString }?.longOrNull
136
+ if (nonce == null || expiresAt == null) throw walletError(WalletErrorCode.API_ERROR)
137
+ return ProofNonce(nonce, expiresAt)
138
+ }
139
+
140
+ suspend fun createWallet(
141
+ session: String,
142
+ address: String,
143
+ companyShare: String,
144
+ devicePublicKey: DevicePublicJwk,
145
+ proof: String,
146
+ bound: ProofNonce,
147
+ ): CreatedWallet {
148
+ val body = buildJsonObject {
149
+ put("address", address)
150
+ put("companyShare", companyShare)
151
+ put("devicePublicKey", devicePublicKey.toJson())
152
+ put("proof", proof)
153
+ put("nonce", bound.nonce)
154
+ put("nonceExpiresAt", bound.expiresAt)
155
+ put("protocol", FRAME_PROTOCOL)
156
+ }
157
+ val data = ensureOk(call("POST", "/me", session, body))
158
+ val wallet = when {
159
+ data["wallet"] is JsonObject -> normalizeWallet(data["wallet"]!!)
160
+ data["id"] != null && data["id"] !is JsonNull -> normalizeWallet(data)
161
+ else -> null
162
+ }
163
+ val device = data["device"] as? JsonObject
164
+ return CreatedWallet(
165
+ wallet = wallet,
166
+ walletId = data.str("walletId") ?: wallet?.id ?: "undefined",
167
+ deviceId = data.str("deviceId") ?: device?.str("id") ?: "undefined",
168
+ shareVersion = data.int("shareVersion") ?: wallet?.shareVersion ?: 1,
169
+ )
170
+ }
171
+
172
+ suspend fun confirmWallet(session: String, proof: DeviceProof) {
173
+ ensureOk(call("POST", "/me/confirm", session, buildJsonObject { proof.fill(this) }))
174
+ }
175
+
176
+ suspend fun discardPending(session: String) {
177
+ val res = call("DELETE", "/me/pending", session)
178
+ if (res.status == 404) return
179
+ ensureOk(res)
180
+ }
181
+
182
+ suspend fun challenge(session: String, deviceId: String): String {
183
+ val data = ensureOk(call("POST", "/me/share/challenge", session, buildJsonObject { put("deviceId", deviceId) }))
184
+ return data.str("challenge") ?: throw walletError(WalletErrorCode.API_ERROR)
185
+ }
186
+
187
+ suspend fun releaseShare(session: String, proof: DeviceProof): String {
188
+ val data = ensureOk(call("POST", "/me/share", session, buildJsonObject { proof.fill(this) }))
189
+ return data.str("companyShare") ?: throw walletError(WalletErrorCode.API_ERROR)
190
+ }
191
+
192
+ class BackupKeyFields(val credentialPublicKey: String?, val credentialAlg: Int?, val attestationObject: String?)
193
+
194
+ suspend fun putBackup(
195
+ session: String,
196
+ proof: DeviceProof,
197
+ backupCiphertext: String,
198
+ credentialId: String,
199
+ newCompanyShare: String,
200
+ key: BackupKeyFields,
201
+ upgrade: Boolean,
202
+ ): Int {
203
+ val body = buildJsonObject {
204
+ proof.fill(this)
205
+ put("backupCiphertext", backupCiphertext)
206
+ put("credentialId", credentialId)
207
+ put("newCompanyShare", newCompanyShare)
208
+ if (key.credentialPublicKey != null && key.credentialAlg != null) {
209
+ put("credentialPublicKey", key.credentialPublicKey)
210
+ put("credentialAlg", key.credentialAlg)
211
+ } else if (key.attestationObject != null) {
212
+ put("attestationObject", key.attestationObject)
213
+ }
214
+ if (upgrade) put("upgrade", true)
215
+ put("protocol", FRAME_PROTOCOL)
216
+ }
217
+ val data = ensureOk(call("PUT", "/me/backup", session, body))
218
+ return data.int("shareVersion") ?: throw walletError(WalletErrorCode.API_ERROR)
219
+ }
220
+
221
+ suspend fun restoreStart(session: String): RestoreStart {
222
+ val data = ensureOk(call("POST", "/me/restore/start", session, JsonObject(emptyMap())))
223
+ val companyShare = data.str("companyShare")
224
+ val passkeyChallenge = data.str("passkeyChallenge")
225
+ if (companyShare == null && passkeyChallenge == null) throw walletError(WalletErrorCode.API_ERROR)
226
+ return RestoreStart(
227
+ backupCiphertext = data.str("backupCiphertext"),
228
+ credentialId = data.str("credentialId"),
229
+ companyShare = companyShare,
230
+ passkeyChallenge = passkeyChallenge,
231
+ upgradeRecommended = companyShare != null || data.bool("upgradeRecommended") == true,
232
+ )
233
+ }
234
+
235
+ suspend fun restoreAssert(session: String, assertion: PasskeyAssertion, challenge: String): String {
236
+ val body = buildJsonObject {
237
+ put("credentialId", assertion.credentialId)
238
+ put("clientDataJSON", assertion.clientDataJSON)
239
+ put("authenticatorData", assertion.authenticatorData)
240
+ put("signature", assertion.signature)
241
+ put("challenge", challenge)
242
+ }
243
+ val data = ensureOk(call("POST", "/me/restore/assert", session, body))
244
+ return data.str("companyShare") ?: throw walletError(WalletErrorCode.API_ERROR)
245
+ }
246
+
247
+ suspend fun restore(
248
+ session: String,
249
+ newCompanyShare: String,
250
+ newBackupCiphertext: String,
251
+ newDevicePublicKey: DevicePublicJwk,
252
+ credentialId: String,
253
+ proof: String,
254
+ bound: ProofNonce,
255
+ ): EnrolledDevice {
256
+ val body = buildJsonObject {
257
+ put("newCompanyShare", newCompanyShare)
258
+ put("newBackupCiphertext", newBackupCiphertext)
259
+ put("newDevicePublicKey", newDevicePublicKey.toJson())
260
+ put("credentialId", credentialId)
261
+ put("proof", proof)
262
+ put("nonce", bound.nonce)
263
+ put("nonceExpiresAt", bound.expiresAt)
264
+ put("protocol", FRAME_PROTOCOL)
265
+ }
266
+ val data = ensureOk(call("POST", "/me/restore", session, body))
267
+ return EnrolledDevice(
268
+ data.str("deviceId") ?: (data["device"] as? JsonObject)?.str("id") ?: "undefined",
269
+ data.int("shareVersion") ?: throw walletError(WalletErrorCode.API_ERROR),
270
+ )
271
+ }
272
+
273
+ suspend fun addDevice(session: String, devicePublicKey: DevicePublicJwk, proof: String, bound: ProofNonce): EnrolledDevice {
274
+ val body = buildJsonObject {
275
+ put("devicePublicKey", devicePublicKey.toJson())
276
+ put("proof", proof)
277
+ put("nonce", bound.nonce)
278
+ put("nonceExpiresAt", bound.expiresAt)
279
+ put("protocol", FRAME_PROTOCOL)
280
+ }
281
+ val data = ensureOk(call("POST", "/me/devices", session, body))
282
+ return EnrolledDevice(
283
+ data.str("deviceId") ?: "undefined",
284
+ data.int("shareVersion") ?: throw walletError(WalletErrorCode.API_ERROR),
285
+ )
286
+ }
287
+
288
+ suspend fun revokeDevice(session: String, deviceId: String, proof: DeviceProof) {
289
+ ensureOk(call("DELETE", "/me/devices/${enc(deviceId)}", session, buildJsonObject { proof.fill(this) }))
290
+ }
291
+
292
+ companion object {
293
+ /** Protocol sent as `protocol` in proof and backup requests (the API's CURRENT_FRAME_PROTOCOL). */
294
+ const val FRAME_PROTOCOL = 2
295
+ const val DEFAULT_RP_ID = "wallet.authon.dev"
296
+
297
+ private val STATUS_CODES = mapOf(
298
+ 400 to WalletErrorCode.INVALID_REQUEST,
299
+ 401 to WalletErrorCode.UNAUTHORIZED,
300
+ 403 to WalletErrorCode.FORBIDDEN,
301
+ 404 to WalletErrorCode.NOT_FOUND,
302
+ 409 to WalletErrorCode.ALREADY_EXISTS,
303
+ 426 to WalletErrorCode.FRAME_OUTDATED,
304
+ 429 to WalletErrorCode.RATE_LIMITED,
305
+ )
306
+ private val RESTORE_CODES = setOf("NEEDS_RESTORE", "DEVICE_REVOKED", "DEVICE_NOT_FOUND", "STALE_SHARE_VERSION")
307
+ private val KNOWN_CODES = setOf(
308
+ "NOT_PROTECTED", "ORIGIN_NOT_ALLOWED", "ALREADY_EXISTS", "NOT_FOUND", "LAST_DEVICE", "SESSION_REQUIRED",
309
+ "WALLET_PENDING", "RATE_LIMITED", "FRAME_OUTDATED", "BACKUP_KEY_REQUIRED", "ALREADY_UPGRADED",
310
+ )
311
+
312
+ /** encodeURIComponent. */
313
+ fun enc(value: String): String = URLEncoder.encode(value, "UTF-8")
314
+ .replace("+", "%20").replace("%21", "!").replace("%27", "'")
315
+ .replace("%28", "(").replace("%29", ")").replace("%7E", "~")
316
+
317
+ private fun JsonObject.str(key: String): String? = (this[key] as? JsonPrimitive)?.takeIf { it.isString }?.content
318
+
319
+ private fun JsonObject.bool(key: String): Boolean? = (this[key] as? JsonPrimitive)?.takeIf { !it.isString }?.booleanOrNull
320
+
321
+ /** `Number(x)` for counters the server sends as numbers (or numeric strings). */
322
+ private fun JsonObject.int(key: String): Int? = (this[key] as? JsonPrimitive)?.contentOrNull?.toBigDecimalOrNull()?.toInt()
323
+
324
+ private fun JsonElement.asObj(): JsonObject = this as? JsonObject ?: JsonObject(emptyMap())
325
+
326
+ fun normalizeWallet(raw: JsonElement): ServerWallet {
327
+ val w = raw.asObj()
328
+ val prot = w.bool("protected") ?: (w["protectedAt"].let { it != null && it !is JsonNull && (it as? JsonPrimitive)?.contentOrNull?.isNotEmpty() == true })
329
+ return ServerWallet(
330
+ id = w.anyString("id") ?: w.anyString("walletId") ?: "undefined",
331
+ address = w.anyString("address") ?: "undefined",
332
+ status = w.anyString("status") ?: "active",
333
+ protected = prot,
334
+ backupVerifiable = w.bool("backupVerifiable"),
335
+ shareVersion = w.int("shareVersion") ?: 1,
336
+ devices = (w["devices"] as? JsonArray)?.mapNotNull { d ->
337
+ val o = d as? JsonObject ?: return@mapNotNull null
338
+ WalletDevice(
339
+ id = o.anyString("id") ?: return@mapNotNull null,
340
+ label = o.str("label"),
341
+ shareVersion = o.int("shareVersion"),
342
+ createdAt = o.str("createdAt"),
343
+ lastUsedAt = o.str("lastUsedAt"),
344
+ revokedAt = o.str("revokedAt"),
345
+ )
346
+ } ?: emptyList(),
347
+ )
348
+ }
349
+
350
+ private fun JsonObject.anyString(key: String): String? =
351
+ (this[key] as? JsonPrimitive)?.takeIf { it !is JsonNull }?.contentOrNull
352
+ }
353
+ }
@@ -0,0 +1,58 @@
1
+ // GENERATED by scripts/sync-native-wallet.mjs from kotlin/authon/src/main/kotlin/dev/authon/sdk/wallet/WalletError.kt. Do not edit: change the source and run the script.
2
+ package dev.authon.sdk.wallet
3
+
4
+ /** Error codes of the web wallet frame (`apps/wallet/src/errors.ts`); [name] is the wire string. */
5
+ enum class WalletErrorCode {
6
+ NOT_FOUND,
7
+ NEEDS_RESTORE,
8
+ NOT_PROTECTED,
9
+ PRF_UNSUPPORTED,
10
+ ORIGIN_NOT_ALLOWED,
11
+ USER_CANCELLED,
12
+ NEEDS_POPUP,
13
+ INVALID_REQUEST,
14
+ UNSUPPORTED_TYPE,
15
+ UNAUTHORIZED,
16
+ FORBIDDEN,
17
+ ALREADY_EXISTS,
18
+ RATE_LIMITED,
19
+ ADDRESS_MISMATCH,
20
+ STORAGE_UNAVAILABLE,
21
+ API_ERROR,
22
+ INTERNAL,
23
+ LAST_DEVICE,
24
+ SESSION_REQUIRED,
25
+ WALLET_PENDING,
26
+
27
+ /** The approval UI could not be shown. Nothing was signed. */
28
+ CONFIRM_WINDOW_BLOCKED,
29
+
30
+ /** The approval UI was dismissed before the user approved. Nothing was signed. */
31
+ CONFIRM_WINDOW_CLOSED,
32
+
33
+ /** The message or typed data is too long to show in full; it is refused. */
34
+ MESSAGE_TOO_LARGE,
35
+
36
+ /** The API no longer serves this SDK protocol version. */
37
+ FRAME_OUTDATED,
38
+
39
+ /** The backup passkey did not report a usable public key. */
40
+ BACKUP_KEY_REQUIRED,
41
+
42
+ /** upgradeBackup on a wallet whose backup is already verifiable. */
43
+ ALREADY_UPGRADED,
44
+
45
+ /** The wallet predates HD wallets and has only account 0. */
46
+ ACCOUNT_UNAVAILABLE,
47
+ }
48
+
49
+ class WalletException(
50
+ val code: WalletErrorCode,
51
+ message: String? = null,
52
+ cause: Throwable? = null,
53
+ ) : Exception(message ?: code.name, cause)
54
+
55
+ internal fun walletError(code: WalletErrorCode, message: String? = null): WalletException = WalletException(code, message)
56
+
57
+ internal fun Throwable.toWalletException(): WalletException =
58
+ this as? WalletException ?: WalletException(WalletErrorCode.INTERNAL, "unexpected error", this)