@0xsequence/wallet-wdk 3.0.1 → 3.0.3
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +24 -0
- package/dist/sequence/handlers/authcode.d.ts.map +1 -1
- package/dist/sequence/handlers/authcode.js +6 -0
- package/dist/sequence/handlers/identity.d.ts +1 -0
- package/dist/sequence/handlers/identity.d.ts.map +1 -1
- package/dist/sequence/handlers/identity.js +3 -0
- package/dist/sequence/handlers/idtoken.d.ts +33 -0
- package/dist/sequence/handlers/idtoken.d.ts.map +1 -0
- package/dist/sequence/handlers/idtoken.js +110 -0
- package/dist/sequence/handlers/index.d.ts +1 -0
- package/dist/sequence/handlers/index.d.ts.map +1 -1
- package/dist/sequence/handlers/index.js +1 -0
- package/dist/sequence/manager.d.ts +20 -14
- package/dist/sequence/manager.d.ts.map +1 -1
- package/dist/sequence/manager.js +21 -3
- package/dist/sequence/sessions.d.ts.map +1 -1
- package/dist/sequence/sessions.js +5 -1
- package/dist/sequence/signers.d.ts.map +1 -1
- package/dist/sequence/signers.js +4 -0
- package/dist/sequence/types/signer.d.ts +1 -1
- package/dist/sequence/types/signer.js +1 -1
- package/dist/sequence/types/wallet.d.ts +1 -1
- package/dist/sequence/wallets.d.ts +7 -1
- package/dist/sequence/wallets.d.ts.map +1 -1
- package/dist/sequence/wallets.js +69 -7
- package/package.json +6 -6
- package/src/sequence/handlers/authcode.ts +6 -0
- package/src/sequence/handlers/identity.ts +4 -0
- package/src/sequence/handlers/idtoken.ts +140 -0
- package/src/sequence/handlers/index.ts +1 -0
- package/src/sequence/manager.ts +78 -29
- package/src/sequence/sessions.ts +7 -1
- package/src/sequence/signers.ts +5 -0
- package/src/sequence/types/signer.ts +1 -1
- package/src/sequence/types/wallet.ts +1 -1
- package/src/sequence/wallets.ts +88 -9
- package/test/authcode-pkce.test.ts +1 -1
- package/test/authcode.test.ts +2 -2
- package/test/identity-auth-dbs.test.ts +86 -2
- package/test/identity-signer.test.ts +1 -1
- package/test/idtoken.test.ts +327 -0
- package/test/sessions-idtoken.test.ts +97 -0
- package/test/signers-kindof.test.ts +22 -0
- package/test/transactions.test.ts +19 -0
- package/test/wallets.test.ts +141 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { Hash, Hex, Mnemonic, Secp256k1, Address as OxAddress } from 'ox'
|
|
3
|
+
import { Payload } from '@0xsequence/wallet-primitives'
|
|
4
|
+
import { newManager } from './constants.js'
|
|
5
|
+
import { Manager } from '../src/sequence/index.js'
|
|
6
|
+
import { Kinds } from '../src/sequence/types/signer.js'
|
|
7
|
+
|
|
8
|
+
describe('Sessions ID token attestation', () => {
|
|
9
|
+
let manager: Manager | undefined
|
|
10
|
+
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await manager?.stop()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('Should include issuer and audience hashes for google-id-token implicit session authorization', async () => {
|
|
16
|
+
manager = newManager({
|
|
17
|
+
identity: {
|
|
18
|
+
google: {
|
|
19
|
+
enabled: true,
|
|
20
|
+
clientId: 'test-google-client-id',
|
|
21
|
+
authMethod: 'id-token',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const wallet = await manager.wallets.signUp({
|
|
27
|
+
mnemonic: Mnemonic.random(Mnemonic.english),
|
|
28
|
+
kind: 'mnemonic',
|
|
29
|
+
noGuard: true,
|
|
30
|
+
})
|
|
31
|
+
expect(wallet).toBeDefined()
|
|
32
|
+
|
|
33
|
+
const signersModule = (manager as any).shared.modules.signers
|
|
34
|
+
vi.spyOn(signersModule, 'kindOf').mockResolvedValue(Kinds.LoginGoogle)
|
|
35
|
+
|
|
36
|
+
const sessionAddress = OxAddress.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() }))
|
|
37
|
+
const requestId = await manager.sessions.prepareAuthorizeImplicitSession(wallet!, sessionAddress, {
|
|
38
|
+
target: 'https://example.com',
|
|
39
|
+
applicationData: '0x1234',
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const request = await manager.signatures.get(requestId)
|
|
43
|
+
expect(request.action).toBe('session-implicit-authorize')
|
|
44
|
+
expect(Payload.isSessionImplicitAuthorize(request.envelope.payload)).toBe(true)
|
|
45
|
+
|
|
46
|
+
if (!Payload.isSessionImplicitAuthorize(request.envelope.payload)) {
|
|
47
|
+
throw new Error('Expected session implicit authorize payload')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const attestation = request.envelope.payload.attestation
|
|
51
|
+
expect(Hex.fromBytes(attestation.issuerHash)).toBe(Hash.keccak256(Hex.fromString('https://accounts.google.com')))
|
|
52
|
+
expect(Hex.fromBytes(attestation.audienceHash)).toBe(Hash.keccak256(Hex.fromString('test-google-client-id')))
|
|
53
|
+
expect(Hex.fromBytes(attestation.applicationData)).toBe('0x1234')
|
|
54
|
+
expect(Hex.fromBytes(attestation.identityType)).toBe('0x00000002')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('Should include issuer and audience hashes for apple implicit session authorization', async () => {
|
|
58
|
+
manager = newManager({
|
|
59
|
+
identity: {
|
|
60
|
+
apple: {
|
|
61
|
+
enabled: true,
|
|
62
|
+
clientId: 'test-apple-client-id',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const wallet = await manager.wallets.signUp({
|
|
68
|
+
mnemonic: Mnemonic.random(Mnemonic.english),
|
|
69
|
+
kind: 'mnemonic',
|
|
70
|
+
noGuard: true,
|
|
71
|
+
})
|
|
72
|
+
expect(wallet).toBeDefined()
|
|
73
|
+
|
|
74
|
+
const signersModule = (manager as any).shared.modules.signers
|
|
75
|
+
vi.spyOn(signersModule, 'kindOf').mockResolvedValue(Kinds.LoginApple)
|
|
76
|
+
|
|
77
|
+
const sessionAddress = OxAddress.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() }))
|
|
78
|
+
const requestId = await manager.sessions.prepareAuthorizeImplicitSession(wallet!, sessionAddress, {
|
|
79
|
+
target: 'https://example.com',
|
|
80
|
+
applicationData: '0x1234',
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const request = await manager.signatures.get(requestId)
|
|
84
|
+
expect(request.action).toBe('session-implicit-authorize')
|
|
85
|
+
expect(Payload.isSessionImplicitAuthorize(request.envelope.payload)).toBe(true)
|
|
86
|
+
|
|
87
|
+
if (!Payload.isSessionImplicitAuthorize(request.envelope.payload)) {
|
|
88
|
+
throw new Error('Expected session implicit authorize payload')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const attestation = request.envelope.payload.attestation
|
|
92
|
+
expect(Hex.fromBytes(attestation.issuerHash)).toBe(Hash.keccak256(Hex.fromString('https://appleid.apple.com')))
|
|
93
|
+
expect(Hex.fromBytes(attestation.audienceHash)).toBe(Hash.keccak256(Hex.fromString('test-apple-client-id')))
|
|
94
|
+
expect(Hex.fromBytes(attestation.applicationData)).toBe('0x1234')
|
|
95
|
+
expect(Hex.fromBytes(attestation.identityType)).toBe('0x00000002')
|
|
96
|
+
})
|
|
97
|
+
})
|
|
@@ -37,4 +37,26 @@ describe('Signers.kindOf', () => {
|
|
|
37
37
|
await signers.kindOf(wallet, '0x2222222222222222222222222222222222222222')
|
|
38
38
|
expect(getWitnessFor).toHaveBeenCalledTimes(1)
|
|
39
39
|
})
|
|
40
|
+
|
|
41
|
+
it('normalizes legacy Google PKCE signer kind to the canonical Google signer kind', async () => {
|
|
42
|
+
const getWitnessFor = vi.fn().mockResolvedValue({
|
|
43
|
+
payload: {
|
|
44
|
+
type: 'message',
|
|
45
|
+
message: '0x' + Buffer.from(JSON.stringify({ signerKind: 'login-google-pkce' }), 'utf8').toString('hex'),
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const manager = newManager({
|
|
50
|
+
stateProvider: {
|
|
51
|
+
getWitnessFor,
|
|
52
|
+
getWitnessForSapient: vi.fn(),
|
|
53
|
+
} as any,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const signers = (manager as any).shared.modules.signers
|
|
57
|
+
const wallet = '0x1111111111111111111111111111111111111111'
|
|
58
|
+
const signer = '0x2222222222222222222222222222222222222222'
|
|
59
|
+
|
|
60
|
+
await expect(signers.kindOf(wallet, signer)).resolves.toBe(Kinds.LoginGoogle)
|
|
61
|
+
})
|
|
40
62
|
})
|
|
@@ -481,12 +481,31 @@ describe('Transactions', () => {
|
|
|
481
481
|
const txId1 = manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [
|
|
482
482
|
{
|
|
483
483
|
to: wallet!,
|
|
484
|
+
data: '0x1234',
|
|
484
485
|
},
|
|
485
486
|
])
|
|
486
487
|
|
|
487
488
|
await expect(txId1).rejects.toThrow()
|
|
488
489
|
})
|
|
489
490
|
|
|
491
|
+
it('Should allow native token transfer to self in safe mode', async () => {
|
|
492
|
+
const manager = newManager()
|
|
493
|
+
const wallet = await manager.wallets.signUp({
|
|
494
|
+
mnemonic: Mnemonic.random(Mnemonic.english),
|
|
495
|
+
kind: 'mnemonic',
|
|
496
|
+
noGuard: true,
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
const txId1 = await manager.transactions.request(wallet!, Network.ChainId.ARBITRUM, [
|
|
500
|
+
{
|
|
501
|
+
to: wallet!,
|
|
502
|
+
value: 1n,
|
|
503
|
+
},
|
|
504
|
+
])
|
|
505
|
+
|
|
506
|
+
expect(txId1).toBeDefined()
|
|
507
|
+
})
|
|
508
|
+
|
|
490
509
|
it('Should allow transactions to self in unsafe mode', async () => {
|
|
491
510
|
const manager = newManager()
|
|
492
511
|
const wallet = await manager.wallets.signUp({
|
package/test/wallets.test.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from 'vitest'
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { Manager, SignerActionable, SignerReady } from '../src/sequence/index.js'
|
|
3
3
|
import { Mnemonic, Address } from 'ox'
|
|
4
4
|
import { newManager } from './constants.js'
|
|
5
5
|
import { Config, Constants, Network } from '@0xsequence/wallet-primitives'
|
|
6
|
+
import { AuthCodePkceHandler } from '../src/sequence/handlers/authcode-pkce.js'
|
|
7
|
+
import { IdTokenHandler } from '../src/sequence/handlers/idtoken.js'
|
|
8
|
+
import { IdentitySigner } from '../src/identity/signer.js'
|
|
9
|
+
import { MnemonicHandler } from '../src/sequence/handlers/mnemonic.js'
|
|
10
|
+
import { Kinds } from '../src/sequence/types/signer.js'
|
|
6
11
|
|
|
7
12
|
describe('Wallets', () => {
|
|
8
13
|
let manager: Manager | undefined
|
|
@@ -24,6 +29,141 @@ describe('Wallets', () => {
|
|
|
24
29
|
await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy()
|
|
25
30
|
})
|
|
26
31
|
|
|
32
|
+
it('Should create a new wallet using google-id-token when Google ID token auth is enabled', async () => {
|
|
33
|
+
manager = newManager({
|
|
34
|
+
identity: {
|
|
35
|
+
google: {
|
|
36
|
+
enabled: true,
|
|
37
|
+
clientId: 'test-google-client-id',
|
|
38
|
+
authMethod: 'id-token',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as IdTokenHandler
|
|
44
|
+
const loginMnemonic = Mnemonic.random(Mnemonic.english)
|
|
45
|
+
const loginSigner = MnemonicHandler.toSigner(loginMnemonic)
|
|
46
|
+
if (!loginSigner) {
|
|
47
|
+
throw new Error('Failed to create login signer for test')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const completeAuthSpy = vi
|
|
51
|
+
.spyOn(handler, 'completeAuth')
|
|
52
|
+
.mockResolvedValue([loginSigner as unknown as IdentitySigner, { email: 'google-user@example.com' }])
|
|
53
|
+
|
|
54
|
+
const wallet = await manager.wallets.signUp({
|
|
55
|
+
kind: 'google-id-token',
|
|
56
|
+
idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.',
|
|
57
|
+
noGuard: true,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
expect(wallet).toBeDefined()
|
|
61
|
+
expect(completeAuthSpy).toHaveBeenCalledWith('eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.')
|
|
62
|
+
await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy()
|
|
63
|
+
|
|
64
|
+
const walletEntry = await manager.wallets.get(wallet!)
|
|
65
|
+
expect(walletEntry).toBeDefined()
|
|
66
|
+
expect(walletEntry!.loginType).toBe(Kinds.LoginGoogle)
|
|
67
|
+
expect(walletEntry!.loginEmail).toBe('google-user@example.com')
|
|
68
|
+
|
|
69
|
+
const configuration = await manager.wallets.getConfiguration(wallet!)
|
|
70
|
+
expect(configuration.login).toHaveLength(1)
|
|
71
|
+
expect(configuration.login[0]!.kind).toBe(Kinds.LoginGoogle)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('Should register and unregister Google ID token UI callbacks through the manager', async () => {
|
|
75
|
+
manager = newManager({
|
|
76
|
+
identity: {
|
|
77
|
+
google: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
clientId: 'test-google-client-id',
|
|
80
|
+
authMethod: 'id-token',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as IdTokenHandler
|
|
86
|
+
const promptIdToken = vi.fn()
|
|
87
|
+
|
|
88
|
+
const unregister = manager.registerIdTokenUI(promptIdToken)
|
|
89
|
+
|
|
90
|
+
expect(handler['onPromptIdToken']).toBe(promptIdToken)
|
|
91
|
+
|
|
92
|
+
unregister()
|
|
93
|
+
|
|
94
|
+
expect(handler['onPromptIdToken']).toBeUndefined()
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('Should keep Google PKCE redirect flow as the default when authMethod is not specified', async () => {
|
|
98
|
+
manager = newManager({
|
|
99
|
+
identity: {
|
|
100
|
+
google: {
|
|
101
|
+
enabled: true,
|
|
102
|
+
clientId: 'test-google-client-id',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const handler = (manager as any).shared.handlers.get(Kinds.LoginGoogle) as AuthCodePkceHandler
|
|
108
|
+
expect(handler).toBeInstanceOf(AuthCodePkceHandler)
|
|
109
|
+
|
|
110
|
+
const commitAuthSpy = vi
|
|
111
|
+
.spyOn(handler, 'commitAuth')
|
|
112
|
+
.mockResolvedValue('https://accounts.google.com/o/oauth2/v2/auth?state=test-state')
|
|
113
|
+
|
|
114
|
+
const url = await manager.wallets.startSignUpWithRedirect({
|
|
115
|
+
kind: 'google-pkce',
|
|
116
|
+
target: '/auth/return',
|
|
117
|
+
metadata: {},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
expect(url).toBe('https://accounts.google.com/o/oauth2/v2/auth?state=test-state')
|
|
121
|
+
expect(commitAuthSpy).toHaveBeenCalledWith('/auth/return', true)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('Should reject google-id-token signup when Google is configured for redirect auth', async () => {
|
|
125
|
+
manager = newManager({
|
|
126
|
+
identity: {
|
|
127
|
+
google: {
|
|
128
|
+
enabled: true,
|
|
129
|
+
clientId: 'test-google-client-id',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
await expect(
|
|
135
|
+
manager.wallets.signUp({
|
|
136
|
+
kind: 'google-id-token',
|
|
137
|
+
idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.',
|
|
138
|
+
noGuard: true,
|
|
139
|
+
}),
|
|
140
|
+
).rejects.toThrow('handler-does-not-support-id-token')
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('Should reject custom ID token signup when the provider uses redirect auth', async () => {
|
|
144
|
+
manager = newManager({
|
|
145
|
+
identity: {
|
|
146
|
+
customProviders: [
|
|
147
|
+
{
|
|
148
|
+
kind: 'custom-oidc',
|
|
149
|
+
authMethod: 'authcode',
|
|
150
|
+
issuer: 'https://issuer.example.com',
|
|
151
|
+
oauthUrl: 'https://issuer.example.com/oauth/authorize',
|
|
152
|
+
clientId: 'test-custom-client-id',
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
await expect(
|
|
159
|
+
manager.wallets.signUp({
|
|
160
|
+
kind: 'custom-oidc',
|
|
161
|
+
idToken: 'eyJhbGciOiJub25lIn0.eyJleHAiOjQxMDI0NDQ4MDB9.',
|
|
162
|
+
noGuard: true,
|
|
163
|
+
}),
|
|
164
|
+
).rejects.toThrow('handler-does-not-support-id-token')
|
|
165
|
+
})
|
|
166
|
+
|
|
27
167
|
it('Should get a specific wallet by address', async () => {
|
|
28
168
|
manager = newManager()
|
|
29
169
|
const mnemonic = Mnemonic.random(Mnemonic.english)
|