@ic-reactor/react 3.13.0 → 4.0.0-beta.2
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 +237 -787
- package/dist/index.d.ts +260 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +471 -23
- package/dist/index.js.map +1 -1
- package/llms.txt +82 -278
- package/package.json +11 -39
- package/src/index.tsx +612 -0
- package/dist/auth/auth-client-compat.d.ts +0 -122
- package/dist/auth/auth-client-compat.d.ts.map +0 -1
- package/dist/auth/auth-client-compat.js +0 -162
- package/dist/auth/auth-client-compat.js.map +0 -1
- package/dist/auth/authentication-manager.d.ts +0 -405
- package/dist/auth/authentication-manager.d.ts.map +0 -1
- package/dist/auth/authentication-manager.js +0 -1537
- package/dist/auth/authentication-manager.js.map +0 -1
- package/dist/auth/constants.d.ts +0 -24
- package/dist/auth/constants.d.ts.map +0 -1
- package/dist/auth/constants.js +0 -24
- package/dist/auth/constants.js.map +0 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts +0 -14
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +0 -1
- package/dist/auth/createIdentityAttributeHooks.js +0 -122
- package/dist/auth/createIdentityAttributeHooks.js.map +0 -1
- package/dist/auth/identity-attributes-manager.d.ts +0 -27
- package/dist/auth/identity-attributes-manager.d.ts.map +0 -1
- package/dist/auth/identity-attributes-manager.js +0 -191
- package/dist/auth/identity-attributes-manager.js.map +0 -1
- package/dist/auth/identity-attributes.d.ts +0 -19
- package/dist/auth/identity-attributes.d.ts.map +0 -1
- package/dist/auth/identity-attributes.js +0 -227
- package/dist/auth/identity-attributes.js.map +0 -1
- package/dist/auth/index.d.ts +0 -8
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js +0 -8
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/local-ii-probe.d.ts +0 -57
- package/dist/auth/local-ii-probe.d.ts.map +0 -1
- package/dist/auth/local-ii-probe.js +0 -121
- package/dist/auth/local-ii-probe.js.map +0 -1
- package/dist/auth/types.d.ts +0 -222
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/auth/types.js +0 -2
- package/dist/auth/types.js.map +0 -1
- package/dist/createActorHooks.d.ts +0 -41
- package/dist/createActorHooks.d.ts.map +0 -1
- package/dist/createActorHooks.js +0 -17
- package/dist/createActorHooks.js.map +0 -1
- package/dist/createInfiniteQuery.d.ts +0 -185
- package/dist/createInfiniteQuery.d.ts.map +0 -1
- package/dist/createInfiniteQuery.js +0 -198
- package/dist/createInfiniteQuery.js.map +0 -1
- package/dist/createMutation.d.ts +0 -33
- package/dist/createMutation.d.ts.map +0 -1
- package/dist/createMutation.js +0 -199
- package/dist/createMutation.js.map +0 -1
- package/dist/createQuery.d.ts +0 -63
- package/dist/createQuery.d.ts.map +0 -1
- package/dist/createQuery.js +0 -204
- package/dist/createQuery.js.map +0 -1
- package/dist/createReactorProvider.d.ts +0 -158
- package/dist/createReactorProvider.d.ts.map +0 -1
- package/dist/createReactorProvider.js +0 -256
- package/dist/createReactorProvider.js.map +0 -1
- package/dist/createSuspenseInfiniteQuery.d.ts +0 -154
- package/dist/createSuspenseInfiniteQuery.d.ts.map +0 -1
- package/dist/createSuspenseInfiniteQuery.js +0 -209
- package/dist/createSuspenseInfiniteQuery.js.map +0 -1
- package/dist/createSuspenseQuery.d.ts +0 -46
- package/dist/createSuspenseQuery.d.ts.map +0 -1
- package/dist/createSuspenseQuery.js +0 -158
- package/dist/createSuspenseQuery.js.map +0 -1
- package/dist/defineDisplayReactor.d.ts +0 -43
- package/dist/defineDisplayReactor.d.ts.map +0 -1
- package/dist/defineDisplayReactor.js +0 -42
- package/dist/defineDisplayReactor.js.map +0 -1
- package/dist/defineReactor.d.ts +0 -99
- package/dist/defineReactor.d.ts.map +0 -1
- package/dist/defineReactor.js +0 -15
- package/dist/defineReactor.js.map +0 -1
- package/dist/defineReactorShared.d.ts +0 -84
- package/dist/defineReactorShared.d.ts.map +0 -1
- package/dist/defineReactorShared.js +0 -139
- package/dist/defineReactorShared.js.map +0 -1
- package/dist/hooks/createAuthHooks.d.ts +0 -50
- package/dist/hooks/createAuthHooks.d.ts.map +0 -1
- package/dist/hooks/createAuthHooks.js +0 -291
- package/dist/hooks/createAuthHooks.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -21
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -24
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts +0 -67
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +0 -1
- package/dist/hooks/useActorInfiniteQuery.js +0 -89
- package/dist/hooks/useActorInfiniteQuery.js.map +0 -1
- package/dist/hooks/useActorMethod.d.ts +0 -148
- package/dist/hooks/useActorMethod.d.ts.map +0 -1
- package/dist/hooks/useActorMethod.js +0 -394
- package/dist/hooks/useActorMethod.js.map +0 -1
- package/dist/hooks/useActorMutation.d.ts +0 -51
- package/dist/hooks/useActorMutation.d.ts.map +0 -1
- package/dist/hooks/useActorMutation.js +0 -70
- package/dist/hooks/useActorMutation.js.map +0 -1
- package/dist/hooks/useActorQuery.d.ts +0 -45
- package/dist/hooks/useActorQuery.d.ts.map +0 -1
- package/dist/hooks/useActorQuery.js +0 -67
- package/dist/hooks/useActorQuery.js.map +0 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +0 -51
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +0 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +0 -76
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +0 -1
- package/dist/hooks/useActorSuspenseQuery.d.ts +0 -32
- package/dist/hooks/useActorSuspenseQuery.d.ts.map +0 -1
- package/dist/hooks/useActorSuspenseQuery.js +0 -58
- package/dist/hooks/useActorSuspenseQuery.js.map +0 -1
- package/dist/ownedAuthentication.d.ts +0 -52
- package/dist/ownedAuthentication.d.ts.map +0 -1
- package/dist/ownedAuthentication.js +0 -49
- package/dist/ownedAuthentication.js.map +0 -1
- package/dist/server.d.ts +0 -21
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -23
- package/dist/server.js.map +0 -1
- package/dist/testing.d.ts +0 -19
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -19
- package/dist/testing.js.map +0 -1
- package/dist/types.d.ts +0 -671
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -207
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -405
- package/dist/utils.js.map +0 -1
- package/dist/validation.d.ts +0 -136
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -144
- package/dist/validation.js.map +0 -1
- package/src/auth/auth-client-compat.ts +0 -273
- package/src/auth/authentication-manager.ts +0 -1682
- package/src/auth/constants.ts +0 -32
- package/src/auth/createIdentityAttributeHooks.ts +0 -169
- package/src/auth/identity-attributes-manager.ts +0 -226
- package/src/auth/identity-attributes.ts +0 -345
- package/src/auth/index.ts +0 -7
- package/src/auth/local-ii-probe.ts +0 -173
- package/src/auth/types.ts +0 -243
- package/src/createActorHooks.ts +0 -208
- package/src/createInfiniteQuery.ts +0 -670
- package/src/createMutation.ts +0 -324
- package/src/createQuery.ts +0 -369
- package/src/createReactorProvider.ts +0 -365
- package/src/createSuspenseInfiniteQuery.ts +0 -651
- package/src/createSuspenseQuery.ts +0 -304
- package/src/defineDisplayReactor.ts +0 -62
- package/src/defineReactor.ts +0 -142
- package/src/defineReactorShared.ts +0 -268
- package/src/hooks/createAuthHooks.ts +0 -371
- package/src/hooks/index.ts +0 -103
- package/src/hooks/useActorInfiniteQuery.ts +0 -278
- package/src/hooks/useActorMethod.ts +0 -710
- package/src/hooks/useActorMutation.ts +0 -205
- package/src/hooks/useActorQuery.ts +0 -157
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +0 -248
- package/src/hooks/useActorSuspenseQuery.ts +0 -147
- package/src/index.ts +0 -31
- package/src/ownedAuthentication.ts +0 -81
- package/src/server.ts +0 -23
- package/src/testing.ts +0 -18
- package/src/types.ts +0 -948
- package/src/utils.ts +0 -505
- package/src/validation.ts +0 -226
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import { IDL } from "@icp-sdk/core/candid"
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
IdentityAttributeOpenIdProvider,
|
|
5
|
-
IdentityAttributeValues,
|
|
6
|
-
SignedIdentityAttributes,
|
|
7
|
-
} from "./types.js"
|
|
8
|
-
import { IC_INTERNET_IDENTITY_PROVIDER } from "./constants.js"
|
|
9
|
-
|
|
10
|
-
export const IDENTITY_ATTRIBUTES_PROVIDER = IC_INTERNET_IDENTITY_PROVIDER
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @deprecated Use the default ClientManager identity provider or
|
|
14
|
-
* `IDENTITY_ATTRIBUTES_PROVIDER` instead.
|
|
15
|
-
*/
|
|
16
|
-
export const IDENTITY_ATTRIBUTES_BETA_PROVIDER = IDENTITY_ATTRIBUTES_PROVIDER
|
|
17
|
-
|
|
18
|
-
const OPEN_ID_PROVIDER_URLS = {
|
|
19
|
-
apple: "https://appleid.apple.com",
|
|
20
|
-
google: "https://accounts.google.com",
|
|
21
|
-
microsoft: "https://login.microsoftonline.com/{tid}/v2.0",
|
|
22
|
-
} as const
|
|
23
|
-
|
|
24
|
-
export function identityAttributeKeys({
|
|
25
|
-
openIdProvider,
|
|
26
|
-
keys,
|
|
27
|
-
}: {
|
|
28
|
-
openIdProvider: IdentityAttributeOpenIdProvider
|
|
29
|
-
keys: string[]
|
|
30
|
-
}): string[] {
|
|
31
|
-
const provider = normalizeOpenIdProvider(openIdProvider)
|
|
32
|
-
return keys.map((key) => `openid:${provider}:${key}`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function normalizeOpenIdProvider(
|
|
36
|
-
openIdProvider: IdentityAttributeOpenIdProvider
|
|
37
|
-
): string {
|
|
38
|
-
return (
|
|
39
|
-
OPEN_ID_PROVIDER_URLS[
|
|
40
|
-
openIdProvider as keyof typeof OPEN_ID_PROVIDER_URLS
|
|
41
|
-
] ?? openIdProvider
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function resolveIdentityAttributeKeys({
|
|
46
|
-
openIdProvider,
|
|
47
|
-
keys,
|
|
48
|
-
}: {
|
|
49
|
-
openIdProvider: IdentityAttributeOpenIdProvider
|
|
50
|
-
keys: string[]
|
|
51
|
-
}): Promise<string[]> {
|
|
52
|
-
return identityAttributeKeys({ openIdProvider, keys })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function decodeIdentityAttributeValues(
|
|
56
|
-
data: Uint8Array,
|
|
57
|
-
requestedKeys: string[]
|
|
58
|
-
): IdentityAttributeValues {
|
|
59
|
-
const requestedKeyMap = requestedKeys.reduce<Record<string, string>>(
|
|
60
|
-
(acc, key) => {
|
|
61
|
-
acc[key] = identityAttributeDisplayKey(key)
|
|
62
|
-
acc[identityAttributeDisplayKey(key)] = identityAttributeDisplayKey(key)
|
|
63
|
-
return acc
|
|
64
|
-
},
|
|
65
|
-
{}
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
// Internet Identity's own format, read by its type. Once a message is known
|
|
69
|
-
// to be one, a key it does not hold is absent: the text fallback below
|
|
70
|
-
// cannot add anything true, only a neighbouring value under the wrong name.
|
|
71
|
-
const icrc3Entries = decodeIcrc3AttributeMap(data)
|
|
72
|
-
if (icrc3Entries) {
|
|
73
|
-
return collectIcrc3TextValues(icrc3Entries, requestedKeyMap)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const decodedValues = decodeCandidAttributeValues(data, requestedKeyMap)
|
|
77
|
-
if (Object.keys(decodedValues).length > 0) {
|
|
78
|
-
return decodedValues
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return extractPrintableAttributeValues(data, requestedKeys)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** The ICRC-3 `Value` an Internet Identity attribute message is encoded as. */
|
|
85
|
-
type Icrc3Value =
|
|
86
|
-
| { Nat: bigint }
|
|
87
|
-
| { Int: bigint }
|
|
88
|
-
| { Blob: Uint8Array | number[] }
|
|
89
|
-
| { Text: string }
|
|
90
|
-
| { Array: Icrc3Value[] }
|
|
91
|
-
| { Map: Array<[string, Icrc3Value]> }
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* The entries of an Internet Identity attribute message, or `undefined` when
|
|
95
|
-
* `data` is not one.
|
|
96
|
-
*
|
|
97
|
-
* The canister certifies `Encode!(&Icrc3Value::Map(..))`: the requested
|
|
98
|
-
* attributes under the keys they were requested by (bare, such as `email`, for
|
|
99
|
-
* an unscoped request), plus `implicit:nonce`, `implicit:origin` and
|
|
100
|
-
* `implicit:issued_at_timestamp_ns`. None of the shapes
|
|
101
|
-
* {@link decodeCandidAttributeValues} tries match it, so without this every
|
|
102
|
-
* real response was read by scraping printable text, which returned the
|
|
103
|
-
* neighbouring length byte as part of a value, cut names at the first
|
|
104
|
-
* non-ASCII character, and matched keys inside other values.
|
|
105
|
-
*
|
|
106
|
-
* @see https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md
|
|
107
|
-
*/
|
|
108
|
-
function decodeIcrc3AttributeMap(
|
|
109
|
-
data: Uint8Array
|
|
110
|
-
): Array<[string, Icrc3Value]> | undefined {
|
|
111
|
-
const value = IDL.Rec()
|
|
112
|
-
value.fill(
|
|
113
|
-
IDL.Variant({
|
|
114
|
-
Nat: IDL.Nat,
|
|
115
|
-
Int: IDL.Int,
|
|
116
|
-
Blob: IDL.Vec(IDL.Nat8),
|
|
117
|
-
Text: IDL.Text,
|
|
118
|
-
Array: IDL.Vec(value),
|
|
119
|
-
Map: IDL.Vec(IDL.Tuple(IDL.Text, value)),
|
|
120
|
-
})
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const [decoded] = IDL.decode([value], data) as [Icrc3Value]
|
|
125
|
-
return "Map" in decoded ? decoded.Map : undefined
|
|
126
|
-
} catch {
|
|
127
|
-
return undefined
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* The requested attributes in an ICRC-3 map, as certified.
|
|
133
|
-
*
|
|
134
|
-
* Only `Text` entries are attribute values; the `implicit:` entries are there
|
|
135
|
-
* for the verifier and are returned only if asked for by name.
|
|
136
|
-
*/
|
|
137
|
-
function collectIcrc3TextValues(
|
|
138
|
-
entries: Array<[string, Icrc3Value]>,
|
|
139
|
-
requestedKeyMap: Record<string, string>
|
|
140
|
-
): IdentityAttributeValues {
|
|
141
|
-
const values: IdentityAttributeValues = {}
|
|
142
|
-
for (const [key, value] of entries) {
|
|
143
|
-
if (!Object.prototype.hasOwnProperty.call(requestedKeyMap, key)) continue
|
|
144
|
-
if ("Text" in value) {
|
|
145
|
-
values[requestedKeyMap[key]] = value.Text
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return values
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function decodeCandidAttributeValues(
|
|
152
|
-
data: Uint8Array,
|
|
153
|
-
requestedKeyMap: Record<string, string>
|
|
154
|
-
): IdentityAttributeValues {
|
|
155
|
-
const textPairs = IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text))
|
|
156
|
-
const recordPairs = IDL.Vec(
|
|
157
|
-
IDL.Record({
|
|
158
|
-
key: IDL.Text,
|
|
159
|
-
value: IDL.Text,
|
|
160
|
-
})
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
const candidates = [
|
|
164
|
-
[textPairs],
|
|
165
|
-
[recordPairs],
|
|
166
|
-
[IDL.Record({ attributes: textPairs })],
|
|
167
|
-
[IDL.Record({ values: textPairs })],
|
|
168
|
-
[IDL.Record({ attributes: recordPairs })],
|
|
169
|
-
[IDL.Record({ values: recordPairs })],
|
|
170
|
-
]
|
|
171
|
-
|
|
172
|
-
for (const candidate of candidates) {
|
|
173
|
-
try {
|
|
174
|
-
const decoded = IDL.decode(candidate, data)
|
|
175
|
-
const values = collectDecodedAttributeValues(decoded, requestedKeyMap)
|
|
176
|
-
if (Object.keys(values).length > 0) {
|
|
177
|
-
return values
|
|
178
|
-
}
|
|
179
|
-
} catch {
|
|
180
|
-
// Try the next known beta payload shape, then fall back to text extraction.
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function collectDecodedAttributeValues(
|
|
188
|
-
value: unknown,
|
|
189
|
-
requestedKeyMap: Record<string, string>,
|
|
190
|
-
activeKey?: string
|
|
191
|
-
): IdentityAttributeValues {
|
|
192
|
-
const values: IdentityAttributeValues = {}
|
|
193
|
-
|
|
194
|
-
if (typeof value === "string") {
|
|
195
|
-
const displayKey = requestedKeyMap[value]
|
|
196
|
-
if (displayKey) {
|
|
197
|
-
return values
|
|
198
|
-
}
|
|
199
|
-
if (activeKey && isPrintableTextValue(value)) {
|
|
200
|
-
values[activeKey] = value
|
|
201
|
-
}
|
|
202
|
-
return values
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (Array.isArray(value)) {
|
|
206
|
-
if (value.length === 2 && typeof value[0] === "string") {
|
|
207
|
-
const displayKey = requestedKeyMap[value[0]]
|
|
208
|
-
if (
|
|
209
|
-
displayKey &&
|
|
210
|
-
typeof value[1] === "string" &&
|
|
211
|
-
isPrintableTextValue(value[1])
|
|
212
|
-
) {
|
|
213
|
-
values[displayKey] = value[1]
|
|
214
|
-
return values
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
for (const item of value) {
|
|
219
|
-
Object.assign(
|
|
220
|
-
values,
|
|
221
|
-
collectDecodedAttributeValues(item, requestedKeyMap, activeKey)
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
return values
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (value && typeof value === "object") {
|
|
228
|
-
const record = value as Record<string, unknown>
|
|
229
|
-
if (typeof record.key === "string" && typeof record.value === "string") {
|
|
230
|
-
const displayKey = requestedKeyMap[record.key]
|
|
231
|
-
if (displayKey && isPrintableTextValue(record.value)) {
|
|
232
|
-
values[displayKey] = record.value
|
|
233
|
-
return values
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
for (const [key, nested] of Object.entries(record)) {
|
|
238
|
-
const displayKey = requestedKeyMap[key] ?? activeKey
|
|
239
|
-
if (
|
|
240
|
-
displayKey &&
|
|
241
|
-
typeof nested === "string" &&
|
|
242
|
-
isPrintableTextValue(nested)
|
|
243
|
-
) {
|
|
244
|
-
values[displayKey] = nested
|
|
245
|
-
continue
|
|
246
|
-
}
|
|
247
|
-
Object.assign(
|
|
248
|
-
values,
|
|
249
|
-
collectDecodedAttributeValues(nested, requestedKeyMap, displayKey)
|
|
250
|
-
)
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return values
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function extractPrintableAttributeValues(
|
|
258
|
-
data: Uint8Array,
|
|
259
|
-
requestedKeys: string[]
|
|
260
|
-
): IdentityAttributeValues {
|
|
261
|
-
const text = new TextDecoder().decode(data)
|
|
262
|
-
const values: IdentityAttributeValues = {}
|
|
263
|
-
|
|
264
|
-
for (const requestedKey of requestedKeys) {
|
|
265
|
-
const start = text.indexOf(requestedKey)
|
|
266
|
-
if (start === -1) {
|
|
267
|
-
continue
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const tail = text.slice(start + requestedKey.length)
|
|
271
|
-
const printableRuns = tail.match(/[\x20-\x7E]{2,512}/g) ?? []
|
|
272
|
-
const value = printableRuns
|
|
273
|
-
.map((candidate) =>
|
|
274
|
-
cleanPrintableAttributeValue(candidate, requestedKeys)
|
|
275
|
-
)
|
|
276
|
-
.find(
|
|
277
|
-
(candidate) =>
|
|
278
|
-
isPrintableTextValue(candidate) &&
|
|
279
|
-
candidate !== requestedKey &&
|
|
280
|
-
!candidate.startsWith("openid:")
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
if (value) {
|
|
284
|
-
values[identityAttributeDisplayKey(requestedKey)] = value.trim()
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return values
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function cleanPrintableAttributeValue(
|
|
292
|
-
value: string,
|
|
293
|
-
requestedKeys: string[]
|
|
294
|
-
): string {
|
|
295
|
-
let cleaned = value
|
|
296
|
-
|
|
297
|
-
for (const requestedKey of requestedKeys) {
|
|
298
|
-
const nextKeyIndex = cleaned.indexOf(requestedKey)
|
|
299
|
-
if (nextKeyIndex > 0) {
|
|
300
|
-
cleaned = cleaned.slice(
|
|
301
|
-
0,
|
|
302
|
-
trimCandidTextLengthPrefix(cleaned, nextKeyIndex)
|
|
303
|
-
)
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const nextOpenIdKeyIndex = cleaned.indexOf("openid:")
|
|
308
|
-
if (nextOpenIdKeyIndex > 0) {
|
|
309
|
-
cleaned = cleaned.slice(
|
|
310
|
-
0,
|
|
311
|
-
trimCandidTextLengthPrefix(cleaned, nextOpenIdKeyIndex)
|
|
312
|
-
)
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return cleaned.trim()
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function trimCandidTextLengthPrefix(value: string, index: number): number {
|
|
319
|
-
let trimmedIndex = index
|
|
320
|
-
while (trimmedIndex > 0 && isAsciiDigit(value.charCodeAt(trimmedIndex - 1))) {
|
|
321
|
-
trimmedIndex -= 1
|
|
322
|
-
}
|
|
323
|
-
return trimmedIndex
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function isAsciiDigit(charCode: number): boolean {
|
|
327
|
-
return charCode >= 48 && charCode <= 57
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function identityAttributeDisplayKey(key: string): string {
|
|
331
|
-
return key.split(":").pop() || key
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function isPrintableTextValue(value: string): boolean {
|
|
335
|
-
return value.length > 0 && value.length <= 512 && /^[\x20-\x7E]+$/.test(value)
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export function normalizeSignedIdentityAttributes(
|
|
339
|
-
attributes: SignedIdentityAttributes
|
|
340
|
-
): SignedIdentityAttributes {
|
|
341
|
-
return {
|
|
342
|
-
data: new Uint8Array(attributes.data),
|
|
343
|
-
signature: new Uint8Array(attributes.signature),
|
|
344
|
-
}
|
|
345
|
-
}
|
package/src/auth/index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
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"
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ask a locally deployed Internet Identity canister what it actually serves.
|
|
3
|
-
*
|
|
4
|
-
* `localInternetIdentityProvider` builds `http://<id>.localhost:<port>/authorize`,
|
|
5
|
-
* the id.ai-era path. Whether that works depends on which Internet Identity
|
|
6
|
-
* build is installed, and the window is narrow:
|
|
7
|
-
*
|
|
8
|
-
* - up to `release-2025-03-07`, the SPA lived at `/` and used a `#authorize`
|
|
9
|
-
* fragment; `/authorize` is not an asset
|
|
10
|
-
* - `release-2026-01-05` through `release-2026-03-16` serve `/authorize`
|
|
11
|
-
* - from `release-2026-03-23` the frontend left the canister entirely (the id.ai
|
|
12
|
-
* split), so `/authorize` 404s even on `.raw`
|
|
13
|
-
*
|
|
14
|
-
* Outside that window the login popup renders the gateway's
|
|
15
|
-
* "Response Verification Error" page and the app simply waits until the user
|
|
16
|
-
* closes it. Nothing points at the cause.
|
|
17
|
-
*
|
|
18
|
-
* Asking the canister over the agent avoids the gateway, so the answer is about
|
|
19
|
-
* the canister's assets rather than about how the gateway felt about them.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { Actor, type HttpAgent } from "@icp-sdk/core/agent"
|
|
23
|
-
import { IDL } from "@icp-sdk/core/candid"
|
|
24
|
-
import type { AuthClientFlavor } from "./auth-client-compat.js"
|
|
25
|
-
|
|
26
|
-
/** The `http_request` query every asset-serving canister exposes. */
|
|
27
|
-
const httpInterface: IDL.InterfaceFactory = ({ IDL }) => {
|
|
28
|
-
const HeaderField = IDL.Tuple(IDL.Text, IDL.Text)
|
|
29
|
-
return IDL.Service({
|
|
30
|
-
http_request: IDL.Func(
|
|
31
|
-
[
|
|
32
|
-
IDL.Record({
|
|
33
|
-
method: IDL.Text,
|
|
34
|
-
url: IDL.Text,
|
|
35
|
-
headers: IDL.Vec(HeaderField),
|
|
36
|
-
body: IDL.Vec(IDL.Nat8),
|
|
37
|
-
}),
|
|
38
|
-
],
|
|
39
|
-
[
|
|
40
|
-
IDL.Record({
|
|
41
|
-
status_code: IDL.Nat16,
|
|
42
|
-
headers: IDL.Vec(HeaderField),
|
|
43
|
-
body: IDL.Vec(IDL.Nat8),
|
|
44
|
-
}),
|
|
45
|
-
],
|
|
46
|
-
["query"]
|
|
47
|
-
),
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface HttpCapableActor {
|
|
52
|
-
http_request: (request: {
|
|
53
|
-
method: string
|
|
54
|
-
url: string
|
|
55
|
-
headers: [string, string][]
|
|
56
|
-
body: Uint8Array
|
|
57
|
-
}) => Promise<{ status_code: number }>
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Where a given Internet Identity build expects the sign-in flow to start. */
|
|
61
|
-
export type AuthorizePath = "/authorize" | "/#authorize"
|
|
62
|
-
|
|
63
|
-
export interface LocalIiProbe {
|
|
64
|
-
/** The path that works, or `null` when the canister serves no sign-in UI. */
|
|
65
|
-
path: AuthorizePath | null
|
|
66
|
-
/**
|
|
67
|
-
* The probe could not reach a conclusion — the canister was unreachable, or
|
|
68
|
-
* it does not expose `http_request` at all. Callers keep their existing
|
|
69
|
-
* behaviour rather than blocking a login that might well work.
|
|
70
|
-
*/
|
|
71
|
-
inconclusive: boolean
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function statusOf(
|
|
75
|
-
actor: HttpCapableActor,
|
|
76
|
-
url: string
|
|
77
|
-
): Promise<number | undefined> {
|
|
78
|
-
try {
|
|
79
|
-
const response = await actor.http_request({
|
|
80
|
-
method: "GET",
|
|
81
|
-
url,
|
|
82
|
-
headers: [],
|
|
83
|
-
body: new Uint8Array(),
|
|
84
|
-
})
|
|
85
|
-
return response.status_code
|
|
86
|
-
} catch {
|
|
87
|
-
return undefined
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Determine which authorize path a locally deployed Internet Identity serves.
|
|
93
|
-
*
|
|
94
|
-
* Never throws. A probe that cannot answer returns `inconclusive`, because a
|
|
95
|
-
* diagnostic that blocks a working setup is worse than the failure it explains.
|
|
96
|
-
*/
|
|
97
|
-
export async function probeLocalInternetIdentity(
|
|
98
|
-
agent: HttpAgent,
|
|
99
|
-
canisterId: string
|
|
100
|
-
): Promise<LocalIiProbe> {
|
|
101
|
-
let actor: HttpCapableActor
|
|
102
|
-
try {
|
|
103
|
-
actor = Actor.createActor<HttpCapableActor>(httpInterface, {
|
|
104
|
-
agent,
|
|
105
|
-
canisterId,
|
|
106
|
-
})
|
|
107
|
-
} catch {
|
|
108
|
-
return { path: "/authorize", inconclusive: true }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const authorize = await statusOf(actor, "/authorize")
|
|
112
|
-
|
|
113
|
-
// The canister does not answer http_request at all — not an asset canister,
|
|
114
|
-
// not deployed, or the replica is down. Not our call to make.
|
|
115
|
-
if (authorize === undefined) {
|
|
116
|
-
return { path: "/authorize", inconclusive: true }
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (authorize >= 200 && authorize < 300) {
|
|
120
|
-
return { path: "/authorize", inconclusive: false }
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Pre-2026 builds serve the SPA at the root and drive the flow from a
|
|
124
|
-
// `#authorize` fragment, which is a path the gateway never sees.
|
|
125
|
-
const root = await statusOf(actor, "/")
|
|
126
|
-
if (root !== undefined && root >= 200 && root < 300) {
|
|
127
|
-
return { path: "/#authorize", inconclusive: false }
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return { path: null, inconclusive: false }
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* The error a login gets when the installed build serves no sign-in UI, in
|
|
135
|
-
* place of a 503 page inside a popup the app cannot see.
|
|
136
|
-
*
|
|
137
|
-
* The fix depends on the installed `@icp-sdk/auth` major. v8 signs in through
|
|
138
|
-
* the page the canister serves, so an older build that still serves one works.
|
|
139
|
-
* v10 signs in through `ii_session_delegation` and mints with
|
|
140
|
-
* `app_prepare_delegation`, which Internet Identity gained only after its
|
|
141
|
-
* frontend left the canister (#561). No build serves both, so a v10 app needs
|
|
142
|
-
* the frontend from somewhere else, and older builds cannot help it.
|
|
143
|
-
*
|
|
144
|
-
* @param flavor - The installed client's contract. Defaults to `legacy` (v8),
|
|
145
|
-
* whose advice this error gave before it knew about v10.
|
|
146
|
-
*/
|
|
147
|
-
export function localInternetIdentityUnavailableError(
|
|
148
|
-
canisterId: string,
|
|
149
|
-
flavor: AuthClientFlavor = "legacy"
|
|
150
|
-
): Error {
|
|
151
|
-
const problem =
|
|
152
|
-
`[ic-reactor] The Internet Identity canister ${canisterId} does not serve a sign-in UI: ` +
|
|
153
|
-
`neither /authorize nor / returned a page. `
|
|
154
|
-
|
|
155
|
-
if (flavor === "session") {
|
|
156
|
-
return new Error(
|
|
157
|
-
problem +
|
|
158
|
-
`With @icp-sdk/auth v10, local sign-in needs an Internet Identity frontend served ` +
|
|
159
|
-
`separately, because no Internet Identity canister build both serves /authorize and ` +
|
|
160
|
-
`supports v10 sessions. Set \`identityProvider\` to that frontend's authorize URL and ` +
|
|
161
|
-
`\`internetIdentityId\` to the local Internet Identity canister that mints its delegations.`
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return new Error(
|
|
166
|
-
problem +
|
|
167
|
-
`Internet Identity builds from ` +
|
|
168
|
-
`release-2026-03-23 onward moved their frontend out of the canister (the id.ai split) ` +
|
|
169
|
-
`and cannot be used locally. Install internet_identity_dev.wasm from release-2026-03-16, ` +
|
|
170
|
-
`the newest build that still self-serves /authorize, or point \`identityProvider\` at a ` +
|
|
171
|
-
`provider you host yourself.`
|
|
172
|
-
)
|
|
173
|
-
}
|