@ic-reactor/react 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +28 -8
  2. package/dist/auth/authentication-manager.d.ts +66 -0
  3. package/dist/auth/authentication-manager.d.ts.map +1 -0
  4. package/dist/auth/authentication-manager.js +458 -0
  5. package/dist/auth/authentication-manager.js.map +1 -0
  6. package/dist/auth/constants.d.ts +18 -0
  7. package/dist/auth/constants.d.ts.map +1 -0
  8. package/dist/auth/constants.js +18 -0
  9. package/dist/auth/constants.js.map +1 -0
  10. package/dist/auth/createIdentityAttributeHooks.d.ts +14 -0
  11. package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -0
  12. package/dist/auth/createIdentityAttributeHooks.js +54 -0
  13. package/dist/auth/createIdentityAttributeHooks.js.map +1 -0
  14. package/dist/auth/identity-attributes-manager.d.ts +19 -0
  15. package/dist/auth/identity-attributes-manager.d.ts.map +1 -0
  16. package/dist/auth/identity-attributes-manager.js +94 -0
  17. package/dist/auth/identity-attributes-manager.js.map +1 -0
  18. package/dist/auth/identity-attributes.d.ts +19 -0
  19. package/dist/auth/identity-attributes.d.ts.map +1 -0
  20. package/dist/auth/identity-attributes.js +170 -0
  21. package/dist/auth/identity-attributes.js.map +1 -0
  22. package/dist/auth/index.d.ts +8 -0
  23. package/dist/auth/index.d.ts.map +1 -0
  24. package/dist/auth/index.js +8 -0
  25. package/dist/auth/index.js.map +1 -0
  26. package/dist/auth/types.d.ts +75 -0
  27. package/dist/auth/types.d.ts.map +1 -0
  28. package/dist/auth/types.js +2 -0
  29. package/dist/auth/types.js.map +1 -0
  30. package/dist/createActorHooks.d.ts +12 -12
  31. package/dist/createActorHooks.d.ts.map +1 -1
  32. package/dist/createActorHooks.js.map +1 -1
  33. package/dist/createInfiniteQuery.d.ts +32 -32
  34. package/dist/createInfiniteQuery.d.ts.map +1 -1
  35. package/dist/createInfiniteQuery.js +6 -6
  36. package/dist/createInfiniteQuery.js.map +1 -1
  37. package/dist/createMutation.d.ts +12 -1
  38. package/dist/createMutation.d.ts.map +1 -1
  39. package/dist/createMutation.js +11 -0
  40. package/dist/createMutation.js.map +1 -1
  41. package/dist/createQuery.d.ts +12 -2
  42. package/dist/createQuery.d.ts.map +1 -1
  43. package/dist/createQuery.js +15 -5
  44. package/dist/createQuery.js.map +1 -1
  45. package/dist/createSuspenseInfiniteQuery.d.ts +31 -31
  46. package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
  47. package/dist/createSuspenseInfiniteQuery.js +6 -6
  48. package/dist/createSuspenseInfiniteQuery.js.map +1 -1
  49. package/dist/createSuspenseQuery.d.ts +2 -2
  50. package/dist/createSuspenseQuery.d.ts.map +1 -1
  51. package/dist/createSuspenseQuery.js.map +1 -1
  52. package/dist/defineReactor.d.ts +74 -0
  53. package/dist/defineReactor.d.ts.map +1 -0
  54. package/dist/defineReactor.js +75 -0
  55. package/dist/defineReactor.js.map +1 -0
  56. package/dist/{createAuthHooks.d.ts → hooks/createAuthHooks.d.ts} +6 -13
  57. package/dist/hooks/createAuthHooks.d.ts.map +1 -0
  58. package/dist/{createAuthHooks.js → hooks/createAuthHooks.js} +12 -57
  59. package/dist/hooks/createAuthHooks.js.map +1 -0
  60. package/dist/hooks/index.d.ts +2 -0
  61. package/dist/hooks/index.d.ts.map +1 -1
  62. package/dist/hooks/index.js +2 -0
  63. package/dist/hooks/index.js.map +1 -1
  64. package/dist/hooks/useActorInfiniteQuery.d.ts +9 -9
  65. package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
  66. package/dist/hooks/useActorInfiniteQuery.js +3 -1
  67. package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
  68. package/dist/hooks/useActorMethod.d.ts +17 -17
  69. package/dist/hooks/useActorMethod.d.ts.map +1 -1
  70. package/dist/hooks/useActorMethod.js.map +1 -1
  71. package/dist/hooks/useActorMutation.d.ts +18 -7
  72. package/dist/hooks/useActorMutation.d.ts.map +1 -1
  73. package/dist/hooks/useActorMutation.js +11 -0
  74. package/dist/hooks/useActorMutation.js.map +1 -1
  75. package/dist/hooks/useActorQuery.d.ts +8 -8
  76. package/dist/hooks/useActorQuery.d.ts.map +1 -1
  77. package/dist/hooks/useActorQuery.js.map +1 -1
  78. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +9 -9
  79. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
  80. package/dist/hooks/useActorSuspenseInfiniteQuery.js +3 -1
  81. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
  82. package/dist/hooks/useActorSuspenseQuery.d.ts +8 -8
  83. package/dist/hooks/useActorSuspenseQuery.d.ts.map +1 -1
  84. package/dist/hooks/useActorSuspenseQuery.js.map +1 -1
  85. package/dist/index.d.ts +2 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +2 -1
  88. package/dist/index.js.map +1 -1
  89. package/dist/types.d.ts +24 -24
  90. package/dist/types.d.ts.map +1 -1
  91. package/dist/utils.d.ts +3 -0
  92. package/dist/utils.d.ts.map +1 -1
  93. package/dist/utils.js +10 -2
  94. package/dist/utils.js.map +1 -1
  95. package/llms.txt +48 -0
  96. package/package.json +27 -21
  97. package/src/auth/authentication-manager.ts +519 -0
  98. package/src/auth/constants.ts +25 -0
  99. package/src/auth/createIdentityAttributeHooks.ts +83 -0
  100. package/src/auth/identity-attributes-manager.ts +127 -0
  101. package/src/auth/identity-attributes.ts +270 -0
  102. package/src/auth/index.ts +7 -0
  103. package/src/auth/types.ts +88 -0
  104. package/src/createActorHooks.ts +81 -49
  105. package/src/createInfiniteQuery.ts +153 -97
  106. package/src/createMutation.ts +34 -18
  107. package/src/createQuery.ts +92 -48
  108. package/src/createSuspenseInfiniteQuery.ts +165 -114
  109. package/src/createSuspenseQuery.ts +67 -41
  110. package/src/defineReactor.test.ts +70 -0
  111. package/src/defineReactor.ts +150 -0
  112. package/src/{createAuthHooks.ts → hooks/createAuthHooks.ts} +17 -98
  113. package/src/hooks/index.ts +4 -0
  114. package/src/hooks/useActorInfiniteQuery.ts +44 -30
  115. package/src/hooks/useActorMethod.ts +62 -53
  116. package/src/hooks/useActorMutation.ts +47 -30
  117. package/src/hooks/useActorQuery.ts +36 -32
  118. package/src/hooks/useActorSuspenseInfiniteQuery.ts +56 -30
  119. package/src/hooks/useActorSuspenseQuery.ts +42 -32
  120. package/src/index.ts +3 -1
  121. package/src/types.ts +76 -71
  122. package/src/utils.test.ts +14 -0
  123. package/src/utils.ts +15 -2
  124. package/dist/createAuthHooks.d.ts.map +0 -1
  125. package/dist/createAuthHooks.js.map +0 -1
@@ -0,0 +1,519 @@
1
+ import type { Identity } from "@icp-sdk/core/agent"
2
+ import type {
3
+ AuthClientLike,
4
+ AuthClientSignInOptions,
5
+ AuthState,
6
+ AuthenticationClientOptions,
7
+ AuthenticationSignInOptions,
8
+ } from "./types"
9
+ import { ClientManager, isDev } from "@ic-reactor/core"
10
+
11
+ import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"
12
+ import {
13
+ IC_INTERNET_IDENTITY_PROVIDER,
14
+ INTERNET_IDENTITY_PROVIDER_ENV_KEY,
15
+ localInternetIdentityProvider,
16
+ } from "./constants"
17
+
18
+ export interface AuthenticationManagerParameters {
19
+ clientManager: ClientManager
20
+ authClient?: AuthClientLike
21
+ identityProvider?: string | URL
22
+ internetIdentityId?: string
23
+ }
24
+
25
+ type AuthClientConstructor = {
26
+ new (options?: AuthenticationClientOptions): AuthClientLike
27
+ }
28
+
29
+ const AUTH_CLIENT_MODULE = "@icp-sdk/auth/client"
30
+
31
+ /**
32
+ * Manages Internet Identity sign-in, session restoration, and authentication
33
+ * state for a {@link ClientManager}.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const authentication = new AuthenticationManager({ clientManager })
38
+ * const identity = await authentication.authenticate()
39
+ * ```
40
+ */
41
+ export class AuthenticationManager {
42
+ private authClient?: AuthClientLike
43
+ private authPromise?: Promise<Identity | undefined>
44
+ private authClientWasProvided = false
45
+ private authStateRevision = 0
46
+ private authStateSubscribers: Array<(state: AuthState) => void> = []
47
+ private authClientConstructor?: AuthClientConstructor
48
+ private authClientConstructorPromise?: Promise<
49
+ AuthClientConstructor | undefined
50
+ >
51
+ private authModuleMissing = false
52
+ private authStateValue: AuthState = {
53
+ identity: null,
54
+ isAuthenticating: false,
55
+ isAuthenticated: false,
56
+ error: undefined,
57
+ }
58
+ private readonly identityProvider?: string | URL
59
+ private readonly internetIdentityId?: string
60
+ public readonly clientManager: ClientManager
61
+
62
+ /** The current authentication state. */
63
+ public get authState(): AuthState {
64
+ return this.authStateValue
65
+ }
66
+
67
+ constructor({
68
+ clientManager,
69
+ authClient,
70
+ identityProvider,
71
+ internetIdentityId,
72
+ }: AuthenticationManagerParameters) {
73
+ this.clientManager = clientManager
74
+ const canisterEnv =
75
+ typeof window !== "undefined" ? getAuthenticationCanisterEnv() : undefined
76
+ this.identityProvider =
77
+ identityProvider ||
78
+ canisterEnv?.[INTERNET_IDENTITY_PROVIDER_ENV_KEY] ||
79
+ canisterEnv?.["PUBLIC_INTERNET_IDENTITY_PROVIDER"]
80
+ this.internetIdentityId =
81
+ internetIdentityId ||
82
+ canisterEnv?.["internet_identity"] ||
83
+ canisterEnv?.["PUBLIC_CANISTER_ID:internet_identity"] ||
84
+ canisterEnv?.["CANISTER_ID_INTERNET_IDENTITY"]
85
+
86
+ if (authClient) {
87
+ this.authClientWasProvided = true
88
+ this.authClient = authClient
89
+ this.syncStateFromClient(this.authStateRevision).catch((error) => {
90
+ this.updateState({ error: error as Error, isAuthenticating: false })
91
+ })
92
+ } else if (typeof window !== "undefined") {
93
+ this.loadAuthClientConstructor().catch(() => {
94
+ // Optional auth support is reported when an auth method is used.
95
+ })
96
+ }
97
+ }
98
+
99
+ /** @internal Used by IdentityAttributesManager. */
100
+ public get client() {
101
+ return this.authClient
102
+ }
103
+
104
+ public subscribeAuthState(callback: (state: AuthState) => void) {
105
+ this.authStateSubscribers.push(callback)
106
+ return () => {
107
+ this.authStateSubscribers = this.authStateSubscribers.filter(
108
+ (subscriber) => subscriber !== callback
109
+ )
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Preloads and creates an AuthClient before a user gesture is needed.
115
+ */
116
+ public async prepareClient(options?: AuthenticationClientOptions) {
117
+ const clientOptions = getAuthClientOptions({
118
+ ...options,
119
+ identityProvider:
120
+ options?.identityProvider ?? this.getDefaultIdentityProvider(),
121
+ })
122
+
123
+ if (
124
+ this.authClient &&
125
+ (!this.shouldRecreateClient(clientOptions) || this.authClientWasProvided)
126
+ ) {
127
+ return this.authClient
128
+ }
129
+
130
+ return this.initializeClient(clientOptions)
131
+ }
132
+
133
+ public authenticate = async (): Promise<Identity | undefined> => {
134
+ if (this.authState.isAuthenticated) {
135
+ return this.authState.identity || undefined
136
+ }
137
+ if (this.authPromise) {
138
+ return this.authPromise
139
+ }
140
+ if (this.authModuleMissing) {
141
+ return undefined
142
+ }
143
+
144
+ this.authPromise = (async () => {
145
+ if (isDev() && typeof window !== "undefined") {
146
+ console.info(
147
+ `%cic-reactor:%c Authenticating...`,
148
+ "color: #3b82f6; font-weight: bold",
149
+ "color: inherit",
150
+ {
151
+ network: this.clientManager.network,
152
+ authClient: this.authClient ? "Shared Instance" : "Dynamic Import",
153
+ }
154
+ )
155
+ }
156
+ this.updateState({ isAuthenticating: true })
157
+ try {
158
+ if (!this.authClient) {
159
+ const authClient = await this.initializeClient(
160
+ getAuthClientOptions({
161
+ identityProvider: this.getDefaultIdentityProvider(),
162
+ })
163
+ )
164
+ if (!authClient) {
165
+ this.updateState({ isAuthenticating: false })
166
+ return undefined
167
+ }
168
+ }
169
+ const identity = await this.authClient!.getIdentity()
170
+ const isAuthenticated = await this.authClient!.isAuthenticated()
171
+ this.clientManager.updateAgent(identity)
172
+ this.updateState({
173
+ identity,
174
+ isAuthenticated,
175
+ isAuthenticating: false,
176
+ })
177
+ return identity
178
+ } catch (error) {
179
+ this.updateState({ error: error as Error, isAuthenticating: false })
180
+ console.error("Authentication failed:", error)
181
+ throw error
182
+ } finally {
183
+ this.authPromise = undefined
184
+ }
185
+ })()
186
+
187
+ return this.authPromise
188
+ }
189
+
190
+ public login = async (loginOptions?: AuthenticationSignInOptions) => {
191
+ let didCompleteSignIn = false
192
+
193
+ try {
194
+ const identityProvider =
195
+ loginOptions?.identityProvider || this.getDefaultIdentityProvider()
196
+ const authClientOptions = getAuthClientOptions({
197
+ ...loginOptions,
198
+ identityProvider,
199
+ })
200
+
201
+ if (!this.ensurePreparedClient(authClientOptions)) {
202
+ await this.initializeClient(authClientOptions)
203
+ }
204
+
205
+ if (!this.authClient) {
206
+ await this.authenticate()
207
+ }
208
+
209
+ if (!this.authClient) {
210
+ throw new Error(
211
+ "Authentication module is missing or failed to initialize. To use login, please install the auth package: npm install @icp-sdk/auth"
212
+ )
213
+ }
214
+
215
+ this.updateState({ isAuthenticating: true, error: undefined })
216
+ const identity = await this.signInOrRecoverIdentity(
217
+ getSignInOptions(loginOptions)
218
+ )
219
+
220
+ if (!this.clientManager.agentState.isInitialized) {
221
+ await this.clientManager.initializeAgent()
222
+ }
223
+
224
+ this.clientManager.updateAgent(identity)
225
+ this.updateState({
226
+ identity,
227
+ isAuthenticated: true,
228
+ isAuthenticating: false,
229
+ })
230
+ didCompleteSignIn = true
231
+
232
+ try {
233
+ await loginOptions?.onSuccess?.()
234
+ } catch (callbackError) {
235
+ this.updateState({ error: callbackError as Error })
236
+ await loginOptions?.onError?.((callbackError as Error).message)
237
+ throw callbackError
238
+ }
239
+ } catch (error) {
240
+ if (!didCompleteSignIn) {
241
+ await loginOptions?.onError?.((error as Error).message)
242
+ this.updateState({
243
+ error: error as Error,
244
+ isAuthenticating: false,
245
+ })
246
+ }
247
+ throw error
248
+ }
249
+ }
250
+
251
+ public logout = async (options?: { returnTo?: string }) => {
252
+ if (!this.authClient) {
253
+ throw new Error(
254
+ "Authentication module is missing or failed to initialize. To use logout, please install the auth package: npm install @icp-sdk/auth"
255
+ )
256
+ }
257
+ this.updateState({ isAuthenticating: true, error: undefined })
258
+ await this.authClient.signOut(options)
259
+ const identity = await this.authClient.getIdentity()
260
+ this.clientManager.updateAgent(identity)
261
+ this.updateState({
262
+ identity,
263
+ isAuthenticated: false,
264
+ isAuthenticating: false,
265
+ })
266
+ }
267
+
268
+ private async initializeClient(
269
+ options?: AuthenticationClientOptions
270
+ ): Promise<AuthClientLike | undefined> {
271
+ const AuthClient = await this.loadAuthClientConstructor()
272
+
273
+ if (!AuthClient) {
274
+ return undefined
275
+ }
276
+
277
+ this.authClient = new AuthClient(options)
278
+ return this.authClient
279
+ }
280
+
281
+ /** @internal Used by IdentityAttributesManager. */
282
+ public async signInOrRecoverIdentity(
283
+ options?: AuthClientSignInOptions
284
+ ): Promise<Identity> {
285
+ if (!this.authClient) {
286
+ throw new Error(
287
+ "Authentication module is missing or failed to initialize. To use login, please install the auth package: npm install @icp-sdk/auth"
288
+ )
289
+ }
290
+
291
+ try {
292
+ return await this.authClient.signIn(options)
293
+ } catch (error) {
294
+ const identity = await Promise.resolve(
295
+ this.authClient.getIdentity()
296
+ ).catch(() => null)
297
+ const isAuthenticated = await Promise.resolve(
298
+ this.authClient.isAuthenticated()
299
+ ).catch(() => false)
300
+
301
+ if (identity && isAuthenticated) {
302
+ return identity
303
+ }
304
+
305
+ throw error
306
+ }
307
+ }
308
+
309
+ private ensurePreparedClient(
310
+ options?: AuthenticationClientOptions
311
+ ): AuthClientLike | undefined {
312
+ if (
313
+ this.authClient &&
314
+ (!this.shouldRecreateClient(options) || this.authClientWasProvided)
315
+ ) {
316
+ return this.authClient
317
+ }
318
+
319
+ const AuthClient = this.authClientConstructor
320
+ if (!AuthClient || this.authClientWasProvided) {
321
+ return undefined
322
+ }
323
+
324
+ this.authClient = new AuthClient(options)
325
+ return this.authClient
326
+ }
327
+
328
+ private shouldRecreateClient(options?: AuthenticationClientOptions): boolean {
329
+ return !this.authClientWasProvided && hasAuthClientOptions(options)
330
+ }
331
+
332
+ private async syncStateFromClient(revision = this.authStateRevision) {
333
+ if (!this.authClient) {
334
+ return
335
+ }
336
+
337
+ const identity = await this.authClient.getIdentity()
338
+ const isAuthenticated = await this.authClient.isAuthenticated()
339
+ if (revision !== this.authStateRevision) {
340
+ return
341
+ }
342
+ this.clientManager.updateAgent(identity)
343
+ this.updateState({
344
+ identity,
345
+ isAuthenticated,
346
+ isAuthenticating: false,
347
+ error: undefined,
348
+ })
349
+ }
350
+
351
+ /** @internal Used by IdentityAttributesManager. */
352
+ public async ensureClient(options?: AuthenticationClientOptions) {
353
+ const clientOptions = getAuthClientOptions({
354
+ ...options,
355
+ identityProvider:
356
+ options?.identityProvider ?? this.getDefaultIdentityProvider(),
357
+ })
358
+ if (!this.ensurePreparedClient(clientOptions)) {
359
+ await this.initializeClient(clientOptions)
360
+ }
361
+ return this.authClient
362
+ }
363
+
364
+ /** @internal Used by IdentityAttributesManager. */
365
+ public async commitIdentity(identity: Identity, isAuthenticated: boolean) {
366
+ if (!this.clientManager.agentState.isInitialized) {
367
+ await this.clientManager.initializeAgent()
368
+ }
369
+ this.clientManager.updateAgent(identity)
370
+ this.updateState({ identity, isAuthenticated, isAuthenticating: false })
371
+ }
372
+
373
+ /** @internal Used by IdentityAttributesManager. */
374
+ public setAuthenticating() {
375
+ this.updateState({ isAuthenticating: true, error: undefined })
376
+ }
377
+
378
+ /** @internal Used by IdentityAttributesManager. */
379
+ public setAuthenticationError(error: Error) {
380
+ this.updateState({ error, isAuthenticating: false })
381
+ }
382
+
383
+ private getDefaultIdentityProvider(): string | URL {
384
+ if (this.identityProvider) {
385
+ return this.identityProvider
386
+ }
387
+ return this.clientManager.isLocal
388
+ ? localInternetIdentityProvider(
389
+ Number(this.clientManager.agentHost?.port) || 4943,
390
+ this.internetIdentityId
391
+ )
392
+ : IC_INTERNET_IDENTITY_PROVIDER
393
+ }
394
+
395
+ private updateState(newState: Partial<AuthState>) {
396
+ if (isDev()) console.debug("[ic-reactor] Updating Auth State:", newState)
397
+ this.authStateRevision += 1
398
+ this.authStateValue = { ...this.authStateValue, ...newState }
399
+ this.authStateSubscribers.forEach((subscriber) =>
400
+ subscriber(this.authStateValue)
401
+ )
402
+ }
403
+
404
+ private async loadAuthClientConstructor() {
405
+ if (this.authClientConstructor) {
406
+ return this.authClientConstructor
407
+ }
408
+
409
+ if (!this.authClientConstructorPromise) {
410
+ // Keep the optional auth peer out of non-auth bundles. Vite/Rollup/esbuild
411
+ // can otherwise try to resolve this package even when auth APIs are unused.
412
+ this.authClientConstructorPromise = import(
413
+ /* @vite-ignore */ AUTH_CLIENT_MODULE
414
+ )
415
+ .then((authModule) => {
416
+ const AuthClient = (
417
+ authModule as { AuthClient?: AuthClientConstructor }
418
+ ).AuthClient
419
+
420
+ if (!AuthClient) {
421
+ throw new Error("@icp-sdk/auth/client did not export AuthClient")
422
+ }
423
+
424
+ this.authClientConstructor = AuthClient
425
+ return AuthClient
426
+ })
427
+ .catch((error) => {
428
+ this.authModuleMissing = true
429
+ this.authClientConstructorPromise = undefined
430
+ if (
431
+ error instanceof Error &&
432
+ error.message.includes("did not export AuthClient")
433
+ ) {
434
+ throw error
435
+ }
436
+ return undefined
437
+ })
438
+ }
439
+
440
+ return this.authClientConstructorPromise
441
+ }
442
+ }
443
+
444
+ function getAuthClientOptions(
445
+ options?: AuthenticationClientOptions
446
+ ): AuthenticationClientOptions | undefined {
447
+ if (!options) {
448
+ return undefined
449
+ }
450
+
451
+ return {
452
+ identityProvider: options.identityProvider,
453
+ windowOpenerFeatures: options.windowOpenerFeatures,
454
+ openIdProvider: getAuthClientOpenIdProvider(options.openIdProvider),
455
+ }
456
+ }
457
+
458
+ function getAuthClientOpenIdProvider(
459
+ openIdProvider?: AuthenticationClientOptions["openIdProvider"]
460
+ ): AuthenticationClientOptions["openIdProvider"] | undefined {
461
+ return openIdProvider === "google" ||
462
+ openIdProvider === "apple" ||
463
+ openIdProvider === "microsoft"
464
+ ? openIdProvider
465
+ : undefined
466
+ }
467
+
468
+ function hasAuthClientOptions(options?: AuthenticationClientOptions): boolean {
469
+ return Boolean(
470
+ options?.identityProvider ||
471
+ options?.windowOpenerFeatures ||
472
+ options?.openIdProvider
473
+ )
474
+ }
475
+
476
+ function getAuthenticationCanisterEnv(): Record<string, string> | undefined {
477
+ const canisterEnv = safeGetCanisterEnv<Record<string, string>>()
478
+ if (canisterEnv) {
479
+ return canisterEnv as unknown as Record<string, string>
480
+ }
481
+
482
+ if (typeof document === "undefined") {
483
+ return undefined
484
+ }
485
+
486
+ const cookie = document.cookie
487
+ .split(";")
488
+ .find((part) => part.trim().startsWith("ic_env="))
489
+ const encodedValue = cookie?.split("=").slice(1).join("=")?.trim()
490
+ if (!encodedValue) {
491
+ return undefined
492
+ }
493
+
494
+ const env = Object.fromEntries(
495
+ decodeURIComponent(encodedValue)
496
+ .split("&")
497
+ .map((entry) => {
498
+ const separatorIndex = entry.indexOf("=")
499
+ return separatorIndex === -1
500
+ ? [entry, ""]
501
+ : [entry.slice(0, separatorIndex), entry.slice(separatorIndex + 1)]
502
+ })
503
+ )
504
+
505
+ return Object.keys(env).length ? env : undefined
506
+ }
507
+
508
+ function getSignInOptions(
509
+ options?: AuthenticationSignInOptions
510
+ ): AuthClientSignInOptions | undefined {
511
+ if (!options) {
512
+ return undefined
513
+ }
514
+
515
+ return {
516
+ maxTimeToLive: options.maxTimeToLive,
517
+ targets: options.targets,
518
+ }
519
+ }
@@ -0,0 +1,25 @@
1
+ export const IC_INTERNET_IDENTITY_PROVIDER = "https://id.ai/authorize"
2
+
3
+ /**
4
+ * `ic_env` key used to provide a fully resolved Internet Identity URL.
5
+ */
6
+ export const INTERNET_IDENTITY_PROVIDER_ENV_KEY = "INTERNET_IDENTITY_PROVIDER"
7
+
8
+ /**
9
+ * Well-known Internet Identity canister ID used when deploying II locally.
10
+ */
11
+ export const LOCAL_INTERNET_IDENTITY_CANISTER_ID = "rdmx6-jaaaa-aaaaa-aaadq-cai"
12
+
13
+ /**
14
+ * Builds the local Internet Identity provider URL for the given replica port.
15
+ */
16
+ export const localInternetIdentityProvider = (
17
+ port: number,
18
+ canisterId: string = LOCAL_INTERNET_IDENTITY_CANISTER_ID
19
+ ) => `http://${canisterId}.localhost:${port}/authorize`
20
+
21
+ /**
22
+ * Legacy default URL for local Internet Identity, using port `4943`.
23
+ */
24
+ export const LOCAL_INTERNET_IDENTITY_PROVIDER =
25
+ localInternetIdentityProvider(4943)
@@ -0,0 +1,83 @@
1
+ import { useCallback, useState } from "react"
2
+ import type { IdentityAttributesManager } from "./identity-attributes-manager"
3
+ import type {
4
+ IdentityAttributeResult,
5
+ RequestIdentityAttributesParameters,
6
+ RequestOpenIdIdentityAttributesParameters,
7
+ } from "./types"
8
+
9
+ export interface UseIdentityAttributesReturn {
10
+ requestAttributes: (
11
+ params: RequestIdentityAttributesParameters
12
+ ) => Promise<IdentityAttributeResult>
13
+ requestOpenIdAttributes: (
14
+ params: RequestOpenIdIdentityAttributesParameters
15
+ ) => Promise<IdentityAttributeResult>
16
+ attributes: IdentityAttributeResult | null
17
+ isRequestingAttributes: boolean
18
+ attributeError: Error | null
19
+ clearAttributes: () => void
20
+ }
21
+
22
+ export function createIdentityAttributeHooks(
23
+ identityAttributes: IdentityAttributesManager
24
+ ) {
25
+ const useIdentityAttributes = (): UseIdentityAttributesReturn => {
26
+ const [attributes, setAttributes] =
27
+ useState<IdentityAttributeResult | null>(null)
28
+ const [isRequestingAttributes, setIsRequestingAttributes] = useState(false)
29
+ const [attributeError, setAttributeError] = useState<Error | null>(null)
30
+
31
+ const requestAttributes = useCallback(
32
+ async (params: RequestIdentityAttributesParameters) => {
33
+ setIsRequestingAttributes(true)
34
+ setAttributeError(null)
35
+ try {
36
+ const result = await identityAttributes.request(params)
37
+ setAttributes(result)
38
+ return result
39
+ } catch (error) {
40
+ setAttributeError(error as Error)
41
+ throw error
42
+ } finally {
43
+ setIsRequestingAttributes(false)
44
+ }
45
+ },
46
+ []
47
+ )
48
+
49
+ const requestOpenIdAttributes = useCallback(
50
+ async (params: RequestOpenIdIdentityAttributesParameters) => {
51
+ setIsRequestingAttributes(true)
52
+ setAttributeError(null)
53
+ try {
54
+ const result = await identityAttributes.requestOpenId(params)
55
+ setAttributes(result)
56
+ return result
57
+ } catch (error) {
58
+ setAttributeError(error as Error)
59
+ throw error
60
+ } finally {
61
+ setIsRequestingAttributes(false)
62
+ }
63
+ },
64
+ []
65
+ )
66
+
67
+ const clearAttributes = useCallback(() => {
68
+ setAttributes(null)
69
+ setAttributeError(null)
70
+ }, [])
71
+
72
+ return {
73
+ requestAttributes,
74
+ requestOpenIdAttributes,
75
+ attributes,
76
+ isRequestingAttributes,
77
+ attributeError,
78
+ clearAttributes,
79
+ }
80
+ }
81
+
82
+ return { useIdentityAttributes }
83
+ }