@zerodev/wallet-core 0.0.1-alpha.18 → 0.0.1-alpha.19
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/dist/_cjs/actions/auth/getOAuthLoginUrl.js +18 -0
- package/dist/_cjs/actions/auth/getOAuthLoginUrl.js.map +1 -0
- package/dist/_cjs/actions/auth/getWhoami.js +2 -2
- package/dist/_cjs/actions/auth/getWhoami.js.map +1 -1
- package/dist/_cjs/actions/auth/index.js +3 -1
- package/dist/_cjs/actions/auth/index.js.map +1 -1
- package/dist/_cjs/actions/auth/loginWithStamp.js +5 -5
- package/dist/_cjs/actions/auth/loginWithStamp.js.map +1 -1
- package/dist/_cjs/actions/index.js +2 -1
- package/dist/_cjs/actions/index.js.map +1 -1
- package/dist/_cjs/actions/wallet/signingUtils.js +2 -2
- package/dist/_cjs/actions/wallet/signingUtils.js.map +1 -1
- package/dist/_cjs/client/createClient.js +5 -5
- package/dist/_cjs/client/createClient.js.map +1 -1
- package/dist/_cjs/client/decorators/client.js +1 -0
- package/dist/_cjs/client/decorators/client.js.map +1 -1
- package/dist/_cjs/client/transports/createTransport.js +5 -5
- package/dist/_cjs/client/transports/createTransport.js.map +1 -1
- package/dist/_cjs/client/transports/rest.js +5 -5
- package/dist/_cjs/client/transports/rest.js.map +1 -1
- package/dist/_cjs/core/createZeroDevWallet.js +29 -61
- package/dist/_cjs/core/createZeroDevWallet.js.map +1 -1
- package/dist/_cjs/index.js.map +1 -1
- package/dist/_cjs/stampers/indexedDbStamper.js +17 -2
- package/dist/_cjs/stampers/indexedDbStamper.js.map +1 -1
- package/dist/_cjs/stampers/webauthnStamper.js +23 -3
- package/dist/_cjs/stampers/webauthnStamper.js.map +1 -1
- package/dist/_cjs/utils/exportPrivateKey.js +1 -1
- package/dist/_cjs/utils/exportPrivateKey.js.map +1 -1
- package/dist/_cjs/utils/exportWallet.js +2 -6
- package/dist/_cjs/utils/exportWallet.js.map +1 -1
- package/dist/_cjs/utils/hpke.js +4 -15
- package/dist/_cjs/utils/hpke.js.map +1 -1
- package/dist/_cjs/utils/utils.js +5 -6
- package/dist/_cjs/utils/utils.js.map +1 -1
- package/dist/_esm/actions/auth/getOAuthLoginUrl.js +23 -0
- package/dist/_esm/actions/auth/getOAuthLoginUrl.js.map +1 -0
- package/dist/_esm/actions/auth/getWhoami.js +2 -2
- package/dist/_esm/actions/auth/getWhoami.js.map +1 -1
- package/dist/_esm/actions/auth/index.js +1 -0
- package/dist/_esm/actions/auth/index.js.map +1 -1
- package/dist/_esm/actions/auth/loginWithStamp.js +5 -5
- package/dist/_esm/actions/auth/loginWithStamp.js.map +1 -1
- package/dist/_esm/actions/index.js +1 -1
- package/dist/_esm/actions/index.js.map +1 -1
- package/dist/_esm/actions/wallet/signingUtils.js +2 -2
- package/dist/_esm/actions/wallet/signingUtils.js.map +1 -1
- package/dist/_esm/client/createClient.js +5 -5
- package/dist/_esm/client/createClient.js.map +1 -1
- package/dist/_esm/client/decorators/client.js +2 -1
- package/dist/_esm/client/decorators/client.js.map +1 -1
- package/dist/_esm/client/transports/createTransport.js +5 -5
- package/dist/_esm/client/transports/createTransport.js.map +1 -1
- package/dist/_esm/client/transports/rest.js +5 -5
- package/dist/_esm/client/transports/rest.js.map +1 -1
- package/dist/_esm/core/createZeroDevWallet.js +30 -62
- package/dist/_esm/core/createZeroDevWallet.js.map +1 -1
- package/dist/_esm/index.js.map +1 -1
- package/dist/_esm/stampers/indexedDbStamper.js +17 -2
- package/dist/_esm/stampers/indexedDbStamper.js.map +1 -1
- package/dist/_esm/stampers/webauthnStamper.js +23 -4
- package/dist/_esm/stampers/webauthnStamper.js.map +1 -1
- package/dist/_esm/utils/exportPrivateKey.js +1 -1
- package/dist/_esm/utils/exportPrivateKey.js.map +1 -1
- package/dist/_esm/utils/exportWallet.js +2 -6
- package/dist/_esm/utils/exportWallet.js.map +1 -1
- package/dist/_esm/utils/hpke.js +6 -22
- package/dist/_esm/utils/hpke.js.map +1 -1
- package/dist/_esm/utils/utils.js +5 -6
- package/dist/_esm/utils/utils.js.map +1 -1
- package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts +30 -0
- package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts.map +1 -0
- package/dist/_types/actions/auth/index.d.ts +1 -0
- package/dist/_types/actions/auth/index.d.ts.map +1 -1
- package/dist/_types/actions/auth/loginWithStamp.d.ts +2 -1
- package/dist/_types/actions/auth/loginWithStamp.d.ts.map +1 -1
- package/dist/_types/actions/index.d.ts +1 -1
- package/dist/_types/actions/index.d.ts.map +1 -1
- package/dist/_types/client/decorators/client.d.ts +7 -1
- package/dist/_types/client/decorators/client.d.ts.map +1 -1
- package/dist/_types/client/transports/rest.d.ts +5 -4
- package/dist/_types/client/transports/rest.d.ts.map +1 -1
- package/dist/_types/client/types.d.ts +9 -9
- package/dist/_types/client/types.d.ts.map +1 -1
- package/dist/_types/core/createZeroDevWallet.d.ts +3 -0
- package/dist/_types/core/createZeroDevWallet.d.ts.map +1 -1
- package/dist/_types/index.d.ts +1 -1
- package/dist/_types/index.d.ts.map +1 -1
- package/dist/_types/stampers/index.d.ts +1 -1
- package/dist/_types/stampers/index.d.ts.map +1 -1
- package/dist/_types/stampers/indexedDbStamper.d.ts +2 -2
- package/dist/_types/stampers/indexedDbStamper.d.ts.map +1 -1
- package/dist/_types/stampers/types.d.ts +31 -5
- package/dist/_types/stampers/types.d.ts.map +1 -1
- package/dist/_types/stampers/webauthnStamper.d.ts +2 -2
- package/dist/_types/stampers/webauthnStamper.d.ts.map +1 -1
- package/dist/_types/types/session.d.ts +2 -3
- package/dist/_types/types/session.d.ts.map +1 -1
- package/dist/_types/utils/buildClientSignature.d.ts +3 -3
- package/dist/_types/utils/buildClientSignature.d.ts.map +1 -1
- package/dist/_types/utils/exportWallet.d.ts.map +1 -1
- package/dist/_types/utils/hpke.d.ts.map +1 -1
- package/dist/_types/utils/utils.d.ts.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/actions/auth/getOAuthLoginUrl.ts +48 -0
- package/src/actions/auth/getWhoami.ts +2 -2
- package/src/actions/auth/index.ts +5 -0
- package/src/actions/auth/loginWithStamp.ts +7 -6
- package/src/actions/index.ts +3 -0
- package/src/actions/wallet/signingUtils.ts +2 -2
- package/src/client/createClient.ts +6 -6
- package/src/client/decorators/client.ts +13 -0
- package/src/client/transports/createTransport.ts +5 -5
- package/src/client/transports/rest.ts +11 -10
- package/src/client/types.ts +9 -9
- package/src/core/createZeroDevWallet.ts +35 -77
- package/src/index.ts +5 -2
- package/src/stampers/index.ts +2 -2
- package/src/stampers/indexedDbStamper.ts +24 -4
- package/src/stampers/types.ts +33 -5
- package/src/stampers/webauthnStamper.ts +27 -6
- package/src/types/session.ts +2 -3
- package/src/utils/buildClientSignature.ts +3 -3
- package/src/utils/exportPrivateKey.ts +1 -1
- package/src/utils/exportWallet.ts +2 -6
- package/src/utils/hpke.ts +7 -33
- package/src/utils/utils.ts +5 -6
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { IndexedDbStamper as TurnkeyIndexedDbStamper } from '@turnkey/indexed-db-stamper'
|
|
2
|
-
import
|
|
2
|
+
import { generateCompressedPublicKeyFromKeyPair } from '../utils/utils.js'
|
|
3
|
+
import type { ApiKeyStamper } from './types.js'
|
|
3
4
|
|
|
4
|
-
export async function createIndexedDbStamper(): Promise<
|
|
5
|
+
export async function createIndexedDbStamper(): Promise<ApiKeyStamper> {
|
|
5
6
|
const inner = new TurnkeyIndexedDbStamper()
|
|
6
7
|
await inner.init()
|
|
7
8
|
|
|
9
|
+
let pendingKeyPair: CryptoKeyPair | null = null
|
|
10
|
+
|
|
8
11
|
return {
|
|
9
12
|
async getPublicKey() {
|
|
10
13
|
return await inner.getPublicKey()
|
|
@@ -15,8 +18,25 @@ export async function createIndexedDbStamper(): Promise<IndexedDbStamper> {
|
|
|
15
18
|
async clear() {
|
|
16
19
|
await inner.clear()
|
|
17
20
|
},
|
|
18
|
-
async resetKeyPair(
|
|
19
|
-
|
|
21
|
+
async resetKeyPair() {
|
|
22
|
+
pendingKeyPair = null
|
|
23
|
+
await inner.resetKeyPair()
|
|
24
|
+
},
|
|
25
|
+
async prepareKeyRotation() {
|
|
26
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
27
|
+
{ name: 'ECDSA', namedCurve: 'P-256' },
|
|
28
|
+
false,
|
|
29
|
+
['sign', 'verify'],
|
|
30
|
+
)
|
|
31
|
+
pendingKeyPair = keyPair
|
|
32
|
+
return await generateCompressedPublicKeyFromKeyPair(keyPair)
|
|
33
|
+
},
|
|
34
|
+
async commitKeyRotation() {
|
|
35
|
+
if (!pendingKeyPair) {
|
|
36
|
+
throw new Error('No pending key rotation to commit')
|
|
37
|
+
}
|
|
38
|
+
await inner.resetKeyPair(pendingKeyPair)
|
|
39
|
+
pendingKeyPair = null
|
|
20
40
|
},
|
|
21
41
|
}
|
|
22
42
|
}
|
package/src/stampers/types.ts
CHANGED
|
@@ -5,8 +5,6 @@ export type Stamp = {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export type Stamper = {
|
|
8
|
-
/** retrieve public key compressed or otherwise as per the stamper */
|
|
9
|
-
getPublicKey: () => Promise<string | null>
|
|
10
8
|
/** produce Turnkey header value for a given request body */
|
|
11
9
|
stamp: (payload: string) => Promise<Stamp>
|
|
12
10
|
/** clear local state (embedded key, IDB keypair, etc.) */
|
|
@@ -16,6 +14,8 @@ export type Stamper = {
|
|
|
16
14
|
export type KeyFormat = 'Hexadecimal' | 'Solana'
|
|
17
15
|
|
|
18
16
|
export type IframeStamper = Stamper & {
|
|
17
|
+
/** retrieve public key compressed or otherwise as per the stamper */
|
|
18
|
+
getPublicKey: () => Promise<string | null>
|
|
19
19
|
init(): Promise<string>
|
|
20
20
|
injectCredentialBundle(bundle: string): Promise<boolean>
|
|
21
21
|
injectWalletExportBundle(
|
|
@@ -30,7 +30,35 @@ export type IframeStamper = Stamper & {
|
|
|
30
30
|
applySettings(settings: { styles?: Record<string, string> }): Promise<boolean>
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export type
|
|
34
|
-
|
|
33
|
+
export type ApiKeyStamper = Stamper & {
|
|
34
|
+
/** retrieve public key compressed or otherwise as per the stamper */
|
|
35
|
+
getPublicKey: () => Promise<string | null>
|
|
36
|
+
/** Generate + activate a new key pair immediately (simple cases: login init, logout). */
|
|
37
|
+
resetKeyPair: () => Promise<void>
|
|
38
|
+
/** Generate a new key pair internally, return its compressed public key, but keep the OLD key active for stamp(). */
|
|
39
|
+
prepareKeyRotation: () => Promise<string>
|
|
40
|
+
/** Promote the pending key to active. Call after the server accepts the new key. */
|
|
41
|
+
commitKeyRotation: () => Promise<void>
|
|
42
|
+
}
|
|
43
|
+
export type Attestation = {
|
|
44
|
+
attestationObject: string
|
|
45
|
+
clientDataJson: string
|
|
46
|
+
credentialId: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type PasskeyRegistrationOptions = {
|
|
50
|
+
rp: { id: string; name: string }
|
|
51
|
+
userName: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type PasskeyRegistrationResult = {
|
|
55
|
+
attestation: Attestation
|
|
56
|
+
encodedChallenge: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type PasskeyStamper = Stamper & {
|
|
60
|
+
/** Create a new passkey credential. Owns challenge and user ID generation internally. */
|
|
61
|
+
register: (
|
|
62
|
+
options: PasskeyRegistrationOptions,
|
|
63
|
+
) => Promise<PasskeyRegistrationResult>
|
|
35
64
|
}
|
|
36
|
-
export type WebauthnStamper = Stamper
|
|
@@ -1,21 +1,42 @@
|
|
|
1
|
+
import { getWebAuthnAttestation } from '@turnkey/http'
|
|
1
2
|
import { WebauthnStamper as TurnkeyWebauthnStamper } from '@turnkey/webauthn-stamper'
|
|
2
|
-
import
|
|
3
|
+
import { base64UrlEncode, generateRandomBuffer } from '../utils/utils.js'
|
|
4
|
+
import type { PasskeyRegistrationOptions, PasskeyStamper } from './types.js'
|
|
3
5
|
|
|
4
6
|
export async function createWebauthnStamper({
|
|
5
7
|
rpId,
|
|
6
8
|
}: {
|
|
7
9
|
rpId: string
|
|
8
|
-
}): Promise<
|
|
10
|
+
}): Promise<PasskeyStamper> {
|
|
9
11
|
const inner = new TurnkeyWebauthnStamper({ rpId })
|
|
10
12
|
|
|
11
13
|
return {
|
|
12
|
-
async getPublicKey() {
|
|
13
|
-
// return await inner.();
|
|
14
|
-
return null
|
|
15
|
-
},
|
|
16
14
|
async stamp(payload: string) {
|
|
17
15
|
return await inner.stamp(payload)
|
|
18
16
|
},
|
|
19
17
|
async clear() {},
|
|
18
|
+
async register(options: PasskeyRegistrationOptions) {
|
|
19
|
+
const challenge = generateRandomBuffer()
|
|
20
|
+
const encodedChallenge = base64UrlEncode(challenge)
|
|
21
|
+
const authenticatorUserId = generateRandomBuffer()
|
|
22
|
+
|
|
23
|
+
const attestation = await getWebAuthnAttestation({
|
|
24
|
+
publicKey: {
|
|
25
|
+
rp: options.rp,
|
|
26
|
+
challenge,
|
|
27
|
+
pubKeyCredParams: [
|
|
28
|
+
{ type: 'public-key', alg: -7 },
|
|
29
|
+
{ type: 'public-key', alg: -257 },
|
|
30
|
+
],
|
|
31
|
+
user: {
|
|
32
|
+
id: authenticatorUserId,
|
|
33
|
+
name: options.userName,
|
|
34
|
+
displayName: options.userName,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return { attestation, encodedChallenge }
|
|
40
|
+
},
|
|
20
41
|
}
|
|
21
42
|
}
|
package/src/types/session.ts
CHANGED
|
@@ -3,7 +3,7 @@ export enum SessionType {
|
|
|
3
3
|
READ_WRITE = 'SESSION_TYPE_READ_WRITE',
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
export type StamperType = '
|
|
6
|
+
export type StamperType = 'apiKey' | 'passkey'
|
|
7
7
|
|
|
8
8
|
export type ZeroDevWalletSession = {
|
|
9
9
|
id: string
|
|
@@ -11,8 +11,7 @@ export type ZeroDevWalletSession = {
|
|
|
11
11
|
organizationId: string
|
|
12
12
|
stamperType: StamperType
|
|
13
13
|
sessionType?: SessionType
|
|
14
|
-
token
|
|
15
|
-
publicKey?: string
|
|
14
|
+
token: string
|
|
16
15
|
expiry: number
|
|
17
16
|
createdAt: number
|
|
18
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApiKeyStamper } from '../stampers/types.js'
|
|
2
2
|
import { derToRawSignature } from './derToRawSignature.js'
|
|
3
3
|
|
|
4
4
|
export type BuildClientSignatureParams = {
|
|
@@ -6,8 +6,8 @@ export type BuildClientSignatureParams = {
|
|
|
6
6
|
verificationToken: string
|
|
7
7
|
/** The compressed public key hex */
|
|
8
8
|
publicKey: string
|
|
9
|
-
/** The
|
|
10
|
-
stamper:
|
|
9
|
+
/** The API key stamper for signing */
|
|
10
|
+
stamper: ApiKeyStamper
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -72,7 +72,7 @@ export async function exportPrivateKey(
|
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
const stamperKey =
|
|
75
|
-
session.stamperType === '
|
|
75
|
+
session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
|
|
76
76
|
const stamper = wallet.client[stamperKey]
|
|
77
77
|
if (!stamper) {
|
|
78
78
|
throw new Error(`Stamper '${stamperKey}' not found on wallet.client`)
|
|
@@ -56,9 +56,7 @@ export async function exportWallet(
|
|
|
56
56
|
|
|
57
57
|
const listWalletsStamp =
|
|
58
58
|
await wallet.client[
|
|
59
|
-
session.stamperType === '
|
|
60
|
-
? 'indexedDbStamper'
|
|
61
|
-
: 'webauthnStamper'
|
|
59
|
+
session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
|
|
62
60
|
].stamp(listWalletsBody)
|
|
63
61
|
if (!listWalletsStamp) {
|
|
64
62
|
throw new Error('Failed to stamp list wallets body')
|
|
@@ -93,9 +91,7 @@ export async function exportWallet(
|
|
|
93
91
|
})
|
|
94
92
|
const exportWalletStamp =
|
|
95
93
|
await wallet.client[
|
|
96
|
-
session.stamperType === '
|
|
97
|
-
? 'indexedDbStamper'
|
|
98
|
-
: 'webauthnStamper'
|
|
94
|
+
session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
|
|
99
95
|
].stamp(exportWalletBody)
|
|
100
96
|
if (!exportWalletStamp) {
|
|
101
97
|
throw new Error('Failed to stamp export wallet body')
|
package/src/utils/hpke.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* - tkhq/go-sdk/pkg/enclave_encrypt
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { gcm } from '@noble/ciphers/aes.js'
|
|
18
19
|
import { p256 } from '@noble/curves/nist.js'
|
|
19
20
|
import { expand, extract } from '@noble/hashes/hkdf.js'
|
|
20
21
|
import { sha256 } from '@noble/hashes/sha2.js'
|
|
@@ -165,42 +166,15 @@ function keySchedule(
|
|
|
165
166
|
return { key, baseNonce }
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
// noble/v2 returns) under strict TS lib settings because the underlying buffer
|
|
170
|
-
// could in principle be a SharedArrayBuffer. Copy into a fresh ArrayBuffer to
|
|
171
|
-
// satisfy the type.
|
|
172
|
-
function toArrayBuffer(u8: Uint8Array): ArrayBuffer {
|
|
173
|
-
const out = new ArrayBuffer(u8.byteLength)
|
|
174
|
-
new Uint8Array(out).set(u8)
|
|
175
|
-
return out
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function aesGcmSeal(
|
|
169
|
+
function aesGcmSeal(
|
|
179
170
|
key: Uint8Array,
|
|
180
171
|
nonce: Uint8Array,
|
|
181
172
|
aad: Uint8Array,
|
|
182
173
|
plaintext: Uint8Array,
|
|
183
|
-
):
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
'raw',
|
|
188
|
-
toArrayBuffer(key),
|
|
189
|
-
{ name: 'AES-GCM' },
|
|
190
|
-
/* extractable */ false,
|
|
191
|
-
['encrypt'],
|
|
192
|
-
)
|
|
193
|
-
const ct = await crypto.subtle.encrypt(
|
|
194
|
-
{
|
|
195
|
-
name: 'AES-GCM',
|
|
196
|
-
iv: toArrayBuffer(nonce),
|
|
197
|
-
additionalData: toArrayBuffer(aad),
|
|
198
|
-
tagLength: 128,
|
|
199
|
-
},
|
|
200
|
-
cryptoKey,
|
|
201
|
-
toArrayBuffer(plaintext),
|
|
202
|
-
)
|
|
203
|
-
return new Uint8Array(ct)
|
|
174
|
+
): Uint8Array {
|
|
175
|
+
// Returns ciphertext || tag (16 bytes appended) — matches the single-blob
|
|
176
|
+
// format Turnkey's `Sealer.Seal` and Web Crypto's AES-GCM produce.
|
|
177
|
+
return gcm(key, nonce, aad).encrypt(plaintext)
|
|
204
178
|
}
|
|
205
179
|
|
|
206
180
|
export type HpkeSealResult = {
|
|
@@ -239,7 +213,7 @@ export async function hpkeSealP256({
|
|
|
239
213
|
|
|
240
214
|
// First message of the context, sequence 0 → nonce = base_nonce.
|
|
241
215
|
const aad = concat(enc, receiverPublicKey)
|
|
242
|
-
const ciphertext =
|
|
216
|
+
const ciphertext = aesGcmSeal(key, baseNonce, aad, plaintext)
|
|
243
217
|
|
|
244
218
|
return { encappedPublic: enc, ciphertext }
|
|
245
219
|
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -18,7 +18,8 @@ export function parseSession(
|
|
|
18
18
|
throw new Error('Invalid JWT: Missing payload')
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')
|
|
22
|
+
const decoded = JSON.parse(atob(base64))
|
|
22
23
|
const {
|
|
23
24
|
exp,
|
|
24
25
|
public_key: publicKey,
|
|
@@ -68,11 +69,9 @@ export const generateRandomBuffer = (): ArrayBuffer => {
|
|
|
68
69
|
* @returns {string} - The encoded challenge.
|
|
69
70
|
*/
|
|
70
71
|
export const base64UrlEncode = (challenge: ArrayBuffer): string => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.replace(/\//g, '_')
|
|
75
|
-
.replace(/=/g, '')
|
|
72
|
+
const bytes = new Uint8Array(challenge)
|
|
73
|
+
const binary = String.fromCharCode(...bytes)
|
|
74
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
/**
|