@x1id/resolve 0.23.0 → 0.44.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 +97 -0
- package/dist/accounts.d.ts +6 -0
- package/dist/accounts.js +6 -1
- package/dist/agent.d.ts +184 -8
- package/dist/agent.js +351 -3
- package/dist/agentPolicy.d.ts +238 -0
- package/dist/agentPolicy.js +364 -0
- package/dist/attestation.d.ts +25 -0
- package/dist/attestation.js +25 -0
- package/dist/checkout.d.ts +216 -0
- package/dist/checkout.js +327 -0
- package/dist/credentials.d.ts +466 -0
- package/dist/credentials.js +669 -0
- package/dist/gate.d.ts +94 -4
- package/dist/gate.js +206 -7
- package/dist/gateClient.d.ts +12 -0
- package/dist/giftLink.d.ts +272 -0
- package/dist/giftLink.js +452 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +32 -0
- package/dist/loginButton.d.ts +69 -0
- package/dist/loginButton.js +207 -0
- package/dist/logo.d.ts +1 -0
- package/dist/logo.js +8 -0
- package/dist/messaging.d.ts +439 -0
- package/dist/messaging.js +979 -0
- package/dist/nsGate.d.ts +98 -0
- package/dist/nsGate.js +172 -0
- package/dist/oidc.d.ts +272 -0
- package/dist/oidc.js +641 -0
- package/dist/pay.d.ts +513 -0
- package/dist/pay.js +756 -0
- package/dist/payment.d.ts +9 -2
- package/dist/payment.js +9 -2
- package/dist/recovery.d.ts +186 -0
- package/dist/recovery.js +276 -0
- package/dist/recoveryNft.d.ts +183 -0
- package/dist/recoveryNft.js +291 -0
- package/dist/shield.d.ts +373 -0
- package/dist/shield.js +695 -0
- package/dist/subscription.d.ts +439 -0
- package/dist/subscription.js +642 -0
- package/dist/textRecords.d.ts +52 -6
- package/dist/textRecords.js +80 -8
- package/dist/types.d.ts +3 -0
- package/dist/x402Receipt.d.ts +246 -0
- package/dist/x402Receipt.js +374 -0
- package/package.json +18 -1
- package/schema/agent-manifest.json +91 -8
package/dist/nsGate.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLD token gate (program upgrade 6, OpenProject #8457; spec
|
|
3
|
+
* docs/specs/tld-communities.md §2).
|
|
4
|
+
*
|
|
5
|
+
* A TLD creator sets `["ns_gate", label]` = { mint, min_amount } with
|
|
6
|
+
* {@link buildSetNamespaceGateIx}; the namespace's registration mode becomes
|
|
7
|
+
* `TokenGated` (2). A non-creator, non-minter payer then registers through
|
|
8
|
+
* `register_under_namespace_v2` with two more remaining accounts after the
|
|
9
|
+
* pricing PDA: the gate PDA and a token account of the gate mint owned by the
|
|
10
|
+
* RECIPIENT (the registration's `owner`, the new name's owner) holding
|
|
11
|
+
* `>= min_amount` ({@link appendNamespaceGateAccounts}). The gate checks who
|
|
12
|
+
* RECEIVES the name, never who pays: a holder cannot buy a gated name for a
|
|
13
|
+
* non-holder, and anyone may pay for a holder. A creator or authorised-minter
|
|
14
|
+
* PAYER bypasses the gate (issuing to anyone).
|
|
15
|
+
*
|
|
16
|
+
* {@link decodeRegistrationMode} FAILS CLOSED: any byte other than 0 (Open)
|
|
17
|
+
* is not open — 1 is invite-only, 2 token-gated, anything else "restricted".
|
|
18
|
+
*/
|
|
19
|
+
import type { AddressLike, BuiltInstruction } from "./delegate.js";
|
|
20
|
+
import type { PdaFinder } from "./namespacePricing.js";
|
|
21
|
+
export declare const NAMESPACE_GATE_SEED = "ns_gate";
|
|
22
|
+
export declare const SET_NAMESPACE_GATE_DISCRIMINATOR: Uint8Array<ArrayBuffer>;
|
|
23
|
+
export declare const CLEAR_NAMESPACE_GATE_DISCRIMINATOR: Uint8Array<ArrayBuffer>;
|
|
24
|
+
/** `sha256("account:NamespaceGate")[0..8]`. */
|
|
25
|
+
export declare const NAMESPACE_GATE_ACCOUNT_DISCRIMINATOR: Uint8Array<ArrayBuffer>;
|
|
26
|
+
/** `NamespaceRegistrationMode` bytes. */
|
|
27
|
+
export declare const NAMESPACE_MODE_OPEN = 0;
|
|
28
|
+
export declare const NAMESPACE_MODE_INVITE_ONLY = 1;
|
|
29
|
+
export declare const NAMESPACE_MODE_TOKEN_GATED = 2;
|
|
30
|
+
/** Program error codes (NsGateError, offset 6220). */
|
|
31
|
+
export declare const NAMESPACE_GATE_ERRORS: Readonly<Record<number, string>>;
|
|
32
|
+
export type NamespaceRegistrationModeName = "open" | "invite-only" | "token-gated" | "restricted";
|
|
33
|
+
/** Map a raw mode byte. FAIL CLOSED: only 0 is open; an unknown value (a
|
|
34
|
+
* future mode this client does not know) is "restricted", never "open". */
|
|
35
|
+
export declare function decodeRegistrationMode(byte: number | undefined): NamespaceRegistrationModeName;
|
|
36
|
+
/** `["ns_gate", label]`. */
|
|
37
|
+
export declare function deriveNamespaceGatePda(find: PdaFinder, programId: AddressLike, label: string): Promise<string>;
|
|
38
|
+
export interface SetNamespaceGateParams {
|
|
39
|
+
readonly programId: AddressLike;
|
|
40
|
+
/** The namespace creator (signer; pays the gate's rent on first set). */
|
|
41
|
+
readonly creator: AddressLike;
|
|
42
|
+
readonly namespace: AddressLike;
|
|
43
|
+
readonly namespaceGate: AddressLike;
|
|
44
|
+
readonly label: string;
|
|
45
|
+
/** SPL Token or Token-2022 mint. */
|
|
46
|
+
readonly mint: AddressLike;
|
|
47
|
+
/** Base units the recipient (the new name's owner) must hold (> 0). */
|
|
48
|
+
readonly minAmount: bigint;
|
|
49
|
+
}
|
|
50
|
+
/** `set_namespace_gate(label, mint, min_amount)`: creator(s,w), namespace(w), ns_gate(w), mint, system. Sets mode 2. */
|
|
51
|
+
export declare function buildSetNamespaceGateIx(p: SetNamespaceGateParams): BuiltInstruction;
|
|
52
|
+
export interface ClearNamespaceGateParams {
|
|
53
|
+
readonly programId: AddressLike;
|
|
54
|
+
readonly creator: AddressLike;
|
|
55
|
+
readonly namespace: AddressLike;
|
|
56
|
+
readonly namespaceGate: AddressLike;
|
|
57
|
+
readonly label: string;
|
|
58
|
+
}
|
|
59
|
+
/** `clear_namespace_gate(label)`: creator(s,w), namespace(w), ns_gate(w). Mode 2 returns to Open. */
|
|
60
|
+
export declare function buildClearNamespaceGateIx(p: ClearNamespaceGateParams): BuiltInstruction;
|
|
61
|
+
/**
|
|
62
|
+
* Append the gate accounts to a `buildRegisterUnderNamespaceV2Ix` result
|
|
63
|
+
* (which ends with the pricing PDA at `remaining_accounts[0]`): `[1]` the
|
|
64
|
+
* `["ns_gate", label]` PDA, `[2]` a gate-mint token account OWNED BY THE
|
|
65
|
+
* RECIPIENT (the instruction's `owner`, not the payer). Not needed when the
|
|
66
|
+
* payer is the creator or an authorised minter.
|
|
67
|
+
*/
|
|
68
|
+
export declare function appendNamespaceGateAccounts(ix: BuiltInstruction, namespaceGate: AddressLike, ownerTokenAccount: AddressLike): BuiltInstruction;
|
|
69
|
+
/**
|
|
70
|
+
* Client-side mirror of the program's gate test, for UI pre-checks (the
|
|
71
|
+
* program is the authority). `payer` decides the bypass (creator, or a
|
|
72
|
+
* minter the caller knows is authorised); otherwise the `recipient` must own
|
|
73
|
+
* `tokenAccount` (decoded base layout: `owner`, `mint`, `amount`) of the gate
|
|
74
|
+
* mint with `amount >= gate.minAmount`.
|
|
75
|
+
*/
|
|
76
|
+
export declare function namespaceGateAllows(p: {
|
|
77
|
+
readonly gate: NamespaceGate;
|
|
78
|
+
readonly creator: AddressLike;
|
|
79
|
+
readonly payer: AddressLike;
|
|
80
|
+
readonly recipient: AddressLike;
|
|
81
|
+
readonly payerIsAuthorisedMinter?: boolean;
|
|
82
|
+
readonly tokenAccount?: {
|
|
83
|
+
readonly mint: AddressLike;
|
|
84
|
+
readonly owner: AddressLike;
|
|
85
|
+
readonly amount: bigint;
|
|
86
|
+
} | null;
|
|
87
|
+
}): boolean;
|
|
88
|
+
export interface NamespaceGate {
|
|
89
|
+
readonly version: number;
|
|
90
|
+
readonly bump: number;
|
|
91
|
+
readonly mint: string;
|
|
92
|
+
readonly tokenProgram: string;
|
|
93
|
+
readonly minAmount: bigint;
|
|
94
|
+
readonly updatedAt: bigint;
|
|
95
|
+
readonly label: string;
|
|
96
|
+
}
|
|
97
|
+
/** Decode a `NamespaceGate` (disc | version | bump | mint@10 | token_program@42 | min_amount@74 | updated_at@82 | label@90). */
|
|
98
|
+
export declare function decodeNamespaceGate(data: Uint8Array): NamespaceGate | null;
|
package/dist/nsGate.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLD token gate (program upgrade 6, OpenProject #8457; spec
|
|
3
|
+
* docs/specs/tld-communities.md §2).
|
|
4
|
+
*
|
|
5
|
+
* A TLD creator sets `["ns_gate", label]` = { mint, min_amount } with
|
|
6
|
+
* {@link buildSetNamespaceGateIx}; the namespace's registration mode becomes
|
|
7
|
+
* `TokenGated` (2). A non-creator, non-minter payer then registers through
|
|
8
|
+
* `register_under_namespace_v2` with two more remaining accounts after the
|
|
9
|
+
* pricing PDA: the gate PDA and a token account of the gate mint owned by the
|
|
10
|
+
* RECIPIENT (the registration's `owner`, the new name's owner) holding
|
|
11
|
+
* `>= min_amount` ({@link appendNamespaceGateAccounts}). The gate checks who
|
|
12
|
+
* RECEIVES the name, never who pays: a holder cannot buy a gated name for a
|
|
13
|
+
* non-holder, and anyone may pay for a holder. A creator or authorised-minter
|
|
14
|
+
* PAYER bypasses the gate (issuing to anyone).
|
|
15
|
+
*
|
|
16
|
+
* {@link decodeRegistrationMode} FAILS CLOSED: any byte other than 0 (Open)
|
|
17
|
+
* is not open — 1 is invite-only, 2 token-gated, anything else "restricted".
|
|
18
|
+
*/
|
|
19
|
+
import { encodeBase58, decodeBase58_32 } from "./base58.js";
|
|
20
|
+
export const NAMESPACE_GATE_SEED = "ns_gate";
|
|
21
|
+
export const SET_NAMESPACE_GATE_DISCRIMINATOR = Uint8Array.from([1, 89, 110, 52, 27, 181, 228, 203]);
|
|
22
|
+
export const CLEAR_NAMESPACE_GATE_DISCRIMINATOR = Uint8Array.from([168, 198, 44, 238, 202, 217, 63, 7]);
|
|
23
|
+
/** `sha256("account:NamespaceGate")[0..8]`. */
|
|
24
|
+
export const NAMESPACE_GATE_ACCOUNT_DISCRIMINATOR = Uint8Array.from([76, 234, 116, 167, 238, 185, 71, 218]);
|
|
25
|
+
/** `NamespaceRegistrationMode` bytes. */
|
|
26
|
+
export const NAMESPACE_MODE_OPEN = 0;
|
|
27
|
+
export const NAMESPACE_MODE_INVITE_ONLY = 1;
|
|
28
|
+
export const NAMESPACE_MODE_TOKEN_GATED = 2;
|
|
29
|
+
/** Program error codes (NsGateError, offset 6220). */
|
|
30
|
+
export const NAMESPACE_GATE_ERRORS = {
|
|
31
|
+
6220: "NsGateRequired",
|
|
32
|
+
6221: "NsGateMismatch",
|
|
33
|
+
6222: "NsGateTokenAccountMismatch",
|
|
34
|
+
6223: "NsGateBalanceTooLow",
|
|
35
|
+
6224: "NsGateInvalid",
|
|
36
|
+
};
|
|
37
|
+
/** Map a raw mode byte. FAIL CLOSED: only 0 is open; an unknown value (a
|
|
38
|
+
* future mode this client does not know) is "restricted", never "open". */
|
|
39
|
+
export function decodeRegistrationMode(byte) {
|
|
40
|
+
if (byte === NAMESPACE_MODE_OPEN)
|
|
41
|
+
return "open";
|
|
42
|
+
if (byte === NAMESPACE_MODE_INVITE_ONLY)
|
|
43
|
+
return "invite-only";
|
|
44
|
+
if (byte === NAMESPACE_MODE_TOKEN_GATED)
|
|
45
|
+
return "token-gated";
|
|
46
|
+
return "restricted";
|
|
47
|
+
}
|
|
48
|
+
const SYSTEM_PROGRAM = "11111111111111111111111111111111";
|
|
49
|
+
const TE = new TextEncoder();
|
|
50
|
+
function bytes(v, what) {
|
|
51
|
+
if (typeof v === "string") {
|
|
52
|
+
const b = decodeBase58_32(v);
|
|
53
|
+
if (!b)
|
|
54
|
+
throw new Error(`${what} is not a valid base58 address`);
|
|
55
|
+
return b;
|
|
56
|
+
}
|
|
57
|
+
if (v.length !== 32)
|
|
58
|
+
throw new Error(`${what} must be exactly 32 bytes`);
|
|
59
|
+
return v;
|
|
60
|
+
}
|
|
61
|
+
function k(v, what, isSigner, isWritable) {
|
|
62
|
+
return { pubkey: encodeBase58(bytes(v, what)), isSigner, isWritable };
|
|
63
|
+
}
|
|
64
|
+
function borshStr(s) {
|
|
65
|
+
const b = TE.encode(s);
|
|
66
|
+
const out = new Uint8Array(4 + b.length);
|
|
67
|
+
new DataView(out.buffer).setUint32(0, b.length, true);
|
|
68
|
+
out.set(b, 4);
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/** `["ns_gate", label]`. */
|
|
72
|
+
export async function deriveNamespaceGatePda(find, programId, label) {
|
|
73
|
+
return find([TE.encode(NAMESPACE_GATE_SEED), TE.encode(label)], encodeBase58(bytes(programId, "programId")));
|
|
74
|
+
}
|
|
75
|
+
/** `set_namespace_gate(label, mint, min_amount)`: creator(s,w), namespace(w), ns_gate(w), mint, system. Sets mode 2. */
|
|
76
|
+
export function buildSetNamespaceGateIx(p) {
|
|
77
|
+
if (typeof p.minAmount !== "bigint" || p.minAmount <= 0n || p.minAmount > 0xffffffffffffffffn) {
|
|
78
|
+
throw new Error("minAmount must be a u64 bigint > 0");
|
|
79
|
+
}
|
|
80
|
+
const amt = new Uint8Array(8);
|
|
81
|
+
new DataView(amt.buffer).setBigUint64(0, p.minAmount, true);
|
|
82
|
+
const label = borshStr(p.label);
|
|
83
|
+
const data = new Uint8Array(8 + label.length + 32 + 8);
|
|
84
|
+
data.set(SET_NAMESPACE_GATE_DISCRIMINATOR, 0);
|
|
85
|
+
data.set(label, 8);
|
|
86
|
+
data.set(bytes(p.mint, "mint"), 8 + label.length);
|
|
87
|
+
data.set(amt, 8 + label.length + 32);
|
|
88
|
+
return {
|
|
89
|
+
programId: encodeBase58(bytes(p.programId, "programId")),
|
|
90
|
+
keys: [
|
|
91
|
+
k(p.creator, "creator", true, true),
|
|
92
|
+
k(p.namespace, "namespace", false, true),
|
|
93
|
+
k(p.namespaceGate, "namespaceGate", false, true),
|
|
94
|
+
k(p.mint, "mint", false, false),
|
|
95
|
+
k(SYSTEM_PROGRAM, "systemProgram", false, false),
|
|
96
|
+
],
|
|
97
|
+
data,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** `clear_namespace_gate(label)`: creator(s,w), namespace(w), ns_gate(w). Mode 2 returns to Open. */
|
|
101
|
+
export function buildClearNamespaceGateIx(p) {
|
|
102
|
+
const label = borshStr(p.label);
|
|
103
|
+
const data = new Uint8Array(8 + label.length);
|
|
104
|
+
data.set(CLEAR_NAMESPACE_GATE_DISCRIMINATOR, 0);
|
|
105
|
+
data.set(label, 8);
|
|
106
|
+
return {
|
|
107
|
+
programId: encodeBase58(bytes(p.programId, "programId")),
|
|
108
|
+
keys: [
|
|
109
|
+
k(p.creator, "creator", true, true),
|
|
110
|
+
k(p.namespace, "namespace", false, true),
|
|
111
|
+
k(p.namespaceGate, "namespaceGate", false, true),
|
|
112
|
+
],
|
|
113
|
+
data,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Append the gate accounts to a `buildRegisterUnderNamespaceV2Ix` result
|
|
118
|
+
* (which ends with the pricing PDA at `remaining_accounts[0]`): `[1]` the
|
|
119
|
+
* `["ns_gate", label]` PDA, `[2]` a gate-mint token account OWNED BY THE
|
|
120
|
+
* RECIPIENT (the instruction's `owner`, not the payer). Not needed when the
|
|
121
|
+
* payer is the creator or an authorised minter.
|
|
122
|
+
*/
|
|
123
|
+
export function appendNamespaceGateAccounts(ix, namespaceGate, ownerTokenAccount) {
|
|
124
|
+
return {
|
|
125
|
+
...ix,
|
|
126
|
+
keys: [...ix.keys, k(namespaceGate, "namespaceGate", false, false), k(ownerTokenAccount, "ownerTokenAccount", false, false)],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Client-side mirror of the program's gate test, for UI pre-checks (the
|
|
131
|
+
* program is the authority). `payer` decides the bypass (creator, or a
|
|
132
|
+
* minter the caller knows is authorised); otherwise the `recipient` must own
|
|
133
|
+
* `tokenAccount` (decoded base layout: `owner`, `mint`, `amount`) of the gate
|
|
134
|
+
* mint with `amount >= gate.minAmount`.
|
|
135
|
+
*/
|
|
136
|
+
export function namespaceGateAllows(p) {
|
|
137
|
+
const eq = (a, b, w) => encodeBase58(bytes(a, w)) === encodeBase58(bytes(b, w));
|
|
138
|
+
if (eq(p.payer, p.creator, "payer") || p.payerIsAuthorisedMinter === true)
|
|
139
|
+
return true;
|
|
140
|
+
const ta = p.tokenAccount;
|
|
141
|
+
if (!ta)
|
|
142
|
+
return false;
|
|
143
|
+
return eq(ta.mint, p.gate.mint, "mint") && eq(ta.owner, p.recipient, "recipient") && ta.amount >= p.gate.minAmount;
|
|
144
|
+
}
|
|
145
|
+
/** Decode a `NamespaceGate` (disc | version | bump | mint@10 | token_program@42 | min_amount@74 | updated_at@82 | label@90). */
|
|
146
|
+
export function decodeNamespaceGate(data) {
|
|
147
|
+
if (data.length < 94)
|
|
148
|
+
return null;
|
|
149
|
+
for (let i = 0; i < 8; i++)
|
|
150
|
+
if (data[i] !== NAMESPACE_GATE_ACCOUNT_DISCRIMINATOR[i])
|
|
151
|
+
return null;
|
|
152
|
+
const dv = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
153
|
+
const len = dv.getUint32(90, true);
|
|
154
|
+
if (len > 16 || data.length < 94 + len)
|
|
155
|
+
return null;
|
|
156
|
+
let label;
|
|
157
|
+
try {
|
|
158
|
+
label = new TextDecoder("utf-8", { fatal: true }).decode(data.subarray(94, 94 + len));
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
version: data[8],
|
|
165
|
+
bump: data[9],
|
|
166
|
+
mint: encodeBase58(data.subarray(10, 42)),
|
|
167
|
+
tokenProgram: encodeBase58(data.subarray(42, 74)),
|
|
168
|
+
minAmount: dv.getBigUint64(74, true),
|
|
169
|
+
updatedAt: dv.getBigInt64(82, true),
|
|
170
|
+
label,
|
|
171
|
+
};
|
|
172
|
+
}
|
package/dist/oidc.d.ts
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Sign in with X1ID" — a relying-party (RP) client for the X1ID OpenID Connect
|
|
3
|
+
* provider at `https://auth.x1id.io` (services/auth, OpenProject #8842).
|
|
4
|
+
*
|
|
5
|
+
* Authorization code flow with PKCE S256, `state` and `nonce`; the ID token is
|
|
6
|
+
* verified against the provider's JWKS (signature for the algorithms discovery
|
|
7
|
+
* advertises, `iss`, `aud`, `azp`, `exp`/`iat` with a small clock skew,
|
|
8
|
+
* `nonce`), then `/me` (UserInfo) is fetched and its `sub` checked against the
|
|
9
|
+
* ID token's.
|
|
10
|
+
*
|
|
11
|
+
* Runs in browsers and in Node 20+: Web Crypto + `fetch` only, no runtime
|
|
12
|
+
* dependency, no DOM access at import time. Browser SPAs use
|
|
13
|
+
* {@link beginX1idLogin} / {@link completeX1idLogin} (pending state kept in
|
|
14
|
+
* `sessionStorage`); servers call {@link X1idOidcClient.createAuthorization}
|
|
15
|
+
* and {@link X1idOidcClient.handleCallback} and keep the pending state in their
|
|
16
|
+
* own session.
|
|
17
|
+
*
|
|
18
|
+
* What a sign-in proves: the user controlled the wallet (`sub`) at sign-in. It
|
|
19
|
+
* is NOT proof of legal identity, personhood or KYC. Key accounts on `sub`
|
|
20
|
+
* (the wallet), never on the handle — handles move.
|
|
21
|
+
*/
|
|
22
|
+
export declare const X1ID_ISSUER = "https://auth.x1id.io";
|
|
23
|
+
export declare const X1ID_DEFAULT_SCOPE = "openid profile wallet";
|
|
24
|
+
/** ID-token algorithms this client can verify (the provider's ES256 default,
|
|
25
|
+
* EdDSA/Ed25519, and the opt-in RS256 chain). Never `none`, never HS*. */
|
|
26
|
+
export declare const X1ID_SUPPORTED_ID_TOKEN_ALGS: readonly ["ES256", "EdDSA", "RS256"];
|
|
27
|
+
export type X1idIdTokenAlg = (typeof X1ID_SUPPORTED_ID_TOKEN_ALGS)[number];
|
|
28
|
+
/** Default tolerance for `exp` / `iat` against the local clock, in seconds. */
|
|
29
|
+
export declare const X1ID_DEFAULT_CLOCK_SKEW_SECS = 60;
|
|
30
|
+
/** How long a pending login (state/nonce/verifier) stays usable. Matches the
|
|
31
|
+
* provider's interaction lifetime. */
|
|
32
|
+
export declare const X1ID_PENDING_TTL_MS: number;
|
|
33
|
+
/**
|
|
34
|
+
* Every failure is an `X1idAuthError`. `code` is either one of this client's
|
|
35
|
+
* own codes below, or the provider's OAuth `error` value verbatim
|
|
36
|
+
* (`access_denied`, `invalid_grant`, `login_required`, …).
|
|
37
|
+
*/
|
|
38
|
+
export type X1idAuthErrorCode = "invalid_config" | "discovery_failed" | "state_mismatch" | "iss_mismatch" | "missing_code" | "pending_expired" | "token_request_failed" | "invalid_token_response" | "invalid_id_token" | "userinfo_failed" | "no_crypto" | (string & {});
|
|
39
|
+
export declare class X1idAuthError extends Error {
|
|
40
|
+
readonly code: X1idAuthErrorCode;
|
|
41
|
+
/** The provider's `error_description`, when it sent one. */
|
|
42
|
+
readonly description: string | undefined;
|
|
43
|
+
constructor(code: X1idAuthErrorCode, message: string, description?: string);
|
|
44
|
+
}
|
|
45
|
+
export interface X1idOidcConfig {
|
|
46
|
+
/** Your client id from the X1ID client registry (`x1c_…`). */
|
|
47
|
+
readonly clientId: string;
|
|
48
|
+
/** Registered redirect URI (exact match). */
|
|
49
|
+
readonly redirectUri: string;
|
|
50
|
+
/** Default `https://auth.x1id.io`. https only (loopback http for local dev). */
|
|
51
|
+
readonly issuer?: string;
|
|
52
|
+
/** Default `openid profile wallet`. `openid` is always added. */
|
|
53
|
+
readonly scope?: string | readonly string[];
|
|
54
|
+
/** Confidential (server-side) clients only. NEVER put a secret in a browser
|
|
55
|
+
* bundle. Omit for public clients (SPA / native: PKCE, no secret). */
|
|
56
|
+
readonly clientSecret?: string;
|
|
57
|
+
/** How a confidential client authenticates at `/token`. Default
|
|
58
|
+
* `client_secret_basic` (the registry default). */
|
|
59
|
+
readonly tokenEndpointAuthMethod?: "client_secret_basic" | "client_secret_post";
|
|
60
|
+
/** Pin the ID-token algorithm (e.g. "RS256" if your client is registered
|
|
61
|
+
* for it). Default: any algorithm the provider advertises that this client
|
|
62
|
+
* supports. */
|
|
63
|
+
readonly idTokenSigningAlg?: X1idIdTokenAlg;
|
|
64
|
+
/** `exp`/`iat` tolerance in seconds. Default 60. */
|
|
65
|
+
readonly clockSkewSecs?: number;
|
|
66
|
+
/** Injected `fetch` (tests, proxies). Default `globalThis.fetch`. */
|
|
67
|
+
readonly fetch?: typeof fetch;
|
|
68
|
+
/** Injected Web Crypto (tests, old runtimes). Default `globalThis.crypto`. */
|
|
69
|
+
readonly crypto?: Crypto;
|
|
70
|
+
/** Injected clock in ms (tests). Default `Date.now`. */
|
|
71
|
+
readonly now?: () => number;
|
|
72
|
+
}
|
|
73
|
+
/** The subset of the provider's discovery document this client uses. */
|
|
74
|
+
export interface X1idDiscovery {
|
|
75
|
+
readonly issuer: string;
|
|
76
|
+
readonly authorization_endpoint: string;
|
|
77
|
+
readonly token_endpoint: string;
|
|
78
|
+
readonly jwks_uri: string;
|
|
79
|
+
readonly userinfo_endpoint?: string;
|
|
80
|
+
readonly end_session_endpoint?: string;
|
|
81
|
+
readonly revocation_endpoint?: string;
|
|
82
|
+
readonly id_token_signing_alg_values_supported?: readonly string[];
|
|
83
|
+
readonly code_challenge_methods_supported?: readonly string[];
|
|
84
|
+
readonly authorization_response_iss_parameter_supported?: boolean;
|
|
85
|
+
readonly [k: string]: unknown;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* What must survive the redirect: keep it server-side (session) or in
|
|
89
|
+
* `sessionStorage` (SPA, see {@link beginX1idLogin}). Never put it in the URL.
|
|
90
|
+
*/
|
|
91
|
+
export interface X1idPendingLogin {
|
|
92
|
+
readonly state: string;
|
|
93
|
+
readonly nonce: string;
|
|
94
|
+
readonly codeVerifier: string;
|
|
95
|
+
readonly redirectUri: string;
|
|
96
|
+
readonly issuer: string;
|
|
97
|
+
/** ms since epoch. */
|
|
98
|
+
readonly createdAt: number;
|
|
99
|
+
}
|
|
100
|
+
export interface X1idAuthorizationOptions {
|
|
101
|
+
/** e.g. "login" to force a fresh sign-in, "consent" (required by a PUBLIC
|
|
102
|
+
* client that asks for `offline_access`). */
|
|
103
|
+
readonly prompt?: string;
|
|
104
|
+
/** Override the configured scope for this request. */
|
|
105
|
+
readonly scope?: string | readonly string[];
|
|
106
|
+
/** `max_age` in seconds. */
|
|
107
|
+
readonly maxAge?: number;
|
|
108
|
+
/** `ui_locales`. */
|
|
109
|
+
readonly uiLocales?: string;
|
|
110
|
+
/** Any further authorization parameters. The protocol parameters this client
|
|
111
|
+
* owns (state, nonce, PKCE, client_id, redirect_uri, response_type) cannot
|
|
112
|
+
* be overridden. */
|
|
113
|
+
readonly extraParams?: Readonly<Record<string, string>>;
|
|
114
|
+
}
|
|
115
|
+
export interface X1idIdTokenClaims {
|
|
116
|
+
readonly iss: string;
|
|
117
|
+
/** The wallet address (base58). Your account key. */
|
|
118
|
+
readonly sub: string;
|
|
119
|
+
readonly aud: string | readonly string[];
|
|
120
|
+
readonly exp: number;
|
|
121
|
+
readonly iat: number;
|
|
122
|
+
readonly nonce?: string;
|
|
123
|
+
readonly azp?: string;
|
|
124
|
+
readonly auth_time?: number;
|
|
125
|
+
/** `["pop"]`: proof of possession of the wallet key at sign-in. */
|
|
126
|
+
readonly amr?: readonly string[];
|
|
127
|
+
readonly [k: string]: unknown;
|
|
128
|
+
}
|
|
129
|
+
/** `/me` claims, by scope (see services/auth/README.md "Scopes and claims"). */
|
|
130
|
+
export interface X1idUserInfo {
|
|
131
|
+
readonly sub: string;
|
|
132
|
+
/** `profile`: the wallet's primary @handle (no `@`), read live from chain. */
|
|
133
|
+
readonly x1id_handle?: string;
|
|
134
|
+
readonly preferred_username?: string;
|
|
135
|
+
/** `profile`: the handle's https avatar. */
|
|
136
|
+
readonly picture?: string;
|
|
137
|
+
/** `wallet` */
|
|
138
|
+
readonly wallet_address?: string;
|
|
139
|
+
readonly wallet_chain?: string;
|
|
140
|
+
readonly [k: string]: unknown;
|
|
141
|
+
}
|
|
142
|
+
export interface X1idTokens {
|
|
143
|
+
readonly accessToken: string;
|
|
144
|
+
readonly tokenType: string;
|
|
145
|
+
/** ms since epoch, from `expires_in`. */
|
|
146
|
+
readonly expiresAt: number | undefined;
|
|
147
|
+
readonly refreshToken: string | undefined;
|
|
148
|
+
readonly scope: string | undefined;
|
|
149
|
+
readonly idToken: string | undefined;
|
|
150
|
+
readonly idTokenClaims: X1idIdTokenClaims | undefined;
|
|
151
|
+
}
|
|
152
|
+
export interface X1idLoginResult extends X1idTokens {
|
|
153
|
+
/** The wallet address that signed in (the verified ID token's `sub`). */
|
|
154
|
+
readonly sub: string;
|
|
155
|
+
readonly idToken: string;
|
|
156
|
+
readonly idTokenClaims: X1idIdTokenClaims;
|
|
157
|
+
/** Present unless `{ userInfo: false }` was passed. */
|
|
158
|
+
readonly userInfo: X1idUserInfo | undefined;
|
|
159
|
+
}
|
|
160
|
+
export interface X1idVerifyIdTokenOptions {
|
|
161
|
+
readonly issuer: string;
|
|
162
|
+
readonly clientId: string;
|
|
163
|
+
/** The nonce sent in the authorization request. Required for a login; omit
|
|
164
|
+
* only for an ID token from a refresh. */
|
|
165
|
+
readonly nonce?: string;
|
|
166
|
+
/** Allowed `alg` values. */
|
|
167
|
+
readonly algorithms: readonly string[];
|
|
168
|
+
/** Resolve the verification key for a header. */
|
|
169
|
+
readonly getKey: (header: JwtHeader) => Promise<JsonWebKey | null>;
|
|
170
|
+
readonly clockSkewSecs?: number;
|
|
171
|
+
readonly now?: () => number;
|
|
172
|
+
readonly crypto?: Crypto;
|
|
173
|
+
}
|
|
174
|
+
export interface JwtHeader {
|
|
175
|
+
readonly alg: string;
|
|
176
|
+
readonly kid?: string;
|
|
177
|
+
readonly typ?: string;
|
|
178
|
+
readonly [k: string]: unknown;
|
|
179
|
+
}
|
|
180
|
+
/** `bytes` random bytes, base64url (32 bytes → 43 chars, 256 bits). */
|
|
181
|
+
export declare function randomUrlSafe(bytes?: number, crypto?: Crypto): string;
|
|
182
|
+
/** A fresh RFC 7636 code verifier: 32 random bytes → 43 base64url chars. */
|
|
183
|
+
export declare function generateCodeVerifier(crypto?: Crypto): string;
|
|
184
|
+
/** RFC 7636 S256: BASE64URL(SHA-256(ASCII(verifier))). */
|
|
185
|
+
export declare function codeChallengeS256(verifier: string, crypto?: Crypto): Promise<string>;
|
|
186
|
+
/** Decode a JWT without verifying it (for logging / inspection only). */
|
|
187
|
+
export declare function decodeJwtUnverified(jwt: string): {
|
|
188
|
+
header: JwtHeader;
|
|
189
|
+
payload: Record<string, unknown>;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Verify an X1ID ID token: signature (only `algorithms`, never `none`/HS*),
|
|
193
|
+
* `iss` exact match, `aud` contains the client, `azp` (if present, or if
|
|
194
|
+
* several audiences) equals the client, `exp` in the future and `iat` not in
|
|
195
|
+
* the future (± `clockSkewSecs`), `nonce` equal to the one sent, `sub` a
|
|
196
|
+
* non-empty string. Returns the claims; throws `X1idAuthError("invalid_id_token")`.
|
|
197
|
+
*/
|
|
198
|
+
export declare function verifyX1idIdToken(jwt: string, o: X1idVerifyIdTokenOptions): Promise<X1idIdTokenClaims>;
|
|
199
|
+
export interface X1idOidcClient {
|
|
200
|
+
readonly issuer: string;
|
|
201
|
+
readonly clientId: string;
|
|
202
|
+
readonly redirectUri: string;
|
|
203
|
+
/** Fetch (once, then cached) and validate the discovery document. */
|
|
204
|
+
discover(): Promise<X1idDiscovery>;
|
|
205
|
+
/** Build the authorization URL + the pending state to keep until the callback. */
|
|
206
|
+
createAuthorization(opts?: X1idAuthorizationOptions): Promise<{
|
|
207
|
+
url: string;
|
|
208
|
+
pending: X1idPendingLogin;
|
|
209
|
+
}>;
|
|
210
|
+
/**
|
|
211
|
+
* Handle the redirect back: check `state`, the RFC 9207 `iss` parameter and
|
|
212
|
+
* any `error`, exchange the code (PKCE), verify the ID token, fetch `/me`.
|
|
213
|
+
* Call it once per pending login (codes are single-use).
|
|
214
|
+
*/
|
|
215
|
+
handleCallback(callbackUrl: string | URL, pending: X1idPendingLogin, opts?: {
|
|
216
|
+
userInfo?: boolean;
|
|
217
|
+
}): Promise<X1idLoginResult>;
|
|
218
|
+
/** Exchange a code directly (when you parsed the callback yourself). */
|
|
219
|
+
exchangeCode(code: string, pending: X1idPendingLogin): Promise<X1idTokens & {
|
|
220
|
+
idToken: string;
|
|
221
|
+
idTokenClaims: X1idIdTokenClaims;
|
|
222
|
+
}>;
|
|
223
|
+
/** Verify an ID token from this provider for this client. */
|
|
224
|
+
verifyIdToken(idToken: string, opts?: {
|
|
225
|
+
nonce?: string;
|
|
226
|
+
}): Promise<X1idIdTokenClaims>;
|
|
227
|
+
/** GET `/me` with an access token. Pass `expectedSub` (the ID token's sub) —
|
|
228
|
+
* OIDC Core §5.3.2 requires the two to match. */
|
|
229
|
+
fetchUserInfo(accessToken: string, expectedSub?: string): Promise<X1idUserInfo>;
|
|
230
|
+
/** Refresh (rotating). An ID token in the response is verified; pass the
|
|
231
|
+
* account's `sub` so a different subject is rejected. */
|
|
232
|
+
refresh(refreshToken: string, opts?: {
|
|
233
|
+
expectedSub?: string;
|
|
234
|
+
}): Promise<X1idTokens>;
|
|
235
|
+
/** RFC 7009 revocation of an access or refresh token. */
|
|
236
|
+
revoke(token: string, hint?: "access_token" | "refresh_token"): Promise<void>;
|
|
237
|
+
/** RP-initiated logout URL at auth.x1id.io. */
|
|
238
|
+
logoutUrl(opts?: {
|
|
239
|
+
idTokenHint?: string;
|
|
240
|
+
postLogoutRedirectUri?: string;
|
|
241
|
+
state?: string;
|
|
242
|
+
}): Promise<string>;
|
|
243
|
+
}
|
|
244
|
+
/** Create an RP client. Nothing is fetched until first use. */
|
|
245
|
+
export declare function createX1idOidcClient(config: X1idOidcConfig): X1idOidcClient;
|
|
246
|
+
/** Minimal Storage (sessionStorage, or a test double). */
|
|
247
|
+
export interface X1idStorage {
|
|
248
|
+
getItem(key: string): string | null;
|
|
249
|
+
setItem(key: string, value: string): void;
|
|
250
|
+
removeItem(key: string): void;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Start "Sign in with X1ID" in a browser: stores the pending login in
|
|
254
|
+
* `sessionStorage` (keyed by state, so two tabs don't collide) and navigates
|
|
255
|
+
* to auth.x1id.io. Pass `navigate` to handle the redirect yourself.
|
|
256
|
+
*/
|
|
257
|
+
export declare function beginX1idLogin(client: X1idOidcClient, opts?: X1idAuthorizationOptions & {
|
|
258
|
+
storage?: X1idStorage;
|
|
259
|
+
navigate?: (url: string) => void;
|
|
260
|
+
}): Promise<string>;
|
|
261
|
+
/**
|
|
262
|
+
* Finish the login on your redirect page: looks up (and removes — single use)
|
|
263
|
+
* the pending login for the callback's `state`, then runs
|
|
264
|
+
* {@link X1idOidcClient.handleCallback}. By default strips `code`/`state` from
|
|
265
|
+
* the address bar afterwards (`history.replaceState`).
|
|
266
|
+
*/
|
|
267
|
+
export declare function completeX1idLogin(client: X1idOidcClient, opts?: {
|
|
268
|
+
url?: string | URL;
|
|
269
|
+
storage?: X1idStorage;
|
|
270
|
+
userInfo?: boolean;
|
|
271
|
+
cleanUrl?: boolean;
|
|
272
|
+
}): Promise<X1idLoginResult>;
|