@coordination-network/user-app-link 0.0.1
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/LICENSE +21 -0
- package/README.md +140 -0
- package/dist/index.cjs +580 -0
- package/dist/index.js +539 -0
- package/dist/web.cjs +560 -0
- package/dist/web.js +555 -0
- package/package.json +55 -0
- package/src/client.ts +138 -0
- package/src/codec.ts +134 -0
- package/src/errors.ts +32 -0
- package/src/handler.ts +95 -0
- package/src/index.ts +6 -0
- package/src/schemas.ts +261 -0
- package/src/types.ts +176 -0
- package/src/web.ts +327 -0
- package/types/index.d.ts +440 -0
- package/types/web.d.ts +81 -0
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/** Protocol v1 types for the user-app <-> network-member-app deep link. */
|
|
4
|
+
/** A `0x`-prefixed lowercase hexadecimal string. Byte-length aliases below are documentation only. */
|
|
5
|
+
type Hex = `0x${string}`;
|
|
6
|
+
/** A 20-byte address, e.g. `verifyingContract`. */
|
|
7
|
+
type Hex20 = Hex;
|
|
8
|
+
/** A 31-byte value, sized to fit under the BN254 scalar field for in-circuit use. */
|
|
9
|
+
type Hex31 = Hex;
|
|
10
|
+
/** A 32-byte value, e.g. a `networkId`. */
|
|
11
|
+
type Hex32 = Hex;
|
|
12
|
+
/** A 65-byte uncompressed secp256k1 public key, or a 65-byte signature with a recovery byte. */
|
|
13
|
+
type Hex65 = Hex;
|
|
14
|
+
/** A 66-byte ERC-6538 meta-stealth address (33-byte compressed spending key || 33-byte compressed viewing key). */
|
|
15
|
+
type Hex66 = Hex;
|
|
16
|
+
/** An EC public key in JWK form (RFC 7517). This protocol uses P-256 only. */
|
|
17
|
+
interface Jwk {
|
|
18
|
+
kty: "EC";
|
|
19
|
+
crv: "P-256";
|
|
20
|
+
x: string;
|
|
21
|
+
y: string;
|
|
22
|
+
}
|
|
23
|
+
/** Where the user app's device key lives in the platform's key hierarchy. */
|
|
24
|
+
declare const SECURITY_LEVELS: readonly ["STRONGBOX", "TRUSTED_ENVIRONMENT", "SOFTWARE", "UNKNOWN"];
|
|
25
|
+
type SecurityLevel = (typeof SECURITY_LEVELS)[number];
|
|
26
|
+
/** Identifies the member app making a request, as declared by that app (not verified by the OS). */
|
|
27
|
+
interface Caller {
|
|
28
|
+
name: string;
|
|
29
|
+
scheme: string;
|
|
30
|
+
}
|
|
31
|
+
interface ApprovalScope {
|
|
32
|
+
programme: string;
|
|
33
|
+
country: string;
|
|
34
|
+
}
|
|
35
|
+
interface RegistrationSignParams {
|
|
36
|
+
chainId: number;
|
|
37
|
+
verifyingContract: Hex20;
|
|
38
|
+
}
|
|
39
|
+
/** Mirrors `RegistrationRequest` from `@coordination-network/user-client`, redeclared to avoid a cross-package dependency. */
|
|
40
|
+
interface SignedRegistrationRequest {
|
|
41
|
+
version: "v1";
|
|
42
|
+
networkId: Hex32;
|
|
43
|
+
schemeId: 1;
|
|
44
|
+
metaStealthAddress: Hex66;
|
|
45
|
+
chainId: number;
|
|
46
|
+
verifyingContract: Hex20;
|
|
47
|
+
signature: Hex65;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* `request` is posted to the member server verbatim. `securityLevel` rides along because
|
|
51
|
+
* registration is the one op every member app runs first, and it is the only thing a member needs
|
|
52
|
+
* about the wallet that the request itself does not already carry.
|
|
53
|
+
*/
|
|
54
|
+
interface RegistrationSignResult {
|
|
55
|
+
request: SignedRegistrationRequest;
|
|
56
|
+
securityLevel: SecurityLevel;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The wallet drives the whole issuance itself from these four endpoints, so a member app hands
|
|
60
|
+
* over the URLs rather than relaying each hop.
|
|
61
|
+
*/
|
|
62
|
+
interface CredentialIssueParams {
|
|
63
|
+
issuerNetworkId: Hex32;
|
|
64
|
+
approvalScope: ApprovalScope;
|
|
65
|
+
challengeUrl: string;
|
|
66
|
+
credentialsUrl: string;
|
|
67
|
+
/** The issuer's approved-issuer set, which the wallet checks the credential against before storing. */
|
|
68
|
+
issuersUrl: string;
|
|
69
|
+
/** The issuer's revocation snapshot, checked in the same step. */
|
|
70
|
+
revocationsUrl: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `refused` is the issuer server declining to issue, in its own error vocabulary — not a
|
|
74
|
+
* {@link LinkErrorCode}. A wallet-side failure throws instead.
|
|
75
|
+
*/
|
|
76
|
+
type CredentialIssueResult = {
|
|
77
|
+
status: "stored";
|
|
78
|
+
credentialId: string;
|
|
79
|
+
} | {
|
|
80
|
+
status: "alreadyHeld";
|
|
81
|
+
credentialId: string;
|
|
82
|
+
} | {
|
|
83
|
+
status: "refused";
|
|
84
|
+
code: string;
|
|
85
|
+
message: string;
|
|
86
|
+
};
|
|
87
|
+
interface PresentationCreateParams {
|
|
88
|
+
/** From the member server's `POST /v1/presentation-requests`; echoed back in the `/v1/verify` POST. */
|
|
89
|
+
requestId: string;
|
|
90
|
+
challenge: Hex31;
|
|
91
|
+
audience: string;
|
|
92
|
+
currentTime: number;
|
|
93
|
+
programme: string;
|
|
94
|
+
country: string;
|
|
95
|
+
issuersUrl: string;
|
|
96
|
+
/** One network-wide tree, so any member serves the same one; the verifier's is the one whose
|
|
97
|
+
* root it will accept. */
|
|
98
|
+
revocationsUrl: string;
|
|
99
|
+
responseUrl: string;
|
|
100
|
+
}
|
|
101
|
+
/** The verifier records the outcome under this id; the caller reads it from its own server. */
|
|
102
|
+
interface PresentationCreateResult {
|
|
103
|
+
verificationId: string;
|
|
104
|
+
}
|
|
105
|
+
/** Every op this protocol version defines, mapped to its params and result shapes. */
|
|
106
|
+
interface LinkOpMap {
|
|
107
|
+
"registration.sign": {
|
|
108
|
+
params: RegistrationSignParams;
|
|
109
|
+
result: RegistrationSignResult;
|
|
110
|
+
};
|
|
111
|
+
"credential.issue": {
|
|
112
|
+
params: CredentialIssueParams;
|
|
113
|
+
result: CredentialIssueResult;
|
|
114
|
+
};
|
|
115
|
+
"presentation.create": {
|
|
116
|
+
params: PresentationCreateParams;
|
|
117
|
+
result: PresentationCreateResult;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
declare const LINK_OPS: readonly ["registration.sign", "credential.issue", "presentation.create"];
|
|
121
|
+
type LinkOp = (typeof LINK_OPS)[number];
|
|
122
|
+
type Params<Op extends LinkOp> = LinkOpMap[Op]["params"];
|
|
123
|
+
type Result<Op extends LinkOp> = LinkOpMap[Op]["result"];
|
|
124
|
+
/** Distributive conditional: without it, `LinkRequest` (Op defaulting to `LinkOp`) would let any op pair with any op's params instead of only its own. */
|
|
125
|
+
type LinkRequest<Op extends LinkOp = LinkOp> = Op extends LinkOp ? {
|
|
126
|
+
id: string;
|
|
127
|
+
version: "v1";
|
|
128
|
+
caller: Caller;
|
|
129
|
+
callback: string;
|
|
130
|
+
op: Op;
|
|
131
|
+
params: Params<Op>;
|
|
132
|
+
} : never;
|
|
133
|
+
/** A request whose envelope decoded successfully but whose `op`/`params` are not yet validated against a schema. */
|
|
134
|
+
interface DecodedLinkRequest {
|
|
135
|
+
id: string;
|
|
136
|
+
version: "v1";
|
|
137
|
+
caller: Caller;
|
|
138
|
+
callback: string;
|
|
139
|
+
op: string;
|
|
140
|
+
params: unknown;
|
|
141
|
+
}
|
|
142
|
+
declare const LINK_ERROR_CODES: readonly ["USER_DENIED", "CONSENT_DENIED", "UNSUPPORTED_OP", "INVALID_REQUEST", "NOT_ONBOARDED", "NO_CREDENTIAL", "CREDENTIAL_REVOKED", "PROOF_FAILED", "ISSUER_UNREACHABLE", "VERIFIER_UNREACHABLE", "INTERNAL"];
|
|
143
|
+
type LinkErrorCode = (typeof LINK_ERROR_CODES)[number];
|
|
144
|
+
/** The response envelope. Unlike the request, it carries no `op`: the caller already knows which op it asked for. */
|
|
145
|
+
type LinkResponse<Op extends LinkOp = LinkOp> = (Op extends LinkOp ? {
|
|
146
|
+
id: string;
|
|
147
|
+
version: "v1";
|
|
148
|
+
ok: true;
|
|
149
|
+
result: Result<Op>;
|
|
150
|
+
} : never) | {
|
|
151
|
+
id: string;
|
|
152
|
+
version: "v1";
|
|
153
|
+
ok: false;
|
|
154
|
+
code: LinkErrorCode;
|
|
155
|
+
message?: string;
|
|
156
|
+
};
|
|
157
|
+
/** The handlers a user app registers with {@link createRequestHandler}, one per op it supports. */
|
|
158
|
+
type LinkHandlers = {
|
|
159
|
+
[Op in LinkOp]?: (params: Params<Op>, context: {
|
|
160
|
+
caller: Caller;
|
|
161
|
+
}) => Promise<Result<Op>>;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A response must stay on-device, in a custom app scheme: `http(s)` would let it leave the device
|
|
166
|
+
* entirely, and Android additionally resolves `intent`/`file`/`content`/`javascript`/`data` to
|
|
167
|
+
* behavior beyond opening another app (launching an arbitrary component, reading a local file, or
|
|
168
|
+
* running script), none of which this protocol's `callback` is meant to reach.
|
|
169
|
+
*
|
|
170
|
+
* The browser schemes map straight onto `http(s)://host/path` - `googlechrome://evil/cb` fetches
|
|
171
|
+
* `http://evil/cb` - so they defeat the rule exactly as `https` would. This list cannot be
|
|
172
|
+
* exhaustive: any installed app may register a scheme that proxies to the network, and nothing in
|
|
173
|
+
* the string says so. Closing it properly needs the signed response envelope and a consent screen
|
|
174
|
+
* showing the resolved callback, both already tracked as follow-ups.
|
|
175
|
+
*/
|
|
176
|
+
declare const DENIED_CALLBACK_SCHEMES: readonly ["http", "https", "intent", "file", "content", "javascript", "data", "googlechrome", "googlechromes", "x-safari-http", "x-safari-https", "microsoft-edge-http", "microsoft-edge-https", "firefox", "ftp", "ws", "wss"];
|
|
177
|
+
/**
|
|
178
|
+
* These fields name server endpoints the app itself fetches or posts to, arriving from an
|
|
179
|
+
* unauthenticated deep-link caller - an allowlist has no unknown-scheme bypass to worry about.
|
|
180
|
+
* TODO: production should require https only; that belongs in configuration, not this schema.
|
|
181
|
+
*/
|
|
182
|
+
declare const ALLOWED_ENDPOINT_URL_SCHEMES: readonly ["http", "https"];
|
|
183
|
+
/** `encodeResponse` always appends its own `?payload=` to `callback`, so a `?`/`#` already in it would bury or hide that parameter and leave the client waiting until timeout instead of ever decoding a response. */
|
|
184
|
+
declare const callbackSchema: z.ZodString;
|
|
185
|
+
declare const hex20Schema: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
186
|
+
declare const hex31Schema: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
187
|
+
declare const hex32Schema: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
188
|
+
declare const hex65Schema: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
189
|
+
declare const hex66Schema: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
190
|
+
declare const securityLevelSchema: z.ZodEnum<{
|
|
191
|
+
STRONGBOX: "STRONGBOX";
|
|
192
|
+
TRUSTED_ENVIRONMENT: "TRUSTED_ENVIRONMENT";
|
|
193
|
+
SOFTWARE: "SOFTWARE";
|
|
194
|
+
UNKNOWN: "UNKNOWN";
|
|
195
|
+
}>;
|
|
196
|
+
declare const callerSchema: z.ZodObject<{
|
|
197
|
+
name: z.ZodString;
|
|
198
|
+
scheme: z.ZodString;
|
|
199
|
+
}, z.core.$strict>;
|
|
200
|
+
declare const approvalScopeSchema: z.ZodObject<{
|
|
201
|
+
programme: z.ZodString;
|
|
202
|
+
country: z.ZodString;
|
|
203
|
+
}, z.core.$strict>;
|
|
204
|
+
declare const registrationSignParamsSchema: z.ZodObject<{
|
|
205
|
+
chainId: z.ZodNumber;
|
|
206
|
+
verifyingContract: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
207
|
+
}, z.core.$strict>;
|
|
208
|
+
declare const signedRegistrationRequestSchema: z.ZodObject<{
|
|
209
|
+
version: z.ZodLiteral<"v1">;
|
|
210
|
+
networkId: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
211
|
+
schemeId: z.ZodLiteral<1>;
|
|
212
|
+
metaStealthAddress: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
213
|
+
chainId: z.ZodNumber;
|
|
214
|
+
verifyingContract: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
215
|
+
signature: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
216
|
+
}, z.core.$strict>;
|
|
217
|
+
declare const registrationSignResultSchema: z.ZodObject<{
|
|
218
|
+
request: z.ZodObject<{
|
|
219
|
+
version: z.ZodLiteral<"v1">;
|
|
220
|
+
networkId: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
221
|
+
schemeId: z.ZodLiteral<1>;
|
|
222
|
+
metaStealthAddress: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
223
|
+
chainId: z.ZodNumber;
|
|
224
|
+
verifyingContract: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
225
|
+
signature: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
226
|
+
}, z.core.$strict>;
|
|
227
|
+
securityLevel: z.ZodEnum<{
|
|
228
|
+
STRONGBOX: "STRONGBOX";
|
|
229
|
+
TRUSTED_ENVIRONMENT: "TRUSTED_ENVIRONMENT";
|
|
230
|
+
SOFTWARE: "SOFTWARE";
|
|
231
|
+
UNKNOWN: "UNKNOWN";
|
|
232
|
+
}>;
|
|
233
|
+
}, z.core.$strict>;
|
|
234
|
+
declare const credentialIssueParamsSchema: z.ZodObject<{
|
|
235
|
+
issuerNetworkId: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
236
|
+
approvalScope: z.ZodObject<{
|
|
237
|
+
programme: z.ZodString;
|
|
238
|
+
country: z.ZodString;
|
|
239
|
+
}, z.core.$strict>;
|
|
240
|
+
challengeUrl: z.ZodString;
|
|
241
|
+
credentialsUrl: z.ZodString;
|
|
242
|
+
issuersUrl: z.ZodString;
|
|
243
|
+
revocationsUrl: z.ZodString;
|
|
244
|
+
}, z.core.$strict>;
|
|
245
|
+
declare const credentialIssueResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
246
|
+
status: z.ZodLiteral<"stored">;
|
|
247
|
+
credentialId: z.ZodString;
|
|
248
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
249
|
+
status: z.ZodLiteral<"alreadyHeld">;
|
|
250
|
+
credentialId: z.ZodString;
|
|
251
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
252
|
+
status: z.ZodLiteral<"refused">;
|
|
253
|
+
code: z.ZodString;
|
|
254
|
+
message: z.ZodString;
|
|
255
|
+
}, z.core.$strict>], "status">;
|
|
256
|
+
declare const presentationCreateParamsSchema: z.ZodObject<{
|
|
257
|
+
requestId: z.ZodString;
|
|
258
|
+
challenge: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
259
|
+
audience: z.ZodString;
|
|
260
|
+
currentTime: z.ZodNumber;
|
|
261
|
+
programme: z.ZodString;
|
|
262
|
+
country: z.ZodString;
|
|
263
|
+
issuersUrl: z.ZodString;
|
|
264
|
+
revocationsUrl: z.ZodString;
|
|
265
|
+
responseUrl: z.ZodString;
|
|
266
|
+
}, z.core.$strict>;
|
|
267
|
+
declare const presentationCreateResultSchema: z.ZodObject<{
|
|
268
|
+
verificationId: z.ZodString;
|
|
269
|
+
}, z.core.$strict>;
|
|
270
|
+
/** Looked up by the decoded `op` string, so the same schema can be resolved dynamically at runtime. */
|
|
271
|
+
declare const paramsSchemas: Record<LinkOp, z.ZodTypeAny>;
|
|
272
|
+
declare const resultSchemas: Record<LinkOp, z.ZodTypeAny>;
|
|
273
|
+
/** Envelope only: `op`/`params` stay unvalidated because a request that fails those checks still needs a usable `id`/`callback` to answer with an `ok: false` response. */
|
|
274
|
+
declare const linkRequestEnvelopeSchema: z.ZodObject<{
|
|
275
|
+
id: z.ZodString;
|
|
276
|
+
version: z.ZodLiteral<"v1">;
|
|
277
|
+
caller: z.ZodObject<{
|
|
278
|
+
name: z.ZodString;
|
|
279
|
+
scheme: z.ZodString;
|
|
280
|
+
}, z.core.$strict>;
|
|
281
|
+
callback: z.ZodString;
|
|
282
|
+
op: z.ZodString;
|
|
283
|
+
params: z.ZodUnknown;
|
|
284
|
+
}, z.core.$strict>;
|
|
285
|
+
/** Written out per op rather than mapped from `LINK_OPS`: `z.discriminatedUnion` needs each branch's literal `op` at the type level, which a `z.ZodTypeAny`-typed loop erases. */
|
|
286
|
+
declare const linkRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
287
|
+
op: z.ZodLiteral<"registration.sign">;
|
|
288
|
+
params: z.ZodObject<{
|
|
289
|
+
chainId: z.ZodNumber;
|
|
290
|
+
verifyingContract: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
291
|
+
}, z.core.$strict>;
|
|
292
|
+
id: z.ZodString;
|
|
293
|
+
version: z.ZodLiteral<"v1">;
|
|
294
|
+
caller: z.ZodObject<{
|
|
295
|
+
name: z.ZodString;
|
|
296
|
+
scheme: z.ZodString;
|
|
297
|
+
}, z.core.$strict>;
|
|
298
|
+
callback: z.ZodString;
|
|
299
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
300
|
+
op: z.ZodLiteral<"credential.issue">;
|
|
301
|
+
params: z.ZodObject<{
|
|
302
|
+
issuerNetworkId: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
303
|
+
approvalScope: z.ZodObject<{
|
|
304
|
+
programme: z.ZodString;
|
|
305
|
+
country: z.ZodString;
|
|
306
|
+
}, z.core.$strict>;
|
|
307
|
+
challengeUrl: z.ZodString;
|
|
308
|
+
credentialsUrl: z.ZodString;
|
|
309
|
+
issuersUrl: z.ZodString;
|
|
310
|
+
revocationsUrl: z.ZodString;
|
|
311
|
+
}, z.core.$strict>;
|
|
312
|
+
id: z.ZodString;
|
|
313
|
+
version: z.ZodLiteral<"v1">;
|
|
314
|
+
caller: z.ZodObject<{
|
|
315
|
+
name: z.ZodString;
|
|
316
|
+
scheme: z.ZodString;
|
|
317
|
+
}, z.core.$strict>;
|
|
318
|
+
callback: z.ZodString;
|
|
319
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
320
|
+
op: z.ZodLiteral<"presentation.create">;
|
|
321
|
+
params: z.ZodObject<{
|
|
322
|
+
requestId: z.ZodString;
|
|
323
|
+
challenge: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
|
|
324
|
+
audience: z.ZodString;
|
|
325
|
+
currentTime: z.ZodNumber;
|
|
326
|
+
programme: z.ZodString;
|
|
327
|
+
country: z.ZodString;
|
|
328
|
+
issuersUrl: z.ZodString;
|
|
329
|
+
revocationsUrl: z.ZodString;
|
|
330
|
+
responseUrl: z.ZodString;
|
|
331
|
+
}, z.core.$strict>;
|
|
332
|
+
id: z.ZodString;
|
|
333
|
+
version: z.ZodLiteral<"v1">;
|
|
334
|
+
caller: z.ZodObject<{
|
|
335
|
+
name: z.ZodString;
|
|
336
|
+
scheme: z.ZodString;
|
|
337
|
+
}, z.core.$strict>;
|
|
338
|
+
callback: z.ZodString;
|
|
339
|
+
}, z.core.$strict>], "op">;
|
|
340
|
+
declare const linkErrorCodeSchema: z.ZodEnum<{
|
|
341
|
+
USER_DENIED: "USER_DENIED";
|
|
342
|
+
CONSENT_DENIED: "CONSENT_DENIED";
|
|
343
|
+
UNSUPPORTED_OP: "UNSUPPORTED_OP";
|
|
344
|
+
INVALID_REQUEST: "INVALID_REQUEST";
|
|
345
|
+
NOT_ONBOARDED: "NOT_ONBOARDED";
|
|
346
|
+
NO_CREDENTIAL: "NO_CREDENTIAL";
|
|
347
|
+
CREDENTIAL_REVOKED: "CREDENTIAL_REVOKED";
|
|
348
|
+
PROOF_FAILED: "PROOF_FAILED";
|
|
349
|
+
ISSUER_UNREACHABLE: "ISSUER_UNREACHABLE";
|
|
350
|
+
VERIFIER_UNREACHABLE: "VERIFIER_UNREACHABLE";
|
|
351
|
+
INTERNAL: "INTERNAL";
|
|
352
|
+
}>;
|
|
353
|
+
declare const linkResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
354
|
+
id: z.ZodString;
|
|
355
|
+
version: z.ZodLiteral<"v1">;
|
|
356
|
+
ok: z.ZodLiteral<true>;
|
|
357
|
+
result: z.ZodUnknown;
|
|
358
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
359
|
+
id: z.ZodString;
|
|
360
|
+
version: z.ZodLiteral<"v1">;
|
|
361
|
+
ok: z.ZodLiteral<false>;
|
|
362
|
+
code: z.ZodEnum<{
|
|
363
|
+
USER_DENIED: "USER_DENIED";
|
|
364
|
+
CONSENT_DENIED: "CONSENT_DENIED";
|
|
365
|
+
UNSUPPORTED_OP: "UNSUPPORTED_OP";
|
|
366
|
+
INVALID_REQUEST: "INVALID_REQUEST";
|
|
367
|
+
NOT_ONBOARDED: "NOT_ONBOARDED";
|
|
368
|
+
NO_CREDENTIAL: "NO_CREDENTIAL";
|
|
369
|
+
CREDENTIAL_REVOKED: "CREDENTIAL_REVOKED";
|
|
370
|
+
PROOF_FAILED: "PROOF_FAILED";
|
|
371
|
+
ISSUER_UNREACHABLE: "ISSUER_UNREACHABLE";
|
|
372
|
+
VERIFIER_UNREACHABLE: "VERIFIER_UNREACHABLE";
|
|
373
|
+
INTERNAL: "INTERNAL";
|
|
374
|
+
}>;
|
|
375
|
+
message: z.ZodOptional<z.ZodString>;
|
|
376
|
+
}, z.core.$strict>], "ok">;
|
|
377
|
+
|
|
378
|
+
/** A link-protocol failure, discriminated by the same `code` a wire `{ ok: false }` response carries. */
|
|
379
|
+
declare class LinkError extends Error {
|
|
380
|
+
readonly code: LinkErrorCode;
|
|
381
|
+
constructor(code: LinkErrorCode, message?: string);
|
|
382
|
+
}
|
|
383
|
+
/** Thrown by encodeRequest/encodeResponse when the built URL exceeds MAX_URL_LENGTH. */
|
|
384
|
+
declare class LinkPayloadTooLargeError extends LinkError {
|
|
385
|
+
constructor(message: string);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Thrown by decodeRequest/decodeResponse when a URL can't be decoded into a well-formed v1
|
|
389
|
+
* envelope - the only failure this package ever throws rather than turning into an `ok: false`
|
|
390
|
+
* response, because without a valid `id`/`callback` there is nowhere to send that response.
|
|
391
|
+
*/
|
|
392
|
+
declare class LinkDecodeError extends LinkError {
|
|
393
|
+
constructor(message: string);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
declare const USER_APP_SCHEME = "userapp";
|
|
397
|
+
/** Above this, a platform's `Linking.openURL` (and some OSes) start truncating or refusing the URL. */
|
|
398
|
+
declare const MAX_URL_LENGTH = 2000;
|
|
399
|
+
/** Encodes bytes with the base64url alphabet (RFC 4648 §5), unpadded. No Node `Buffer`: `atob`/`btoa` only. */
|
|
400
|
+
declare function base64UrlEncode(bytes: Uint8Array): string;
|
|
401
|
+
declare function base64UrlDecode(value: string): Uint8Array;
|
|
402
|
+
declare function encodeRequest<Op extends LinkOp>(request: LinkRequest<Op>, options?: {
|
|
403
|
+
scheme?: string;
|
|
404
|
+
}): string;
|
|
405
|
+
declare function decodeRequest(url: string): DecodedLinkRequest;
|
|
406
|
+
declare function encodeResponse<Op extends LinkOp>(callback: string, response: LinkResponse<Op>): string;
|
|
407
|
+
declare function decodeResponse(url: string): LinkResponse;
|
|
408
|
+
|
|
409
|
+
interface LinkClientOptions {
|
|
410
|
+
caller: Caller;
|
|
411
|
+
/** URL the user app opens with the response; must route back into this member app. */
|
|
412
|
+
callback: string;
|
|
413
|
+
openUrl: (url: string) => Promise<void>;
|
|
414
|
+
/** Subscribes to incoming URLs (e.g. `Linking.addEventListener`); returns an unsubscribe function. */
|
|
415
|
+
onUrl: (listener: (url: string) => void) => () => void;
|
|
416
|
+
/** Milliseconds before a request is abandoned. Default 120000. */
|
|
417
|
+
timeoutMs?: number;
|
|
418
|
+
/** Overrides the request `id` generator; tests pass a deterministic one. */
|
|
419
|
+
randomId?: () => string;
|
|
420
|
+
scheme?: string;
|
|
421
|
+
}
|
|
422
|
+
interface LinkClient {
|
|
423
|
+
request<Op extends LinkOp>(op: Op, params: Params<Op>): Promise<Result<Op>>;
|
|
424
|
+
}
|
|
425
|
+
/** One member-app-side client per callback URL: exactly one request in flight, matched by id. */
|
|
426
|
+
declare function createLinkClient(options: LinkClientOptions): LinkClient;
|
|
427
|
+
|
|
428
|
+
interface RequestHandlerOptions {
|
|
429
|
+
handlers: LinkHandlers;
|
|
430
|
+
/** Shows the user what's being requested and by whom; resolves to whether they approved it. */
|
|
431
|
+
consent: (request: DecodedLinkRequest) => Promise<boolean>;
|
|
432
|
+
}
|
|
433
|
+
interface RequestHandler {
|
|
434
|
+
/** Decodes and dispatches `url`, returning the response URL to open. Never rejects except on {@link LinkDecodeError}. */
|
|
435
|
+
handle(url: string): Promise<string>;
|
|
436
|
+
}
|
|
437
|
+
declare function createRequestHandler(options: RequestHandlerOptions): RequestHandler;
|
|
438
|
+
|
|
439
|
+
export { ALLOWED_ENDPOINT_URL_SCHEMES, DENIED_CALLBACK_SCHEMES, LINK_ERROR_CODES, LINK_OPS, LinkDecodeError, LinkError, LinkPayloadTooLargeError, MAX_URL_LENGTH, SECURITY_LEVELS, USER_APP_SCHEME, approvalScopeSchema, base64UrlDecode, base64UrlEncode, callbackSchema, callerSchema, createLinkClient, createRequestHandler, credentialIssueParamsSchema, credentialIssueResultSchema, decodeRequest, decodeResponse, encodeRequest, encodeResponse, hex20Schema, hex31Schema, hex32Schema, hex65Schema, hex66Schema, linkErrorCodeSchema, linkRequestEnvelopeSchema, linkRequestSchema, linkResponseSchema, paramsSchemas, presentationCreateParamsSchema, presentationCreateResultSchema, registrationSignParamsSchema, registrationSignResultSchema, resultSchemas, securityLevelSchema, signedRegistrationRequestSchema };
|
|
440
|
+
export type { ApprovalScope, Caller, CredentialIssueParams, CredentialIssueResult, DecodedLinkRequest, Hex, Hex20, Hex31, Hex32, Hex65, Hex66, Jwk, LinkClient, LinkClientOptions, LinkErrorCode, LinkHandlers, LinkOp, LinkOpMap, LinkRequest, LinkResponse, Params, PresentationCreateParams, PresentationCreateResult, RegistrationSignParams, RegistrationSignResult, RequestHandler, RequestHandlerOptions, Result, SecurityLevel, SignedRegistrationRequest };
|
package/types/web.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** Identifies the member app making a request, as declared by that app (not verified by the OS). */
|
|
2
|
+
interface Caller {
|
|
3
|
+
name: string;
|
|
4
|
+
scheme: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface LinkClientOptions {
|
|
8
|
+
caller: Caller;
|
|
9
|
+
/** URL the user app opens with the response; must route back into this member app. */
|
|
10
|
+
callback: string;
|
|
11
|
+
openUrl: (url: string) => Promise<void>;
|
|
12
|
+
/** Subscribes to incoming URLs (e.g. `Linking.addEventListener`); returns an unsubscribe function. */
|
|
13
|
+
onUrl: (listener: (url: string) => void) => () => void;
|
|
14
|
+
/** Milliseconds before a request is abandoned. Default 120000. */
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** Overrides the request `id` generator; tests pass a deterministic one. */
|
|
17
|
+
randomId?: () => string;
|
|
18
|
+
scheme?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface RequestHandler {
|
|
22
|
+
/** Decodes and dispatches `url`, returning the response URL to open. Never rejects except on {@link LinkDecodeError}. */
|
|
23
|
+
handle(url: string): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface WebLinkTransportOptions {
|
|
27
|
+
/** Browser URL of the user web app. The encoded request query is appended to this URL. */
|
|
28
|
+
userAppUrl: string;
|
|
29
|
+
/** Exact origin trusted to return responses, for example `http://localhost:5173`. */
|
|
30
|
+
userAppOrigin: string;
|
|
31
|
+
popupName?: string;
|
|
32
|
+
popupFeatures?: string;
|
|
33
|
+
/** How often to detect a popup closed before responding. Default 100ms. */
|
|
34
|
+
closePollIntervalMs?: number;
|
|
35
|
+
/** Time to wait for an already-posted response after observing a closed popup. Default 250ms. */
|
|
36
|
+
closeGracePeriodMs?: number;
|
|
37
|
+
/** Injectable for tests; defaults to the current browser window. */
|
|
38
|
+
window?: Window;
|
|
39
|
+
}
|
|
40
|
+
type WebLinkTransport = Pick<LinkClientOptions, "openUrl" | "onUrl"> & {
|
|
41
|
+
/** Reserves the popup synchronously inside a click handler before asynchronous flow work begins. */
|
|
42
|
+
prepare(): void;
|
|
43
|
+
close(): void;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Supplies the two transport functions consumed by `createLinkClient`. The protocol request stays
|
|
47
|
+
* unchanged; only its query string is carried to the browser user app instead of a mobile scheme.
|
|
48
|
+
*/
|
|
49
|
+
declare function createWebLinkTransport(options: WebLinkTransportOptions): WebLinkTransport;
|
|
50
|
+
interface WebLinkResponderOptions {
|
|
51
|
+
handler: RequestHandler;
|
|
52
|
+
/** Member-app origins allowed to open the user app and receive a response. */
|
|
53
|
+
allowedMemberOrigins: readonly string[];
|
|
54
|
+
/** Maximum time to wait for the opener handshake. Default 5000ms. */
|
|
55
|
+
handshakeTimeoutMs?: number;
|
|
56
|
+
/** Close the user-app popup after posting. Default true. */
|
|
57
|
+
closeAfterResponding?: boolean;
|
|
58
|
+
/** Injectable for tests; defaults to the current browser window. */
|
|
59
|
+
window?: Window;
|
|
60
|
+
}
|
|
61
|
+
/** Authenticates the opener, dispatches the request, and posts the response to its verified origin. */
|
|
62
|
+
declare function respondToWebLinkRequest(options: WebLinkResponderOptions): Promise<void>;
|
|
63
|
+
interface EmbeddedWebLinkTransportOptions {
|
|
64
|
+
userAppUrl: string;
|
|
65
|
+
userAppOrigin: string;
|
|
66
|
+
frame: HTMLIFrameElement;
|
|
67
|
+
window?: Window;
|
|
68
|
+
}
|
|
69
|
+
/** Carries the browser protocol through an in-page iframe, avoiding popup-blocker behavior. */
|
|
70
|
+
declare function createEmbeddedWebLinkTransport(options: EmbeddedWebLinkTransportOptions): WebLinkTransport;
|
|
71
|
+
interface RelayWebLinkTransportOptions {
|
|
72
|
+
relayUrl: string;
|
|
73
|
+
sender: string;
|
|
74
|
+
pollIntervalMs?: number;
|
|
75
|
+
fetcher?: typeof fetch;
|
|
76
|
+
}
|
|
77
|
+
/** Carries unchanged link URLs through the local demo relay between independently opened pages. */
|
|
78
|
+
declare function createRelayWebLinkTransport(options: RelayWebLinkTransportOptions): Pick<LinkClientOptions, "openUrl" | "onUrl">;
|
|
79
|
+
|
|
80
|
+
export { createEmbeddedWebLinkTransport, createRelayWebLinkTransport, createWebLinkTransport, respondToWebLinkRequest };
|
|
81
|
+
export type { EmbeddedWebLinkTransportOptions, RelayWebLinkTransportOptions, WebLinkResponderOptions, WebLinkTransport, WebLinkTransportOptions };
|