@ic-reactor/react 3.7.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -9
- package/dist/auth/authentication-manager.d.ts +37 -5
- package/dist/auth/authentication-manager.d.ts.map +1 -1
- package/dist/auth/authentication-manager.js +111 -37
- package/dist/auth/authentication-manager.js.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts +2 -2
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
- package/dist/auth/identity-attributes-manager.d.ts +12 -5
- package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
- package/dist/auth/identity-attributes-manager.js +42 -28
- package/dist/auth/identity-attributes-manager.js.map +1 -1
- package/dist/auth/identity-attributes.d.ts +1 -1
- package/dist/auth/identity-attributes.d.ts.map +1 -1
- package/dist/auth/identity-attributes.js +1 -1
- package/dist/auth/identity-attributes.js.map +1 -1
- package/dist/auth/index.d.ts +7 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +7 -7
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/types.d.ts +123 -16
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/createActorHooks.d.ts +4 -4
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js +6 -6
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createInfiniteQuery.d.ts +1 -1
- package/dist/createInfiniteQuery.d.ts.map +1 -1
- package/dist/createInfiniteQuery.js +1 -1
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createMutation.d.ts +1 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createQuery.d.ts +1 -1
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +1 -1
- package/dist/createQuery.js.map +1 -1
- package/dist/createSuspenseInfiniteQuery.d.ts +1 -1
- package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/createSuspenseInfiniteQuery.js +1 -1
- package/dist/createSuspenseInfiniteQuery.js.map +1 -1
- package/dist/createSuspenseQuery.d.ts +1 -1
- package/dist/createSuspenseQuery.d.ts.map +1 -1
- package/dist/createSuspenseQuery.js +1 -1
- package/dist/createSuspenseQuery.js.map +1 -1
- package/dist/defineReactor.d.ts +54 -4
- package/dist/defineReactor.d.ts.map +1 -1
- package/dist/defineReactor.js +77 -7
- package/dist/defineReactor.js.map +1 -1
- package/dist/hooks/createAuthHooks.d.ts +2 -2
- package/dist/hooks/createAuthHooks.d.ts.map +1 -1
- package/dist/hooks/createAuthHooks.js +7 -2
- package/dist/hooks/createAuthHooks.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js +1 -1
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/llms.txt +1 -1
- package/package.json +14 -9
- package/src/auth/authentication-manager.ts +121 -49
- package/src/auth/createIdentityAttributeHooks.ts +2 -2
- package/src/auth/identity-attributes-manager.ts +53 -44
- package/src/auth/identity-attributes.ts +2 -2
- package/src/auth/index.ts +7 -7
- package/src/auth/types.ts +134 -18
- package/src/createActorHooks.ts +9 -9
- package/src/createInfiniteQuery.ts +2 -2
- package/src/createMutation.ts +1 -1
- package/src/createQuery.ts +2 -2
- package/src/createSuspenseInfiniteQuery.ts +2 -2
- package/src/createSuspenseQuery.ts +2 -2
- package/src/defineReactor.ts +136 -14
- package/src/hooks/createAuthHooks.ts +8 -3
- package/src/hooks/useActorInfiniteQuery.ts +1 -1
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +1 -1
- package/src/index.ts +11 -11
- package/src/defineReactor.test.ts +0 -70
- package/src/hooks/useActorInfiniteQuery.test.tsx +0 -457
- package/src/hooks/useActorMethod.test.tsx +0 -798
- package/src/hooks/useActorMutation.test.tsx +0 -220
- package/src/hooks/useActorQuery.test.tsx +0 -287
- package/src/hooks/useActorSuspenseInfiniteQuery.test.tsx +0 -472
- package/src/hooks/useActorSuspenseQuery.test.tsx +0 -254
- package/src/utils.test.ts +0 -14
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
AuthState,
|
|
6
6
|
AuthenticationClientOptions,
|
|
7
7
|
AuthenticationSignInOptions,
|
|
8
|
-
} from "./types"
|
|
8
|
+
} from "./types.js"
|
|
9
9
|
import { ClientManager, isDev } from "@ic-reactor/core"
|
|
10
10
|
|
|
11
11
|
import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"
|
|
@@ -13,12 +13,16 @@ import {
|
|
|
13
13
|
IC_INTERNET_IDENTITY_PROVIDER,
|
|
14
14
|
INTERNET_IDENTITY_PROVIDER_ENV_KEY,
|
|
15
15
|
localInternetIdentityProvider,
|
|
16
|
-
} from "./constants"
|
|
16
|
+
} from "./constants.js"
|
|
17
17
|
|
|
18
|
-
export interface AuthenticationManagerParameters {
|
|
18
|
+
export interface AuthenticationManagerParameters extends AuthenticationClientOptions {
|
|
19
19
|
clientManager: ClientManager
|
|
20
|
+
/**
|
|
21
|
+
* Bring your own client instance. When provided, IC Reactor never constructs
|
|
22
|
+
* one and never applies the options below to it.
|
|
23
|
+
*/
|
|
20
24
|
authClient?: AuthClientLike
|
|
21
|
-
|
|
25
|
+
/** Canister ID of a locally deployed Internet Identity. */
|
|
22
26
|
internetIdentityId?: string
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -26,8 +30,6 @@ type AuthClientConstructor = {
|
|
|
26
30
|
new (options?: AuthenticationClientOptions): AuthClientLike
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
const AUTH_CLIENT_MODULE = "@icp-sdk/auth/client"
|
|
30
|
-
|
|
31
33
|
/**
|
|
32
34
|
* Manages Internet Identity sign-in, session restoration, and authentication
|
|
33
35
|
* state for a {@link ClientManager}.
|
|
@@ -49,6 +51,7 @@ export class AuthenticationManager {
|
|
|
49
51
|
AuthClientConstructor | undefined
|
|
50
52
|
>
|
|
51
53
|
private authModuleMissing = false
|
|
54
|
+
private authClientOptions?: AuthenticationClientOptions
|
|
52
55
|
private authStateValue: AuthState = {
|
|
53
56
|
identity: null,
|
|
54
57
|
isAuthenticating: false,
|
|
@@ -57,6 +60,7 @@ export class AuthenticationManager {
|
|
|
57
60
|
}
|
|
58
61
|
private readonly identityProvider?: string | URL
|
|
59
62
|
private readonly internetIdentityId?: string
|
|
63
|
+
private readonly defaultClientOptions: AuthenticationClientOptions
|
|
60
64
|
public readonly clientManager: ClientManager
|
|
61
65
|
|
|
62
66
|
/** The current authentication state. */
|
|
@@ -69,6 +73,7 @@ export class AuthenticationManager {
|
|
|
69
73
|
authClient,
|
|
70
74
|
identityProvider,
|
|
71
75
|
internetIdentityId,
|
|
76
|
+
...clientOptions
|
|
72
77
|
}: AuthenticationManagerParameters) {
|
|
73
78
|
this.clientManager = clientManager
|
|
74
79
|
const canisterEnv =
|
|
@@ -82,6 +87,7 @@ export class AuthenticationManager {
|
|
|
82
87
|
canisterEnv?.["internet_identity"] ||
|
|
83
88
|
canisterEnv?.["PUBLIC_CANISTER_ID:internet_identity"] ||
|
|
84
89
|
canisterEnv?.["CANISTER_ID_INTERNET_IDENTITY"]
|
|
90
|
+
this.defaultClientOptions = clientOptions
|
|
85
91
|
|
|
86
92
|
if (authClient) {
|
|
87
93
|
this.authClientWasProvided = true
|
|
@@ -111,25 +117,37 @@ export class AuthenticationManager {
|
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
/**
|
|
114
|
-
* Preloads and creates an AuthClient
|
|
120
|
+
* Preloads the auth module and creates an AuthClient ahead of time.
|
|
121
|
+
*
|
|
122
|
+
* Call (and await) this before wiring up a login button: it makes
|
|
123
|
+
* {@link login} and {@link IdentityAttributesManager.request} able to open
|
|
124
|
+
* the identity provider synchronously inside the click handler, which is
|
|
125
|
+
* what browser popup blockers and the ICRC-29 transport require.
|
|
115
126
|
*/
|
|
116
127
|
public async prepareClient(options?: AuthenticationClientOptions) {
|
|
117
|
-
const clientOptions =
|
|
118
|
-
...options,
|
|
119
|
-
identityProvider:
|
|
120
|
-
options?.identityProvider ?? this.getDefaultIdentityProvider(),
|
|
121
|
-
})
|
|
128
|
+
const clientOptions = this.resolveClientOptions(options)
|
|
122
129
|
|
|
123
|
-
if (
|
|
124
|
-
this.authClient &&
|
|
125
|
-
(!this.shouldRecreateClient(clientOptions) || this.authClientWasProvided)
|
|
126
|
-
) {
|
|
130
|
+
if (this.authClient && !this.shouldRecreateClient(clientOptions)) {
|
|
127
131
|
return this.authClient
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
return this.initializeClient(clientOptions)
|
|
131
135
|
}
|
|
132
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Returns an AuthClient without awaiting, or `undefined` when the auth
|
|
139
|
+
* module has not been loaded yet.
|
|
140
|
+
*
|
|
141
|
+
* Callers that must stay inside a user gesture use this instead of
|
|
142
|
+
* {@link ensureClient}; awaiting anything before opening the identity
|
|
143
|
+
* provider window loses the gesture.
|
|
144
|
+
*/
|
|
145
|
+
public getPreparedClient(
|
|
146
|
+
options?: AuthenticationClientOptions
|
|
147
|
+
): AuthClientLike | undefined {
|
|
148
|
+
return this.ensurePreparedClient(this.resolveClientOptions(options))
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
public authenticate = async (): Promise<Identity | undefined> => {
|
|
134
152
|
if (this.authState.isAuthenticated) {
|
|
135
153
|
return this.authState.identity || undefined
|
|
@@ -157,9 +175,7 @@ export class AuthenticationManager {
|
|
|
157
175
|
try {
|
|
158
176
|
if (!this.authClient) {
|
|
159
177
|
const authClient = await this.initializeClient(
|
|
160
|
-
|
|
161
|
-
identityProvider: this.getDefaultIdentityProvider(),
|
|
162
|
-
})
|
|
178
|
+
this.resolveClientOptions()
|
|
163
179
|
)
|
|
164
180
|
if (!authClient) {
|
|
165
181
|
this.updateState({ isAuthenticating: false })
|
|
@@ -168,7 +184,12 @@ export class AuthenticationManager {
|
|
|
168
184
|
}
|
|
169
185
|
const identity = await this.authClient!.getIdentity()
|
|
170
186
|
const isAuthenticated = await this.authClient!.isAuthenticated()
|
|
171
|
-
|
|
187
|
+
// Restoring an anonymous session is the common first-load case; pushing
|
|
188
|
+
// it through updateAgent would invalidate the whole query cache on
|
|
189
|
+
// every mount for nothing.
|
|
190
|
+
if (isAuthenticated || !identity.getPrincipal().isAnonymous()) {
|
|
191
|
+
this.clientManager.updateAgent(identity)
|
|
192
|
+
}
|
|
172
193
|
this.updateState({
|
|
173
194
|
identity,
|
|
174
195
|
isAuthenticated,
|
|
@@ -191,13 +212,10 @@ export class AuthenticationManager {
|
|
|
191
212
|
let didCompleteSignIn = false
|
|
192
213
|
|
|
193
214
|
try {
|
|
194
|
-
const
|
|
195
|
-
loginOptions?.identityProvider || this.getDefaultIdentityProvider()
|
|
196
|
-
const authClientOptions = getAuthClientOptions({
|
|
197
|
-
...loginOptions,
|
|
198
|
-
identityProvider,
|
|
199
|
-
})
|
|
215
|
+
const authClientOptions = this.resolveClientOptions(loginOptions)
|
|
200
216
|
|
|
217
|
+
// Stays synchronous when `prepareClient()` has already loaded the auth
|
|
218
|
+
// module, so `signIn()` still runs inside the caller's click handler.
|
|
201
219
|
if (!this.ensurePreparedClient(authClientOptions)) {
|
|
202
220
|
await this.initializeClient(authClientOptions)
|
|
203
221
|
}
|
|
@@ -208,7 +226,7 @@ export class AuthenticationManager {
|
|
|
208
226
|
|
|
209
227
|
if (!this.authClient) {
|
|
210
228
|
throw new Error(
|
|
211
|
-
"Authentication module is missing or failed to initialize. To use login,
|
|
229
|
+
"Authentication module is missing or failed to initialize. To use login, install the optional auth peer: npm install @icp-sdk/auth. If it is already installed and your bundler could not resolve it, pass a pre-constructed client instead: new AuthenticationManager({ clientManager, authClient: new AuthClient(...) })"
|
|
212
230
|
)
|
|
213
231
|
}
|
|
214
232
|
|
|
@@ -251,7 +269,7 @@ export class AuthenticationManager {
|
|
|
251
269
|
public logout = async (options?: { returnTo?: string }) => {
|
|
252
270
|
if (!this.authClient) {
|
|
253
271
|
throw new Error(
|
|
254
|
-
"Authentication module is missing or failed to initialize. To use logout,
|
|
272
|
+
"Authentication module is missing or failed to initialize. To use logout, install the optional auth peer: npm install @icp-sdk/auth. If it is already installed and your bundler could not resolve it, pass a pre-constructed client instead: new AuthenticationManager({ clientManager, authClient: new AuthClient(...) })"
|
|
255
273
|
)
|
|
256
274
|
}
|
|
257
275
|
this.updateState({ isAuthenticating: true, error: undefined })
|
|
@@ -275,6 +293,7 @@ export class AuthenticationManager {
|
|
|
275
293
|
}
|
|
276
294
|
|
|
277
295
|
this.authClient = new AuthClient(options)
|
|
296
|
+
this.authClientOptions = options
|
|
278
297
|
return this.authClient
|
|
279
298
|
}
|
|
280
299
|
|
|
@@ -284,7 +303,7 @@ export class AuthenticationManager {
|
|
|
284
303
|
): Promise<Identity> {
|
|
285
304
|
if (!this.authClient) {
|
|
286
305
|
throw new Error(
|
|
287
|
-
"Authentication module is missing or failed to initialize. To use login,
|
|
306
|
+
"Authentication module is missing or failed to initialize. To use login, install the optional auth peer: npm install @icp-sdk/auth. If it is already installed and your bundler could not resolve it, pass a pre-constructed client instead: new AuthenticationManager({ clientManager, authClient: new AuthClient(...) })"
|
|
288
307
|
)
|
|
289
308
|
}
|
|
290
309
|
|
|
@@ -309,10 +328,7 @@ export class AuthenticationManager {
|
|
|
309
328
|
private ensurePreparedClient(
|
|
310
329
|
options?: AuthenticationClientOptions
|
|
311
330
|
): AuthClientLike | undefined {
|
|
312
|
-
if (
|
|
313
|
-
this.authClient &&
|
|
314
|
-
(!this.shouldRecreateClient(options) || this.authClientWasProvided)
|
|
315
|
-
) {
|
|
331
|
+
if (this.authClient && !this.shouldRecreateClient(options)) {
|
|
316
332
|
return this.authClient
|
|
317
333
|
}
|
|
318
334
|
|
|
@@ -322,11 +338,41 @@ export class AuthenticationManager {
|
|
|
322
338
|
}
|
|
323
339
|
|
|
324
340
|
this.authClient = new AuthClient(options)
|
|
341
|
+
this.authClientOptions = options
|
|
325
342
|
return this.authClient
|
|
326
343
|
}
|
|
327
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Only rebuild the client when the effective options actually changed.
|
|
347
|
+
*
|
|
348
|
+
* Recreating on every call would discard the client warmed up by
|
|
349
|
+
* `prepareClient()` and register a duplicate sign-out callback on the
|
|
350
|
+
* shared IdleManager singleton each time.
|
|
351
|
+
*/
|
|
328
352
|
private shouldRecreateClient(options?: AuthenticationClientOptions): boolean {
|
|
329
|
-
|
|
353
|
+
if (this.authClientWasProvided) {
|
|
354
|
+
return false
|
|
355
|
+
}
|
|
356
|
+
return !isSameAuthClientOptions(this.authClientOptions, options)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Merges constructor-level defaults with per-call overrides and fills in the
|
|
361
|
+
* network-appropriate identity provider.
|
|
362
|
+
*/
|
|
363
|
+
private resolveClientOptions(
|
|
364
|
+
options?: AuthenticationClientOptions
|
|
365
|
+
): AuthenticationClientOptions {
|
|
366
|
+
const merged = getAuthClientOptions({
|
|
367
|
+
...this.defaultClientOptions,
|
|
368
|
+
...options,
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
...merged,
|
|
373
|
+
identityProvider:
|
|
374
|
+
merged?.identityProvider ?? this.getDefaultIdentityProvider(),
|
|
375
|
+
}
|
|
330
376
|
}
|
|
331
377
|
|
|
332
378
|
private async syncStateFromClient(revision = this.authStateRevision) {
|
|
@@ -350,11 +396,7 @@ export class AuthenticationManager {
|
|
|
350
396
|
|
|
351
397
|
/** @internal Used by IdentityAttributesManager. */
|
|
352
398
|
public async ensureClient(options?: AuthenticationClientOptions) {
|
|
353
|
-
const clientOptions =
|
|
354
|
-
...options,
|
|
355
|
-
identityProvider:
|
|
356
|
-
options?.identityProvider ?? this.getDefaultIdentityProvider(),
|
|
357
|
-
})
|
|
399
|
+
const clientOptions = this.resolveClientOptions(options)
|
|
358
400
|
if (!this.ensurePreparedClient(clientOptions)) {
|
|
359
401
|
await this.initializeClient(clientOptions)
|
|
360
402
|
}
|
|
@@ -407,11 +449,12 @@ export class AuthenticationManager {
|
|
|
407
449
|
}
|
|
408
450
|
|
|
409
451
|
if (!this.authClientConstructorPromise) {
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
452
|
+
// The specifier must stay a literal: a variable specifier (or /* @vite-ignore */)
|
|
453
|
+
// makes bundlers skip this import entirely, so the bare specifier survives into
|
|
454
|
+
// the browser, where it cannot be resolved without an import map and every login
|
|
455
|
+
// path fails. A literal lets Vite/Rollup/webpack code-split the optional peer,
|
|
456
|
+
// and lets them tree-shake it away for apps that never reference this class.
|
|
457
|
+
this.authClientConstructorPromise = import("@icp-sdk/auth/client")
|
|
415
458
|
.then((authModule) => {
|
|
416
459
|
const AuthClient = (
|
|
417
460
|
authModule as { AuthClient?: AuthClientConstructor }
|
|
@@ -452,6 +495,12 @@ function getAuthClientOptions(
|
|
|
452
495
|
identityProvider: options.identityProvider,
|
|
453
496
|
windowOpenerFeatures: options.windowOpenerFeatures,
|
|
454
497
|
openIdProvider: getAuthClientOpenIdProvider(options.openIdProvider),
|
|
498
|
+
derivationOrigin: options.derivationOrigin,
|
|
499
|
+
storage: options.storage,
|
|
500
|
+
keyType: options.keyType,
|
|
501
|
+
idleOptions: options.idleOptions,
|
|
502
|
+
identity: options.identity,
|
|
503
|
+
transport: options.transport,
|
|
455
504
|
}
|
|
456
505
|
}
|
|
457
506
|
|
|
@@ -465,11 +514,34 @@ function getAuthClientOpenIdProvider(
|
|
|
465
514
|
: undefined
|
|
466
515
|
}
|
|
467
516
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
517
|
+
/**
|
|
518
|
+
* Compares the options two AuthClients would be built from. Object-valued
|
|
519
|
+
* options (`storage`, `identity`, `idleOptions`) are compared by reference,
|
|
520
|
+
* which is what module-scoped configuration produces.
|
|
521
|
+
*/
|
|
522
|
+
function isSameAuthClientOptions(
|
|
523
|
+
current?: AuthenticationClientOptions,
|
|
524
|
+
next?: AuthenticationClientOptions
|
|
525
|
+
): boolean {
|
|
526
|
+
if (current === next) {
|
|
527
|
+
return true
|
|
528
|
+
}
|
|
529
|
+
if (!current || !next) {
|
|
530
|
+
return false
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return (
|
|
534
|
+
String(current.identityProvider ?? "") ===
|
|
535
|
+
String(next.identityProvider ?? "") &&
|
|
536
|
+
current.windowOpenerFeatures === next.windowOpenerFeatures &&
|
|
537
|
+
current.openIdProvider === next.openIdProvider &&
|
|
538
|
+
String(current.derivationOrigin ?? "") ===
|
|
539
|
+
String(next.derivationOrigin ?? "") &&
|
|
540
|
+
current.storage === next.storage &&
|
|
541
|
+
current.keyType === next.keyType &&
|
|
542
|
+
current.idleOptions === next.idleOptions &&
|
|
543
|
+
current.identity === next.identity &&
|
|
544
|
+
current.transport === next.transport
|
|
473
545
|
)
|
|
474
546
|
}
|
|
475
547
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useCallback, useState } from "react"
|
|
2
|
-
import type { IdentityAttributesManager } from "./identity-attributes-manager"
|
|
2
|
+
import type { IdentityAttributesManager } from "./identity-attributes-manager.js"
|
|
3
3
|
import type {
|
|
4
4
|
IdentityAttributeResult,
|
|
5
5
|
RequestIdentityAttributesParameters,
|
|
6
6
|
RequestOpenIdIdentityAttributesParameters,
|
|
7
|
-
} from "./types"
|
|
7
|
+
} from "./types.js"
|
|
8
8
|
|
|
9
9
|
export interface UseIdentityAttributesReturn {
|
|
10
10
|
requestAttributes: (
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
2
|
+
AuthClientLike,
|
|
3
|
+
IdentityAttributeNonce,
|
|
3
4
|
IdentityAttributeResult,
|
|
4
5
|
RequestIdentityAttributesParameters,
|
|
5
6
|
RequestOpenIdIdentityAttributesParameters,
|
|
6
|
-
} from "./types"
|
|
7
|
+
} from "./types.js"
|
|
7
8
|
|
|
8
|
-
import { AuthenticationManager } from "./authentication-manager"
|
|
9
|
+
import { AuthenticationManager } from "./authentication-manager.js"
|
|
9
10
|
import {
|
|
10
11
|
decodeIdentityAttributeValues,
|
|
11
12
|
identityAttributeKeys,
|
|
12
13
|
normalizeSignedIdentityAttributes,
|
|
13
|
-
} from "./identity-attributes"
|
|
14
|
+
} from "./identity-attributes.js"
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Requests and decodes signed identity attributes for an authenticated
|
|
17
18
|
* Internet Identity session.
|
|
18
19
|
*
|
|
20
|
+
* Await `authentication.prepareClient()` once during startup (the `useAuth`
|
|
21
|
+
* hook already does) so that {@link request} can open the identity provider
|
|
22
|
+
* window synchronously from a click handler.
|
|
23
|
+
*
|
|
19
24
|
* @example
|
|
20
25
|
* ```ts
|
|
21
26
|
* const attributes = new IdentityAttributesManager(authentication)
|
|
22
|
-
* const result = await attributes.request({
|
|
27
|
+
* const result = await attributes.request({
|
|
28
|
+
* keys: ["name"],
|
|
29
|
+
* nonce: () => fetchNonceFromCanister(),
|
|
30
|
+
* })
|
|
23
31
|
* ```
|
|
24
32
|
*/
|
|
25
33
|
export class IdentityAttributesManager {
|
|
@@ -28,37 +36,40 @@ export class IdentityAttributesManager {
|
|
|
28
36
|
public request = async ({
|
|
29
37
|
keys,
|
|
30
38
|
nonce,
|
|
31
|
-
identityProvider,
|
|
32
|
-
openIdProvider,
|
|
33
|
-
windowOpenerFeatures,
|
|
34
39
|
signIn = true,
|
|
35
40
|
maxTimeToLive,
|
|
36
41
|
targets,
|
|
42
|
+
...clientOptions
|
|
37
43
|
}: RequestIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// Nothing may be awaited before `requestAttributes` / `signIn` are invoked:
|
|
45
|
+
// the ICRC-29 transport only opens the identity provider window while the
|
|
46
|
+
// browser still considers us inside the user gesture. `getPreparedClient`
|
|
47
|
+
// is synchronous whenever `prepareClient()` has run.
|
|
48
|
+
const preparedClient = this.authentication.getPreparedClient(clientOptions)
|
|
49
|
+
const authClient =
|
|
50
|
+
preparedClient ?? (await this.authentication.ensureClient(clientOptions))
|
|
45
51
|
|
|
46
52
|
if (!authClient) {
|
|
47
53
|
throw new Error(
|
|
48
|
-
"Authentication module is missing or failed to initialize. To request identity attributes, please install @icp-sdk/auth v7 or provide a compatible authClient."
|
|
54
|
+
"Authentication module is missing or failed to initialize. To request identity attributes, please install @icp-sdk/auth v7 or later, or provide a compatible authClient."
|
|
49
55
|
)
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
this.authentication.setAuthenticating()
|
|
53
59
|
|
|
54
60
|
try {
|
|
61
|
+
// `signIn` first: it opens the transport channel synchronously, and the
|
|
62
|
+
// attribute request then reuses that same channel and window.
|
|
55
63
|
const identityPromise = signIn
|
|
56
64
|
? this.authentication.signInOrRecoverIdentity({
|
|
57
65
|
maxTimeToLive,
|
|
58
66
|
targets,
|
|
59
67
|
})
|
|
60
68
|
: Promise.resolve(authClient.getIdentity())
|
|
61
|
-
const requestPromise = authClient.requestAttributes({
|
|
69
|
+
const requestPromise = authClient.requestAttributes({
|
|
70
|
+
keys,
|
|
71
|
+
nonce: toAuthClientNonce(authClient, nonce),
|
|
72
|
+
})
|
|
62
73
|
|
|
63
74
|
const [signedAttributes, identity] = await Promise.all([
|
|
64
75
|
requestPromise,
|
|
@@ -88,40 +99,38 @@ export class IdentityAttributesManager {
|
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
|
|
91
|
-
public requestOpenId =
|
|
92
|
-
nonce,
|
|
93
|
-
openIdProvider,
|
|
102
|
+
public requestOpenId = ({
|
|
94
103
|
keys,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
signIn,
|
|
98
|
-
maxTimeToLive,
|
|
99
|
-
targets,
|
|
104
|
+
openIdProvider,
|
|
105
|
+
...rest
|
|
100
106
|
}: RequestOpenIdIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
|
|
107
|
+
// Called (not awaited) synchronously so the user gesture is preserved.
|
|
101
108
|
return this.request({
|
|
102
|
-
|
|
103
|
-
nonce,
|
|
104
|
-
identityProvider,
|
|
109
|
+
...rest,
|
|
105
110
|
openIdProvider,
|
|
106
|
-
|
|
107
|
-
signIn,
|
|
108
|
-
maxTimeToLive,
|
|
109
|
-
targets,
|
|
111
|
+
keys: identityAttributeKeys({ openIdProvider, keys }),
|
|
110
112
|
})
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Bridges the two `@icp-sdk/auth` nonce contracts.
|
|
118
|
+
*
|
|
119
|
+
* v8 takes `() => Promise<Uint8Array>` (it journals the value so a redirect
|
|
120
|
+
* flow replays the same bytes); v7 takes `Uint8Array | Promise<Uint8Array>`.
|
|
121
|
+
* Both open the transport channel synchronously for the deferred forms, so we
|
|
122
|
+
* always hand over the deferred shape the installed client understands.
|
|
123
|
+
* `memoize` only exists from v8 onwards, which makes it a reliable marker.
|
|
124
|
+
*/
|
|
125
|
+
function toAuthClientNonce(
|
|
126
|
+
authClient: AuthClientLike,
|
|
127
|
+
nonce: IdentityAttributeNonce
|
|
128
|
+
): Uint8Array | Promise<Uint8Array> | (() => Promise<Uint8Array>) {
|
|
129
|
+
const supportsNonceThunk = typeof authClient.memoize === "function"
|
|
130
|
+
|
|
131
|
+
if (supportsNonceThunk) {
|
|
132
|
+
return () => Promise.resolve(typeof nonce === "function" ? nonce() : nonce)
|
|
126
133
|
}
|
|
134
|
+
|
|
135
|
+
return Promise.resolve(typeof nonce === "function" ? nonce() : nonce)
|
|
127
136
|
}
|
|
@@ -4,8 +4,8 @@ import type {
|
|
|
4
4
|
IdentityAttributeOpenIdProvider,
|
|
5
5
|
IdentityAttributeValues,
|
|
6
6
|
SignedIdentityAttributes,
|
|
7
|
-
} from "./types"
|
|
8
|
-
import { IC_INTERNET_IDENTITY_PROVIDER } from "./constants"
|
|
7
|
+
} from "./types.js"
|
|
8
|
+
import { IC_INTERNET_IDENTITY_PROVIDER } from "./constants.js"
|
|
9
9
|
|
|
10
10
|
export const IDENTITY_ATTRIBUTES_PROVIDER = IC_INTERNET_IDENTITY_PROVIDER
|
|
11
11
|
|
package/src/auth/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from "./authentication-manager"
|
|
2
|
-
export * from "./identity-attributes-manager"
|
|
3
|
-
export * from "./identity-attributes"
|
|
4
|
-
export * from "./constants"
|
|
5
|
-
export * from "./types"
|
|
6
|
-
export * from "../hooks/createAuthHooks"
|
|
7
|
-
export * from "./createIdentityAttributeHooks"
|
|
1
|
+
export * from "./authentication-manager.js"
|
|
2
|
+
export * from "./identity-attributes-manager.js"
|
|
3
|
+
export * from "./identity-attributes.js"
|
|
4
|
+
export * from "./constants.js"
|
|
5
|
+
export * from "./types.js"
|
|
6
|
+
export * from "../hooks/createAuthHooks.js"
|
|
7
|
+
export * from "./createIdentityAttributeHooks.js"
|