@reown/appkit-controllers 1.7.20-turbo-fix.0 → 1.7.20
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/esm/exports/features.js +2 -0
- package/dist/esm/exports/features.js.map +1 -0
- package/dist/esm/exports/testing.js +17 -0
- package/dist/esm/exports/testing.js.map +1 -1
- package/dist/esm/src/controllers/BlockchainApiController.js +1 -5
- package/dist/esm/src/controllers/BlockchainApiController.js.map +1 -1
- package/dist/esm/src/controllers/EventsController.js +6 -1
- package/dist/esm/src/controllers/EventsController.js.map +1 -1
- package/dist/esm/src/controllers/ExchangeController.js +1 -1
- package/dist/esm/src/controllers/ExchangeController.js.map +1 -1
- package/dist/esm/src/features/reown-authentication/ReownAuthentication.js +290 -0
- package/dist/esm/src/features/reown-authentication/ReownAuthentication.js.map +1 -0
- package/dist/esm/src/features/reown-authentication/ReownAuthenticationMessenger.js +55 -0
- package/dist/esm/src/features/reown-authentication/ReownAuthenticationMessenger.js.map +1 -0
- package/dist/esm/src/features/reown-authentication/index.js +3 -0
- package/dist/esm/src/features/reown-authentication/index.js.map +1 -0
- package/dist/esm/src/utils/ConstantsUtil.js +6 -3
- package/dist/esm/src/utils/ConstantsUtil.js.map +1 -1
- package/dist/esm/src/utils/SIWXUtil.js +2 -0
- package/dist/esm/src/utils/SIWXUtil.js.map +1 -1
- package/dist/esm/tests/controllers/BlockchainApiController.test.js +1 -1
- package/dist/esm/tests/controllers/BlockchainApiController.test.js.map +1 -1
- package/dist/esm/tests/controllers/ExchangeController.test.js +1 -1
- package/dist/esm/tests/controllers/ExchangeController.test.js.map +1 -1
- package/dist/esm/tests/features/ReownAuthentication.test.js +911 -0
- package/dist/esm/tests/features/ReownAuthentication.test.js.map +1 -0
- package/dist/esm/tests/utils/SIWXUtil.test.js +18 -2
- package/dist/esm/tests/utils/SIWXUtil.test.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/types/exports/features.d.ts +1 -0
- package/dist/types/exports/testing.d.ts +6 -0
- package/dist/types/src/controllers/BlockchainApiController.d.ts +1 -3
- package/dist/types/src/controllers/OptionsController.d.ts +3 -1
- package/dist/types/src/features/reown-authentication/ReownAuthentication.d.ts +187 -0
- package/dist/types/src/features/reown-authentication/ReownAuthenticationMessenger.d.ts +15 -0
- package/dist/types/src/features/reown-authentication/index.d.ts +2 -0
- package/dist/types/src/utils/ChainControllerUtil.d.ts +1 -1
- package/dist/types/src/utils/ConstantsUtil.d.ts +4 -1
- package/dist/types/src/utils/TypeUtil.d.ts +15 -1
- package/dist/types/tests/features/ReownAuthentication.test.d.ts +1 -0
- package/package.json +8 -3
|
@@ -0,0 +1,911 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { NetworkUtil, SafeLocalStorage, SafeLocalStorageKeys } from '@reown/appkit-common';
|
|
3
|
+
import { AccountController, ApiController, BlockchainApiController, ChainController } from '@reown/appkit-controllers';
|
|
4
|
+
import { ReownAuthentication } from '@reown/appkit-controllers/features';
|
|
5
|
+
import { extendedMainnet, mockChainControllerState, mockSession } from '@reown/appkit-controllers/testing';
|
|
6
|
+
vi.useFakeTimers({
|
|
7
|
+
now: new Date('2024-12-05T16:02:32.905Z')
|
|
8
|
+
});
|
|
9
|
+
vi.stubGlobal('localStorage', {
|
|
10
|
+
getItem: vi.fn(),
|
|
11
|
+
setItem: vi.fn(),
|
|
12
|
+
removeItem: vi.fn()
|
|
13
|
+
});
|
|
14
|
+
const mocks = {
|
|
15
|
+
mockFetchResponse: (response, ok = true) => {
|
|
16
|
+
return {
|
|
17
|
+
ok,
|
|
18
|
+
json: async () => response,
|
|
19
|
+
text: async () => (typeof response === 'string' ? response : JSON.stringify(response)),
|
|
20
|
+
headers: {
|
|
21
|
+
get: () => 'application/json'
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
createMockJWT: (payload) => {
|
|
26
|
+
// Create a mock JWT token with proper format (header.payload.signature)
|
|
27
|
+
const header = { alg: 'HS256', typ: 'JWT' };
|
|
28
|
+
const encodedHeader = btoa(JSON.stringify(header));
|
|
29
|
+
const encodedPayload = btoa(JSON.stringify(payload));
|
|
30
|
+
const signature = 'mock_signature';
|
|
31
|
+
return `${encodedHeader}.${encodedPayload}.${signature}`;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
describe.each([
|
|
35
|
+
{ namespace: 'eip155', id: 1, address: '0x1234567890abcdef1234567890abcdef12345678' },
|
|
36
|
+
{
|
|
37
|
+
namespace: 'solana',
|
|
38
|
+
id: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
39
|
+
address: '2VqKhjZ766ZN3uBtBpb7Ls3cN4HrocP1rzxzekhVEgpU'
|
|
40
|
+
}
|
|
41
|
+
])('ReownAuthentication - $namespace', ({ namespace, id, address }) => {
|
|
42
|
+
let siwx;
|
|
43
|
+
let mockJWT;
|
|
44
|
+
beforeAll(() => {
|
|
45
|
+
global.fetch = vi.fn();
|
|
46
|
+
// Mock document.location properly
|
|
47
|
+
Object.defineProperty(global, 'document', {
|
|
48
|
+
value: {
|
|
49
|
+
location: {
|
|
50
|
+
host: 'mocked.com',
|
|
51
|
+
href: 'http://mocked.com/'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
writable: true
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
siwx = new ReownAuthentication();
|
|
59
|
+
// Create a shared mock JWT for all tests
|
|
60
|
+
mockJWT = mocks.createMockJWT({
|
|
61
|
+
aud: 'test-audience',
|
|
62
|
+
iss: 'test-issuer',
|
|
63
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
64
|
+
projectIdKey: 'test-project-id',
|
|
65
|
+
sub: 'test-subject',
|
|
66
|
+
address: address,
|
|
67
|
+
chainId: namespace === 'eip155' ? id : id.toString(),
|
|
68
|
+
chainNamespace: namespace,
|
|
69
|
+
caip2Network: `${namespace}:${id}`,
|
|
70
|
+
uri: 'http://mocked.com/',
|
|
71
|
+
domain: 'mocked.com',
|
|
72
|
+
projectUuid: 'test-project-uuid',
|
|
73
|
+
profileUuid: 'test-profile-uuid',
|
|
74
|
+
nonce: 'mock_nonce'
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
afterAll(() => {
|
|
78
|
+
vi.clearAllMocks();
|
|
79
|
+
});
|
|
80
|
+
describe('createMessage', () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
vi.spyOn(ChainController, 'getAllRequestedCaipNetworks').mockReturnValue([
|
|
83
|
+
{
|
|
84
|
+
id: 1,
|
|
85
|
+
name: 'Ethereum Mainnet',
|
|
86
|
+
chainNamespace: 'eip155',
|
|
87
|
+
caipNetworkId: `${namespace}:${id}`
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 2,
|
|
91
|
+
name: 'Solana',
|
|
92
|
+
chainNamespace: 'solana',
|
|
93
|
+
caipNetworkId: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
|
|
94
|
+
}
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
it('creates a message', async () => {
|
|
98
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
99
|
+
const setItemSpy = vi.spyOn(localStorage, 'setItem');
|
|
100
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: 'mock_token', nonce: 'mock_nonce' }));
|
|
101
|
+
const message = await siwx.createMessage({
|
|
102
|
+
accountAddress: address,
|
|
103
|
+
chainId: `${namespace}:${id}`
|
|
104
|
+
});
|
|
105
|
+
expect(message).toEqual({
|
|
106
|
+
accountAddress: address,
|
|
107
|
+
chainId: `${namespace}:${id}`,
|
|
108
|
+
domain: 'mocked.com',
|
|
109
|
+
expirationTime: undefined,
|
|
110
|
+
issuedAt: '2024-12-05T16:02:32.905Z',
|
|
111
|
+
nonce: 'mock_nonce',
|
|
112
|
+
notBefore: undefined,
|
|
113
|
+
requestId: undefined,
|
|
114
|
+
resources: undefined,
|
|
115
|
+
statement: undefined,
|
|
116
|
+
toString: expect.any(Function),
|
|
117
|
+
uri: 'http://mocked.com/',
|
|
118
|
+
version: '1'
|
|
119
|
+
});
|
|
120
|
+
const networkName = NetworkUtil.getNetworkNameByCaipNetworkId(ChainController.getAllRequestedCaipNetworks(), `${namespace}:${id}`);
|
|
121
|
+
expect(message.toString())
|
|
122
|
+
.toBe(`mocked.com wants you to sign in with your ${networkName} account:
|
|
123
|
+
${address}
|
|
124
|
+
|
|
125
|
+
URI: http://mocked.com/
|
|
126
|
+
Version: 1
|
|
127
|
+
Chain ID: ${namespace}:${id}
|
|
128
|
+
Nonce: mock_nonce
|
|
129
|
+
Issued At: 2024-12-05T16:02:32.905Z`);
|
|
130
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/nonce?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
131
|
+
body: undefined,
|
|
132
|
+
headers: undefined,
|
|
133
|
+
method: 'GET'
|
|
134
|
+
});
|
|
135
|
+
expect(setItemSpy).toHaveBeenCalledWith('@appkit/siwx-nonce-token', 'mock_token');
|
|
136
|
+
});
|
|
137
|
+
it('should throw an text error if response is not json', async () => {
|
|
138
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
139
|
+
fetchSpy.mockResolvedValueOnce({
|
|
140
|
+
ok: false,
|
|
141
|
+
headers: {
|
|
142
|
+
get: () => 'text/plain'
|
|
143
|
+
},
|
|
144
|
+
text: async () => 'mock_error'
|
|
145
|
+
});
|
|
146
|
+
await expect(siwx.createMessage({
|
|
147
|
+
accountAddress: address,
|
|
148
|
+
chainId: `${namespace}:${id}`
|
|
149
|
+
})).rejects.toThrowError('mock_error');
|
|
150
|
+
});
|
|
151
|
+
it('should use default domain and uri if document is not available', async () => {
|
|
152
|
+
const documentSpy = vi.spyOn(global, 'document', 'get').mockReturnValue(undefined);
|
|
153
|
+
siwx = new ReownAuthentication();
|
|
154
|
+
vi.spyOn(global, 'fetch').mockResolvedValueOnce(mocks.mockFetchResponse({ token: 'mock_token', nonce: 'mock_nonce' }));
|
|
155
|
+
const message = await siwx.createMessage({
|
|
156
|
+
accountAddress: address,
|
|
157
|
+
chainId: `${namespace}:${id}`
|
|
158
|
+
});
|
|
159
|
+
expect(message.domain).toBe('Unknown Domain');
|
|
160
|
+
expect(message.uri).toBe('Unknown URI');
|
|
161
|
+
documentSpy.mockRestore();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('addSession', () => {
|
|
165
|
+
it('adds a session', async () => {
|
|
166
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
167
|
+
const setItemSpy = vi.spyOn(localStorage, 'setItem');
|
|
168
|
+
vi.spyOn(BlockchainApiController.state, 'clientId', 'get').mockReturnValueOnce(null);
|
|
169
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
170
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
171
|
+
const session = mockSession({
|
|
172
|
+
data: { accountAddress: address, chainId: `${namespace}:${id}` }
|
|
173
|
+
});
|
|
174
|
+
await siwx.addSession(session);
|
|
175
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/authenticate?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
176
|
+
body: `{"data":{"domain":"example.com","accountAddress":"${address}","statement":"This is a statement","chainId":"${namespace}:${id}","uri":"siwx://example.com","version":"1","nonce":"123"},"message":"Hello AppKit!","signature":"0x3c70e0a2d87f677dc0c3faf98fdf6313e99a3d9191bb79f7ecfce0c2cf46b7b33fd4c4bb83bca82fe872e35963382027d0d18018342d7dc36a675918cb73e9061c","clientId":null}`,
|
|
177
|
+
headers: {
|
|
178
|
+
'x-nonce-jwt': 'Bearer mock_nonce_token'
|
|
179
|
+
},
|
|
180
|
+
method: 'POST'
|
|
181
|
+
});
|
|
182
|
+
expect(setItemSpy).toHaveBeenCalledWith('@appkit/siwx-auth-token', mockJWT);
|
|
183
|
+
});
|
|
184
|
+
it('should use correct client id', async () => {
|
|
185
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
186
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
187
|
+
vi.spyOn(BlockchainApiController.state, 'clientId', 'get').mockReturnValueOnce('mock_client_id');
|
|
188
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
189
|
+
const session = mockSession({
|
|
190
|
+
data: { accountAddress: address, chainId: `${namespace}:${id}` }
|
|
191
|
+
});
|
|
192
|
+
await siwx.addSession(session);
|
|
193
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/authenticate?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
194
|
+
body: `{"data":{"domain":"example.com","accountAddress":"${address}","statement":"This is a statement","chainId":"${namespace}:${id}","uri":"siwx://example.com","version":"1","nonce":"123"},"message":"Hello AppKit!","signature":"0x3c70e0a2d87f677dc0c3faf98fdf6313e99a3d9191bb79f7ecfce0c2cf46b7b33fd4c4bb83bca82fe872e35963382027d0d18018342d7dc36a675918cb73e9061c","clientId":"mock_client_id"}`,
|
|
195
|
+
headers: {
|
|
196
|
+
'x-nonce-jwt': 'Bearer mock_nonce_token'
|
|
197
|
+
},
|
|
198
|
+
method: 'POST'
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
it.each([
|
|
202
|
+
{
|
|
203
|
+
walletInfo: {
|
|
204
|
+
name: 'mock_wallet_name',
|
|
205
|
+
icon: 'mock_wallet_icon'
|
|
206
|
+
},
|
|
207
|
+
expectedBody: `{"data":{"domain":"example.com","accountAddress":"${address}","statement":"This is a statement","chainId":"${namespace}:${id}","uri":"siwx://example.com","version":"1","nonce":"123"},"message":"Hello AppKit!","signature":"0x3c70e0a2d87f677dc0c3faf98fdf6313e99a3d9191bb79f7ecfce0c2cf46b7b33fd4c4bb83bca82fe872e35963382027d0d18018342d7dc36a675918cb73e9061c","walletInfo":{"type":"unknown","name":"mock_wallet_name","icon":"mock_wallet_icon"}}`
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
walletInfo: {
|
|
211
|
+
type: 'ANNOUNCED',
|
|
212
|
+
name: 'mock_wallet_name',
|
|
213
|
+
icon: 'mock_wallet_icon'
|
|
214
|
+
},
|
|
215
|
+
expectedBody: `{"data":{"domain":"example.com","accountAddress":"${address}","statement":"This is a statement","chainId":"${namespace}:${id}","uri":"siwx://example.com","version":"1","nonce":"123"},"message":"Hello AppKit!","signature":"0x3c70e0a2d87f677dc0c3faf98fdf6313e99a3d9191bb79f7ecfce0c2cf46b7b33fd4c4bb83bca82fe872e35963382027d0d18018342d7dc36a675918cb73e9061c","walletInfo":{"type":"extension","name":"mock_wallet_name","icon":"mock_wallet_icon"}}`
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
walletInfo: {
|
|
219
|
+
type: 'WALLET_CONNECT',
|
|
220
|
+
name: 'mock_wallet_name',
|
|
221
|
+
icon: 'mock_wallet_icon'
|
|
222
|
+
},
|
|
223
|
+
expectedBody: `{"data":{"domain":"example.com","accountAddress":"${address}","statement":"This is a statement","chainId":"${namespace}:${id}","uri":"siwx://example.com","version":"1","nonce":"123"},"message":"Hello AppKit!","signature":"0x3c70e0a2d87f677dc0c3faf98fdf6313e99a3d9191bb79f7ecfce0c2cf46b7b33fd4c4bb83bca82fe872e35963382027d0d18018342d7dc36a675918cb73e9061c","walletInfo":{"type":"walletconnect","name":"mock_wallet_name","icon":"mock_wallet_icon"}}`
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
walletInfo: {
|
|
227
|
+
type: 'AUTH',
|
|
228
|
+
name: 'AUTH',
|
|
229
|
+
social: 'google',
|
|
230
|
+
identifier: 'mock_identifier'
|
|
231
|
+
},
|
|
232
|
+
expectedBody: `{"data":{"domain":"example.com","accountAddress":"${address}","statement":"This is a statement","chainId":"${namespace}:${id}","uri":"siwx://example.com","version":"1","nonce":"123"},"message":"Hello AppKit!","signature":"0x3c70e0a2d87f677dc0c3faf98fdf6313e99a3d9191bb79f7ecfce0c2cf46b7b33fd4c4bb83bca82fe872e35963382027d0d18018342d7dc36a675918cb73e9061c","walletInfo":{"type":"social","social":"google","identifier":"mock_identifier"}}`
|
|
233
|
+
}
|
|
234
|
+
])('should use correct wallet info', async ({ walletInfo, expectedBody }) => {
|
|
235
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
236
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
237
|
+
AccountController.state.connectedWalletInfo = undefined;
|
|
238
|
+
vi.spyOn(AccountController.state, 'connectedWalletInfo', 'get').mockReturnValueOnce(walletInfo);
|
|
239
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
240
|
+
const session = mockSession({
|
|
241
|
+
data: { accountAddress: address, chainId: `${namespace}:${id}` }
|
|
242
|
+
});
|
|
243
|
+
await siwx.addSession(session);
|
|
244
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/authenticate?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
245
|
+
body: expectedBody,
|
|
246
|
+
headers: {
|
|
247
|
+
'x-nonce-jwt': 'Bearer mock_nonce_token'
|
|
248
|
+
},
|
|
249
|
+
method: 'POST'
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
describe('getSessions', () => {
|
|
254
|
+
beforeEach(() => {
|
|
255
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValue(mockJWT);
|
|
256
|
+
});
|
|
257
|
+
afterEach(() => {
|
|
258
|
+
vi.spyOn(localStorage, 'getItem').mockRestore();
|
|
259
|
+
});
|
|
260
|
+
it('gets sessions', async () => {
|
|
261
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
262
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({
|
|
263
|
+
address,
|
|
264
|
+
chainId: id,
|
|
265
|
+
caip2Network: `${namespace}:${id}`
|
|
266
|
+
}));
|
|
267
|
+
const sessions = await siwx.getSessions(`${namespace}:${id}`, address);
|
|
268
|
+
expect(sessions).toEqual([
|
|
269
|
+
{
|
|
270
|
+
data: {
|
|
271
|
+
accountAddress: address,
|
|
272
|
+
chainId: `${namespace}:${id}`
|
|
273
|
+
},
|
|
274
|
+
message: '',
|
|
275
|
+
signature: ''
|
|
276
|
+
}
|
|
277
|
+
]);
|
|
278
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/me?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
279
|
+
body: undefined,
|
|
280
|
+
headers: {
|
|
281
|
+
Authorization: `Bearer ${mockJWT}`
|
|
282
|
+
},
|
|
283
|
+
method: 'GET'
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
it('gets sessions when address is not lowercased', async () => {
|
|
287
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
288
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({
|
|
289
|
+
address,
|
|
290
|
+
chainId: id,
|
|
291
|
+
caip2Network: `${namespace}:${id}`
|
|
292
|
+
}));
|
|
293
|
+
const sessions = await siwx.getSessions(`${namespace}:${id}`, address);
|
|
294
|
+
expect(sessions).toEqual([
|
|
295
|
+
{
|
|
296
|
+
data: {
|
|
297
|
+
accountAddress: address,
|
|
298
|
+
chainId: `${namespace}:${id}`
|
|
299
|
+
},
|
|
300
|
+
message: '',
|
|
301
|
+
signature: ''
|
|
302
|
+
}
|
|
303
|
+
]);
|
|
304
|
+
});
|
|
305
|
+
it('returns empty array if session is not found', async () => {
|
|
306
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
307
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({
|
|
308
|
+
address: 'different_address',
|
|
309
|
+
chainId: id
|
|
310
|
+
}));
|
|
311
|
+
await expect(siwx.getSessions(`${namespace}:${id}`, address)).resolves.toEqual([]);
|
|
312
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({
|
|
313
|
+
address,
|
|
314
|
+
chainId: 2
|
|
315
|
+
}));
|
|
316
|
+
await expect(siwx.getSessions(`${namespace}:${id}`, address)).resolves.toEqual([]);
|
|
317
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse(null));
|
|
318
|
+
await expect(siwx.getSessions(`${namespace}:${id}`, address)).resolves.toEqual([]);
|
|
319
|
+
fetchSpy.mockRejectedValueOnce(new Error());
|
|
320
|
+
await expect(siwx.getSessions(`${namespace}:${id}`, address)).resolves.toEqual([]);
|
|
321
|
+
});
|
|
322
|
+
it('should not request session if no auth token is set', async () => {
|
|
323
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(null);
|
|
324
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
325
|
+
await expect(siwx.getSessions(`${namespace}:${id}`, address)).resolves.toEqual([]);
|
|
326
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
describe('revokeSession', () => {
|
|
330
|
+
it('revokes a session', async () => {
|
|
331
|
+
const removeItemSpy = vi.spyOn(localStorage, 'removeItem');
|
|
332
|
+
await siwx.revokeSession(`${namespace}:${id}`, '0x1234567890abcdef1234567890abcdef12345678');
|
|
333
|
+
expect(removeItemSpy).toHaveBeenCalledWith('@appkit/siwx-auth-token');
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
describe('setSessions', () => {
|
|
337
|
+
it('clears storage token if sessions are empty', async () => {
|
|
338
|
+
const removeItemSpy = vi.spyOn(localStorage, 'removeItem');
|
|
339
|
+
await siwx.setSessions([]);
|
|
340
|
+
expect(removeItemSpy).toHaveBeenCalledWith('@appkit/siwx-auth-token');
|
|
341
|
+
});
|
|
342
|
+
it('adds a session with default first item', async () => {
|
|
343
|
+
const addSessionSpy = vi.spyOn(siwx, 'addSession');
|
|
344
|
+
addSessionSpy.mockResolvedValueOnce();
|
|
345
|
+
const session = mockSession({
|
|
346
|
+
data: { accountAddress: address, chainId: `${namespace}:${id}` }
|
|
347
|
+
});
|
|
348
|
+
await siwx.setSessions([session]);
|
|
349
|
+
expect(addSessionSpy).toHaveBeenCalledWith(session);
|
|
350
|
+
});
|
|
351
|
+
it('should use the correct session if there are multiple sessions', async () => {
|
|
352
|
+
const addSessionSpy = vi.spyOn(siwx, 'addSession');
|
|
353
|
+
addSessionSpy.mockResolvedValueOnce();
|
|
354
|
+
mockChainControllerState({
|
|
355
|
+
activeCaipNetwork: {
|
|
356
|
+
...extendedMainnet,
|
|
357
|
+
id: 2,
|
|
358
|
+
caipNetworkId: 'eip155:2'
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const session = mockSession({
|
|
362
|
+
data: { accountAddress: address, chainId: `${namespace}:${id}` }
|
|
363
|
+
});
|
|
364
|
+
const session2 = mockSession({ data: { accountAddress: address, chainId: 'eip155:2' } });
|
|
365
|
+
await siwx.setSessions([session, session2]);
|
|
366
|
+
expect(addSessionSpy).toHaveBeenCalledWith(session2);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
describe('getRequired', () => {
|
|
370
|
+
it('should return true for getRequired() by default', () => {
|
|
371
|
+
expect(siwx.getRequired()).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
it('should return false for getRequired()', () => {
|
|
374
|
+
siwx = new ReownAuthentication({ required: false });
|
|
375
|
+
expect(siwx.getRequired()).toBe(false);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
describe('events', () => {
|
|
379
|
+
it('should register event listeners with on() method', () => {
|
|
380
|
+
const callback = vi.fn();
|
|
381
|
+
const unsubscribe = siwx.on('sessionChanged', callback);
|
|
382
|
+
expect(unsubscribe).toBeInstanceOf(Function);
|
|
383
|
+
});
|
|
384
|
+
it('should emit session-changed event when a session is added', async () => {
|
|
385
|
+
const callback = vi.fn();
|
|
386
|
+
siwx.on('sessionChanged', callback);
|
|
387
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
388
|
+
vi.spyOn(global, 'fetch').mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
389
|
+
const session = mockSession();
|
|
390
|
+
await siwx.addSession(session);
|
|
391
|
+
expect(callback).toHaveBeenCalledWith(session);
|
|
392
|
+
});
|
|
393
|
+
it('should emit session-changed event when sessions are retrieved', async () => {
|
|
394
|
+
const callback = vi.fn();
|
|
395
|
+
siwx.on('sessionChanged', callback);
|
|
396
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(mockJWT);
|
|
397
|
+
vi.spyOn(global, 'fetch').mockResolvedValueOnce(mocks.mockFetchResponse({
|
|
398
|
+
address: address,
|
|
399
|
+
chainId: id,
|
|
400
|
+
caip2Network: `${namespace}:${id}`
|
|
401
|
+
}));
|
|
402
|
+
await siwx.getSessions(`${namespace}:${id}`, address);
|
|
403
|
+
expect(callback).toHaveBeenCalledWith({
|
|
404
|
+
data: {
|
|
405
|
+
accountAddress: address,
|
|
406
|
+
chainId: `${namespace}:${id}`
|
|
407
|
+
},
|
|
408
|
+
message: '',
|
|
409
|
+
signature: ''
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
it('should emit session-changed event with undefined when a session is revoked', async () => {
|
|
413
|
+
const callback = vi.fn();
|
|
414
|
+
siwx.on('sessionChanged', callback);
|
|
415
|
+
await siwx.revokeSession(`${namespace}:${id}`, address);
|
|
416
|
+
expect(callback).toHaveBeenCalledWith(undefined);
|
|
417
|
+
});
|
|
418
|
+
it('should emit session-changed event with undefined when sessions are cleared', async () => {
|
|
419
|
+
const callback = vi.fn();
|
|
420
|
+
siwx.on('sessionChanged', callback);
|
|
421
|
+
await siwx.setSessions([]);
|
|
422
|
+
expect(callback).toHaveBeenCalledWith(undefined);
|
|
423
|
+
});
|
|
424
|
+
it('should properly unsubscribe listener when unsubscribe function is called', async () => {
|
|
425
|
+
const callback = vi.fn();
|
|
426
|
+
const unsubscribe = siwx.on('sessionChanged', callback);
|
|
427
|
+
unsubscribe();
|
|
428
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
429
|
+
vi.spyOn(global, 'fetch').mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
430
|
+
await siwx.addSession(mockSession());
|
|
431
|
+
expect(callback).not.toHaveBeenCalled();
|
|
432
|
+
});
|
|
433
|
+
it('should remove all listeners when removeAllListeners is called', async () => {
|
|
434
|
+
const callback1 = vi.fn();
|
|
435
|
+
const callback2 = vi.fn();
|
|
436
|
+
siwx.on('sessionChanged', callback1);
|
|
437
|
+
siwx.on('sessionChanged', callback2);
|
|
438
|
+
siwx.removeAllListeners();
|
|
439
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
440
|
+
vi.spyOn(global, 'fetch').mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
441
|
+
await siwx.addSession(mockSession());
|
|
442
|
+
expect(callback1).not.toHaveBeenCalled();
|
|
443
|
+
expect(callback2).not.toHaveBeenCalled();
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
describe('getSessionAccount', () => {
|
|
447
|
+
it('should throw an error if not authenticated', async () => {
|
|
448
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(null);
|
|
449
|
+
await expect(siwx.getSessionAccount()).rejects.toThrow('Not authenticated');
|
|
450
|
+
});
|
|
451
|
+
it('should return session account data when authenticated', async () => {
|
|
452
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(mockJWT);
|
|
453
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
454
|
+
const mockAccountData = {
|
|
455
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
456
|
+
chainId: 1,
|
|
457
|
+
aud: 'test-aud',
|
|
458
|
+
iss: 'test-iss',
|
|
459
|
+
exp: 1000000000,
|
|
460
|
+
projectIdKey: 'test-project-id-key',
|
|
461
|
+
sub: 'test-sub',
|
|
462
|
+
uri: 'http://mocked.com/',
|
|
463
|
+
domain: 'mocked.com',
|
|
464
|
+
projectUuid: 'test-project-uuid',
|
|
465
|
+
profileUuid: 'test-profile-uuid',
|
|
466
|
+
nonce: 'test-nonce',
|
|
467
|
+
appKitAccount: {
|
|
468
|
+
uuid: 'test-uuid',
|
|
469
|
+
caip2_chain: 'eip155:1',
|
|
470
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
471
|
+
profile_uuid: 'test-profile-uuid',
|
|
472
|
+
created_at: '2023-01-01T00:00:00.000Z',
|
|
473
|
+
is_main_account: true,
|
|
474
|
+
verification_status: null,
|
|
475
|
+
connection_method: null,
|
|
476
|
+
metadata: {},
|
|
477
|
+
last_signed_in_at: '2023-01-01T00:00:00.000Z',
|
|
478
|
+
signed_up_at: '2023-01-01T00:00:00.000Z',
|
|
479
|
+
updated_at: '2023-01-01T00:00:00.000Z'
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse(mockAccountData));
|
|
483
|
+
const result = await siwx.getSessionAccount();
|
|
484
|
+
expect(result).toEqual(mockAccountData);
|
|
485
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/me?projectId=&st=appkit&sv=html-wagmi-undefined&includeAppKitAccount=true'), {
|
|
486
|
+
body: undefined,
|
|
487
|
+
headers: {
|
|
488
|
+
Authorization: `Bearer ${mockJWT}`
|
|
489
|
+
},
|
|
490
|
+
method: 'GET'
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
it('should handle request errors when fetching session account', async () => {
|
|
494
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(mockJWT);
|
|
495
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
496
|
+
// Simulate a non-JSON response error
|
|
497
|
+
fetchSpy.mockResolvedValueOnce({
|
|
498
|
+
headers: {
|
|
499
|
+
get: () => 'text/plain'
|
|
500
|
+
},
|
|
501
|
+
text: async () => 'Error fetching session account'
|
|
502
|
+
});
|
|
503
|
+
await expect(siwx.getSessionAccount()).rejects.toThrow('Error fetching session account');
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
describe('setSessionAccountMetadata', () => {
|
|
507
|
+
it('should throw an error if not authenticated', async () => {
|
|
508
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(null);
|
|
509
|
+
await expect(siwx.setSessionAccountMetadata({ test: 'value' })).rejects.toThrow('Not authenticated');
|
|
510
|
+
});
|
|
511
|
+
it('should send metadata to API when authenticated', async () => {
|
|
512
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(mockJWT);
|
|
513
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
514
|
+
const metadata = {
|
|
515
|
+
displayName: 'Test User',
|
|
516
|
+
avatar: 'https://example.com/avatar.png',
|
|
517
|
+
customField: 'custom value'
|
|
518
|
+
};
|
|
519
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ success: true }));
|
|
520
|
+
await siwx.setSessionAccountMetadata(metadata);
|
|
521
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/account-metadata?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
522
|
+
body: JSON.stringify({ metadata }),
|
|
523
|
+
headers: {
|
|
524
|
+
Authorization: `Bearer ${mockJWT}`
|
|
525
|
+
},
|
|
526
|
+
method: 'PUT'
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
it('should handle request errors when updating metadata', async () => {
|
|
530
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(mockJWT);
|
|
531
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
532
|
+
// Simulate a non-JSON response error
|
|
533
|
+
fetchSpy.mockResolvedValueOnce({
|
|
534
|
+
headers: {
|
|
535
|
+
get: () => 'text/plain'
|
|
536
|
+
},
|
|
537
|
+
text: async () => 'Error updating metadata'
|
|
538
|
+
});
|
|
539
|
+
await expect(siwx.setSessionAccountMetadata({ test: 'value' })).rejects.toThrow('Error updating metadata');
|
|
540
|
+
});
|
|
541
|
+
it('should pass empty object as metadata if none provided', async () => {
|
|
542
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce(mockJWT);
|
|
543
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
544
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ success: true }));
|
|
545
|
+
await siwx.setSessionAccountMetadata();
|
|
546
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/account-metadata?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
547
|
+
body: JSON.stringify({ metadata: null }),
|
|
548
|
+
headers: {
|
|
549
|
+
Authorization: `Bearer ${mockJWT}`
|
|
550
|
+
},
|
|
551
|
+
method: 'PUT'
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
describe('Constructor with custom parameters', () => {
|
|
557
|
+
it('should use custom storage keys when provided', () => {
|
|
558
|
+
const customAuth = new ReownAuthentication({
|
|
559
|
+
localAuthStorageKey: '@custom/auth-token',
|
|
560
|
+
localNonceStorageKey: '@custom/nonce-token',
|
|
561
|
+
required: false
|
|
562
|
+
});
|
|
563
|
+
expect(customAuth.getRequired()).toBe(false);
|
|
564
|
+
// Test that custom keys are used internally (we can verify through the behavior)
|
|
565
|
+
vi.spyOn(SafeLocalStorage, 'getItem');
|
|
566
|
+
customAuth['getStorageToken']('@custom/auth-token');
|
|
567
|
+
expect(SafeLocalStorage.getItem).toHaveBeenCalledWith('@custom/auth-token');
|
|
568
|
+
});
|
|
569
|
+
it('should use default values when no parameters provided', () => {
|
|
570
|
+
const defaultAuth = new ReownAuthentication();
|
|
571
|
+
expect(defaultAuth.getRequired()).toBe(true);
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
describe('Email OTP functionality', () => {
|
|
575
|
+
let siwx;
|
|
576
|
+
beforeEach(() => {
|
|
577
|
+
siwx = new ReownAuthentication();
|
|
578
|
+
});
|
|
579
|
+
describe('requestEmailOtp', () => {
|
|
580
|
+
it('should request email OTP and set otpUuid', async () => {
|
|
581
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
582
|
+
const mockUuid = 'mock-otp-uuid-123';
|
|
583
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ uuid: mockUuid }));
|
|
584
|
+
const result = await siwx.requestEmailOtp({
|
|
585
|
+
email: 'test@example.com',
|
|
586
|
+
account: 'eip155:1:0x1234567890abcdef1234567890abcdef12345678'
|
|
587
|
+
});
|
|
588
|
+
expect(result).toEqual({ uuid: mockUuid });
|
|
589
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/otp?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
590
|
+
method: 'POST',
|
|
591
|
+
body: JSON.stringify({
|
|
592
|
+
email: 'test@example.com',
|
|
593
|
+
account: 'eip155:1:0x1234567890abcdef1234567890abcdef12345678'
|
|
594
|
+
}),
|
|
595
|
+
headers: undefined
|
|
596
|
+
});
|
|
597
|
+
// Verify that otpUuid is set internally
|
|
598
|
+
expect(siwx['otpUuid']).toBe(mockUuid);
|
|
599
|
+
// Verify that messenger resources are updated
|
|
600
|
+
expect(siwx['messenger'].resources).toEqual(['email:test@example.com']);
|
|
601
|
+
});
|
|
602
|
+
it('should handle null uuid response', async () => {
|
|
603
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
604
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ uuid: null }));
|
|
605
|
+
const result = await siwx.requestEmailOtp({
|
|
606
|
+
email: 'test@example.com',
|
|
607
|
+
account: 'eip155:1:0x1234567890abcdef1234567890abcdef12345678'
|
|
608
|
+
});
|
|
609
|
+
expect(result).toEqual({ uuid: null });
|
|
610
|
+
expect(siwx['otpUuid']).toBe(null);
|
|
611
|
+
});
|
|
612
|
+
it('should handle API errors when requesting OTP', async () => {
|
|
613
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
614
|
+
fetchSpy.mockResolvedValueOnce({
|
|
615
|
+
ok: false,
|
|
616
|
+
text: async () => 'OTP request failed'
|
|
617
|
+
});
|
|
618
|
+
await expect(siwx.requestEmailOtp({
|
|
619
|
+
email: 'test@example.com',
|
|
620
|
+
account: 'eip155:1:0x1234567890abcdef1234567890abcdef12345678'
|
|
621
|
+
})).rejects.toThrow('OTP request failed');
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
describe('confirmEmailOtp', () => {
|
|
625
|
+
beforeEach(() => {
|
|
626
|
+
// Set otpUuid to simulate having requested OTP
|
|
627
|
+
siwx['otpUuid'] = 'mock-otp-uuid-123';
|
|
628
|
+
});
|
|
629
|
+
it('should confirm email OTP with correct headers', async () => {
|
|
630
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
631
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse(null));
|
|
632
|
+
await siwx.confirmEmailOtp({ code: '123456' });
|
|
633
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/otp?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
634
|
+
method: 'PUT',
|
|
635
|
+
body: JSON.stringify({ code: '123456' }),
|
|
636
|
+
headers: {
|
|
637
|
+
'x-otp': 'mock-otp-uuid-123'
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
it('should not include OTP header when otpUuid is null', async () => {
|
|
642
|
+
siwx['otpUuid'] = null;
|
|
643
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
644
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse(null));
|
|
645
|
+
await siwx.confirmEmailOtp({ code: '123456' });
|
|
646
|
+
expect(fetchSpy).toHaveBeenCalledWith(new URL('https://api.web3modal.org/auth/v1/otp?projectId=&st=appkit&sv=html-wagmi-undefined'), {
|
|
647
|
+
method: 'PUT',
|
|
648
|
+
body: JSON.stringify({ code: '123456' }),
|
|
649
|
+
headers: {}
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
it('should handle API errors when confirming OTP', async () => {
|
|
653
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
654
|
+
fetchSpy.mockResolvedValueOnce({
|
|
655
|
+
ok: false,
|
|
656
|
+
text: async () => 'Invalid OTP code'
|
|
657
|
+
});
|
|
658
|
+
await expect(siwx.confirmEmailOtp({ code: '123456' })).rejects.toThrow('Invalid OTP code');
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
it('should clear otpUuid when session is added', async () => {
|
|
662
|
+
// Set initial otpUuid
|
|
663
|
+
siwx['otpUuid'] = 'mock-otp-uuid-123';
|
|
664
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
665
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
666
|
+
const mockJWT = mocks.createMockJWT({
|
|
667
|
+
aud: 'test-audience',
|
|
668
|
+
iss: 'test-issuer',
|
|
669
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
670
|
+
projectIdKey: 'test-project-id',
|
|
671
|
+
sub: 'test-subject',
|
|
672
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
673
|
+
chainId: 1,
|
|
674
|
+
chainNamespace: 'eip155',
|
|
675
|
+
caip2Network: 'eip155:1',
|
|
676
|
+
uri: 'http://mocked.com/',
|
|
677
|
+
domain: 'mocked.com',
|
|
678
|
+
projectUuid: 'test-project-uuid',
|
|
679
|
+
profileUuid: 'test-profile-uuid',
|
|
680
|
+
nonce: 'mock_nonce'
|
|
681
|
+
});
|
|
682
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
683
|
+
const session = mockSession({
|
|
684
|
+
data: { accountAddress: '0x1234567890abcdef1234567890abcdef12345678', chainId: 'eip155:1' }
|
|
685
|
+
});
|
|
686
|
+
await siwx.addSession(session);
|
|
687
|
+
// Verify otpUuid is cleared
|
|
688
|
+
expect(siwx['otpUuid']).toBe(null);
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
describe('JWT Decode functionality (tested indirectly)', () => {
|
|
692
|
+
let siwx;
|
|
693
|
+
beforeEach(() => {
|
|
694
|
+
siwx = new ReownAuthentication();
|
|
695
|
+
});
|
|
696
|
+
it('should decode valid JWT token correctly through addSession', async () => {
|
|
697
|
+
const payload = {
|
|
698
|
+
aud: 'test-audience',
|
|
699
|
+
iss: 'test-issuer',
|
|
700
|
+
exp: 1234567890,
|
|
701
|
+
projectIdKey: 'test-project-id',
|
|
702
|
+
sub: 'test-subject',
|
|
703
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
704
|
+
chainId: 1,
|
|
705
|
+
chainNamespace: 'eip155',
|
|
706
|
+
caip2Network: 'eip155:1',
|
|
707
|
+
uri: 'http://test.com/',
|
|
708
|
+
domain: 'test.com',
|
|
709
|
+
projectUuid: 'test-project-uuid',
|
|
710
|
+
profileUuid: 'test-profile-uuid',
|
|
711
|
+
nonce: 'test-nonce',
|
|
712
|
+
email: 'test@example.com'
|
|
713
|
+
};
|
|
714
|
+
const mockJWT = mocks.createMockJWT(payload);
|
|
715
|
+
const setAccountPropSpy = vi.spyOn(ChainController, 'setAccountProp');
|
|
716
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
717
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
718
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: mockJWT }));
|
|
719
|
+
const session = mockSession({
|
|
720
|
+
data: { accountAddress: '0x1234567890abcdef1234567890abcdef12345678', chainId: 'eip155:1' }
|
|
721
|
+
});
|
|
722
|
+
await siwx.addSession(session);
|
|
723
|
+
// Verify that the JWT was decoded correctly by checking if setAppKitAccountUser was called
|
|
724
|
+
expect(setAccountPropSpy).toHaveBeenCalledWith('user', { email: 'test@example.com' }, 'eip155');
|
|
725
|
+
setAccountPropSpy.mockRestore();
|
|
726
|
+
});
|
|
727
|
+
it('should handle invalid JWT format through addSession', async () => {
|
|
728
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
729
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
730
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: 'invalid-token' }));
|
|
731
|
+
const session = mockSession({
|
|
732
|
+
data: { accountAddress: '0x1234567890abcdef1234567890abcdef12345678', chainId: 'eip155:1' }
|
|
733
|
+
});
|
|
734
|
+
await expect(siwx.addSession(session)).rejects.toThrow('Invalid token');
|
|
735
|
+
});
|
|
736
|
+
it('should handle JWT with missing parts through addSession', async () => {
|
|
737
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
738
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
739
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: 'header.payload' }));
|
|
740
|
+
const session = mockSession({
|
|
741
|
+
data: { accountAddress: '0x1234567890abcdef1234567890abcdef12345678', chainId: 'eip155:1' }
|
|
742
|
+
});
|
|
743
|
+
await expect(siwx.addSession(session)).rejects.toThrow('Invalid token');
|
|
744
|
+
});
|
|
745
|
+
it('should handle JWT with malformed payload through addSession', async () => {
|
|
746
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
747
|
+
vi.spyOn(localStorage, 'getItem').mockReturnValueOnce('mock_nonce_token');
|
|
748
|
+
// Create a token with malformed base64 payload
|
|
749
|
+
const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
|
|
750
|
+
const malformedPayload = 'invalid-base64-!!!';
|
|
751
|
+
const signature = 'signature';
|
|
752
|
+
const malformedToken = `${header}.${malformedPayload}.${signature}`;
|
|
753
|
+
fetchSpy.mockResolvedValueOnce(mocks.mockFetchResponse({ token: malformedToken }));
|
|
754
|
+
const session = mockSession({
|
|
755
|
+
data: { accountAddress: '0x1234567890abcdef1234567890abcdef12345678', chainId: 'eip155:1' }
|
|
756
|
+
});
|
|
757
|
+
await expect(siwx.addSession(session)).rejects.toThrow();
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
describe('setAppKitAccountUser functionality', () => {
|
|
761
|
+
let siwx;
|
|
762
|
+
let setAccountPropSpy;
|
|
763
|
+
beforeEach(() => {
|
|
764
|
+
siwx = new ReownAuthentication();
|
|
765
|
+
setAccountPropSpy = vi.spyOn(ChainController, 'setAccountProp');
|
|
766
|
+
});
|
|
767
|
+
afterEach(() => {
|
|
768
|
+
setAccountPropSpy.mockRestore();
|
|
769
|
+
});
|
|
770
|
+
it('should set user email for all chain namespaces when email exists', () => {
|
|
771
|
+
const mockSession = {
|
|
772
|
+
aud: 'test-audience',
|
|
773
|
+
iss: 'test-issuer',
|
|
774
|
+
exp: 1234567890,
|
|
775
|
+
projectIdKey: 'test-project-id',
|
|
776
|
+
sub: 'test-subject',
|
|
777
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
778
|
+
chainId: 1,
|
|
779
|
+
chainNamespace: 'eip155',
|
|
780
|
+
caip2Network: 'eip155:1',
|
|
781
|
+
uri: 'http://test.com/',
|
|
782
|
+
domain: 'test.com',
|
|
783
|
+
projectUuid: 'test-project-uuid',
|
|
784
|
+
profileUuid: 'test-profile-uuid',
|
|
785
|
+
nonce: 'test-nonce',
|
|
786
|
+
email: 'test@example.com'
|
|
787
|
+
};
|
|
788
|
+
siwx['setAppKitAccountUser'](mockSession);
|
|
789
|
+
// Should be called for each chain namespace in AppKitConstantsUtil.CHAIN
|
|
790
|
+
expect(setAccountPropSpy).toHaveBeenCalledWith('user', { email: 'test@example.com' }, 'eip155');
|
|
791
|
+
expect(setAccountPropSpy).toHaveBeenCalledWith('user', { email: 'test@example.com' }, 'solana');
|
|
792
|
+
expect(setAccountPropSpy).toHaveBeenCalledWith('user', { email: 'test@example.com' }, 'bip122');
|
|
793
|
+
});
|
|
794
|
+
it('should not set user when email is undefined', () => {
|
|
795
|
+
const mockSession = {
|
|
796
|
+
aud: 'test-audience',
|
|
797
|
+
iss: 'test-issuer',
|
|
798
|
+
exp: 1234567890,
|
|
799
|
+
projectIdKey: 'test-project-id',
|
|
800
|
+
sub: 'test-subject',
|
|
801
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
802
|
+
chainId: 1,
|
|
803
|
+
chainNamespace: 'eip155',
|
|
804
|
+
caip2Network: 'eip155:1',
|
|
805
|
+
uri: 'http://test.com/',
|
|
806
|
+
domain: 'test.com',
|
|
807
|
+
projectUuid: 'test-project-uuid',
|
|
808
|
+
profileUuid: 'test-profile-uuid',
|
|
809
|
+
nonce: 'test-nonce'
|
|
810
|
+
// No email property
|
|
811
|
+
};
|
|
812
|
+
siwx['setAppKitAccountUser'](mockSession);
|
|
813
|
+
expect(setAccountPropSpy).not.toHaveBeenCalled();
|
|
814
|
+
});
|
|
815
|
+
it('should not set user when email is empty string', () => {
|
|
816
|
+
const mockSession = {
|
|
817
|
+
aud: 'test-audience',
|
|
818
|
+
iss: 'test-issuer',
|
|
819
|
+
exp: 1234567890,
|
|
820
|
+
projectIdKey: 'test-project-id',
|
|
821
|
+
sub: 'test-subject',
|
|
822
|
+
address: '0x1234567890abcdef1234567890abcdef12345678',
|
|
823
|
+
chainId: 1,
|
|
824
|
+
chainNamespace: 'eip155',
|
|
825
|
+
caip2Network: 'eip155:1',
|
|
826
|
+
uri: 'http://test.com/',
|
|
827
|
+
domain: 'test.com',
|
|
828
|
+
projectUuid: 'test-project-uuid',
|
|
829
|
+
profileUuid: 'test-profile-uuid',
|
|
830
|
+
nonce: 'test-nonce',
|
|
831
|
+
email: ''
|
|
832
|
+
};
|
|
833
|
+
siwx['setAppKitAccountUser'](mockSession);
|
|
834
|
+
expect(setAccountPropSpy).not.toHaveBeenCalled();
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
describe('Edge cases and error handling', () => {
|
|
838
|
+
let siwx;
|
|
839
|
+
beforeEach(() => {
|
|
840
|
+
siwx = new ReownAuthentication();
|
|
841
|
+
});
|
|
842
|
+
it('should handle non-JSON response correctly', async () => {
|
|
843
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
844
|
+
fetchSpy.mockResolvedValueOnce({
|
|
845
|
+
ok: true,
|
|
846
|
+
headers: {
|
|
847
|
+
get: () => 'text/plain'
|
|
848
|
+
},
|
|
849
|
+
json: async () => {
|
|
850
|
+
throw new Error('Not JSON');
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
const result = await siwx['request']({
|
|
854
|
+
method: 'GET',
|
|
855
|
+
key: 'nonce'
|
|
856
|
+
});
|
|
857
|
+
expect(result).toBe(null);
|
|
858
|
+
});
|
|
859
|
+
it('should handle fetch errors gracefully', async () => {
|
|
860
|
+
const fetchSpy = vi.spyOn(global, 'fetch');
|
|
861
|
+
fetchSpy.mockRejectedValueOnce(new Error('Network error'));
|
|
862
|
+
await expect(siwx['request']({
|
|
863
|
+
method: 'GET',
|
|
864
|
+
key: 'nonce'
|
|
865
|
+
})).rejects.toThrow('Network error');
|
|
866
|
+
});
|
|
867
|
+
it('should handle missing SDK properties', () => {
|
|
868
|
+
const originalGetSdkProperties = ApiController._getSdkProperties;
|
|
869
|
+
ApiController._getSdkProperties = vi.fn().mockReturnValue({
|
|
870
|
+
projectId: '',
|
|
871
|
+
st: '',
|
|
872
|
+
sv: ''
|
|
873
|
+
});
|
|
874
|
+
const result = siwx['getSDKProperties']();
|
|
875
|
+
expect(result).toEqual({
|
|
876
|
+
projectId: '',
|
|
877
|
+
st: '',
|
|
878
|
+
sv: ''
|
|
879
|
+
});
|
|
880
|
+
ApiController._getSdkProperties = originalGetSdkProperties;
|
|
881
|
+
});
|
|
882
|
+
it('should handle wallet info when connectedWalletInfo is undefined', () => {
|
|
883
|
+
vi.spyOn(AccountController.state, 'connectedWalletInfo', 'get').mockReturnValue(undefined);
|
|
884
|
+
const walletInfo = siwx['getWalletInfo']();
|
|
885
|
+
expect(walletInfo).toBeUndefined();
|
|
886
|
+
});
|
|
887
|
+
it('should handle wallet info with missing properties', () => {
|
|
888
|
+
vi.spyOn(AccountController.state, 'connectedWalletInfo', 'get').mockReturnValue({
|
|
889
|
+
type: 'unknown-type'
|
|
890
|
+
// Missing name and icon
|
|
891
|
+
});
|
|
892
|
+
const walletInfo = siwx['getWalletInfo']();
|
|
893
|
+
expect(walletInfo).toEqual({
|
|
894
|
+
type: 'unknown',
|
|
895
|
+
name: undefined,
|
|
896
|
+
icon: undefined
|
|
897
|
+
});
|
|
898
|
+
});
|
|
899
|
+
it('should handle clearStorageTokens correctly', () => {
|
|
900
|
+
const removeItemSpy = vi.spyOn(SafeLocalStorage, 'removeItem');
|
|
901
|
+
const emitSpy = vi.spyOn(siwx, 'emit');
|
|
902
|
+
// Set initial state
|
|
903
|
+
siwx['otpUuid'] = 'test-uuid';
|
|
904
|
+
siwx['clearStorageTokens']();
|
|
905
|
+
expect(siwx['otpUuid']).toBe(null);
|
|
906
|
+
expect(removeItemSpy).toHaveBeenCalledWith(SafeLocalStorageKeys.SIWX_AUTH_TOKEN);
|
|
907
|
+
expect(removeItemSpy).toHaveBeenCalledWith(SafeLocalStorageKeys.SIWX_NONCE_TOKEN);
|
|
908
|
+
expect(emitSpy).toHaveBeenCalledWith('sessionChanged', undefined);
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
//# sourceMappingURL=ReownAuthentication.test.js.map
|