@agentlogin/issuer 0.0.0-stage → 0.2.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.
Files changed (58) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +237 -2
  3. package/dist/agents.d.ts +40 -0
  4. package/dist/agents.js +257 -0
  5. package/dist/agents.js.map +1 -0
  6. package/dist/app.d.ts +3 -0
  7. package/dist/app.js +652 -0
  8. package/dist/app.js.map +1 -0
  9. package/dist/clients.d.ts +20 -0
  10. package/dist/clients.js +272 -0
  11. package/dist/clients.js.map +1 -0
  12. package/dist/constants.d.ts +30 -0
  13. package/dist/constants.js +32 -0
  14. package/dist/constants.js.map +1 -0
  15. package/dist/context.d.ts +37 -0
  16. package/dist/context.js +2 -0
  17. package/dist/context.js.map +1 -0
  18. package/dist/errors.d.ts +10 -0
  19. package/dist/errors.js +17 -0
  20. package/dist/errors.js.map +1 -0
  21. package/dist/index.d.ts +45 -0
  22. package/dist/index.js +107 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/owner-address.d.ts +2 -0
  25. package/dist/owner-address.js +24 -0
  26. package/dist/owner-address.js.map +1 -0
  27. package/dist/safefetch.d.ts +38 -0
  28. package/dist/safefetch.js +164 -0
  29. package/dist/safefetch.js.map +1 -0
  30. package/dist/server.d.ts +1 -0
  31. package/dist/server.js +89 -0
  32. package/dist/server.js.map +1 -0
  33. package/dist/signing-configuration.d.ts +14 -0
  34. package/dist/signing-configuration.js +35 -0
  35. package/dist/signing-configuration.js.map +1 -0
  36. package/dist/signing-state.d.ts +22 -0
  37. package/dist/signing-state.js +14 -0
  38. package/dist/signing-state.js.map +1 -0
  39. package/dist/sqlite-store.d.ts +24 -0
  40. package/dist/sqlite-store.js +81 -0
  41. package/dist/sqlite-store.js.map +1 -0
  42. package/dist/state.d.ts +30 -0
  43. package/dist/state.js +77 -0
  44. package/dist/state.js.map +1 -0
  45. package/dist/store.d.ts +693 -0
  46. package/dist/store.js +883 -0
  47. package/dist/store.js.map +1 -0
  48. package/dist/tokens.d.ts +37 -0
  49. package/dist/tokens.js +167 -0
  50. package/dist/tokens.js.map +1 -0
  51. package/dist/transactions.d.ts +90 -0
  52. package/dist/transactions.js +449 -0
  53. package/dist/transactions.js.map +1 -0
  54. package/dist/util.d.ts +29 -0
  55. package/dist/util.js +113 -0
  56. package/dist/util.js.map +1 -0
  57. package/package.json +37 -4
  58. package/static/wait.js +42 -0
@@ -0,0 +1,693 @@
1
+ import type { Scope } from './constants.js';
2
+ import { type PublishedSigningKey, type SigningKeyPublication } from './signing-state.js';
3
+ export type OwnerVerifiedVia = 'email' | 'oidc' | 'domain' | 'passkey';
4
+ export interface Owner {
5
+ id: string;
6
+ ownerSub: string;
7
+ email: string;
8
+ emailVerified: boolean;
9
+ name?: string;
10
+ verifiedVia: OwnerVerifiedVia;
11
+ createdAt: number;
12
+ }
13
+ export interface Agent {
14
+ handle: string;
15
+ local: string;
16
+ domain: string;
17
+ sub: string;
18
+ ownerId: string;
19
+ name?: string;
20
+ preferredUsername: string;
21
+ enabled: boolean;
22
+ /** Monotonic invalidation fence: disabling and re-enabling never revives old authorizations. */
23
+ authorizationVersion: number;
24
+ createdAt: number;
25
+ }
26
+ /**
27
+ * How the session that issued an Owner Credential authenticated the Owner (Section 6.6): `passkey`, `oidc`, `domain`,
28
+ * `email_factor` (email plus a factor the Agent cannot read) qualify for Owner approval and `share_owner`;
29
+ * `email_only` (a one-time code or link alone) does not.
30
+ */
31
+ export type SessionAuth = 'passkey' | 'oidc' | 'domain' | 'email_factor' | 'email_only';
32
+ /** Owner Credential or Delegated Credential (Section 1.2), stored hashed. */
33
+ export interface Credential {
34
+ hash: string;
35
+ ownerId: string;
36
+ kind: 'owner' | 'delegated';
37
+ handle?: string;
38
+ sessionAuth?: SessionAuth;
39
+ createdAt: number;
40
+ }
41
+ /** One-time code of the Owner email sign-in (Section 7.5 `email`), stored hashed; one outstanding code per Owner. */
42
+ export interface OwnerCode {
43
+ ownerId: string;
44
+ hash: string;
45
+ expiresAt: number;
46
+ }
47
+ export type KeyStatus = 'pending' | 'active' | 'revoked' | 'expired';
48
+ export interface P256Jwk {
49
+ kty: 'EC';
50
+ crv: 'P-256';
51
+ x: string;
52
+ y: string;
53
+ }
54
+ export interface AgentKey {
55
+ kid: string;
56
+ handle: string;
57
+ /** Immutable Agent identity that authorized this enrollment. */
58
+ agentSub: string;
59
+ /**
60
+ * Identifies this enrollment of the key. A transaction, code or Access Token is bound to the enrollment that
61
+ * asserted it: enrolling the same public key again after it expired starts a new enrollment and revives nothing.
62
+ */
63
+ enrollment: string;
64
+ status: KeyStatus;
65
+ jwk: P256Jwk;
66
+ permissions: {
67
+ share_owner: boolean;
68
+ };
69
+ name?: string;
70
+ createdAt: number;
71
+ expiresAt: number;
72
+ challenge?: {
73
+ value: string;
74
+ expiresAt: number;
75
+ };
76
+ revokedAt?: number;
77
+ }
78
+ export type AuthMethod = 'none' | 'client_secret_basic' | 'client_secret_post';
79
+ export interface Client {
80
+ clientId: string;
81
+ type: 'open' | 'registered';
82
+ redirectUris: string[];
83
+ clientName?: string;
84
+ logoUri?: string;
85
+ tosUri?: string;
86
+ policyUri?: string;
87
+ initiateLoginUri?: string;
88
+ maxSignupsPerOwner?: number;
89
+ requireReview: boolean;
90
+ /** Section 4.4: the Relay registering as a client; every redirect URI is on its host. */
91
+ relayIssuer?: string;
92
+ /** Section 4.4: the Relay's `op_policy_uri`, from its metadata as of registration (shown by the approval page, Section 6.6). */
93
+ relayPolicyUri?: string;
94
+ tokenEndpointAuthMethod: AuthMethod;
95
+ clientSecretHash?: string;
96
+ fetchedAt?: number;
97
+ cacheUntil?: number;
98
+ }
99
+ /** Public presentation accepted for one transaction; never includes authentication credentials or live policy. */
100
+ export type ClientPresentation = Pick<Client, 'type' | 'clientName' | 'logoUri' | 'tosUri' | 'policyUri' | 'relayIssuer' | 'relayPolicyUri'>;
101
+ export type TxStatus = 'awaiting_agent' | 'awaiting_owner' | 'awaiting_consent' | 'ready' | 'completed' | 'denied' | 'expired';
102
+ export declare const TERMINAL: ReadonlySet<TxStatus>;
103
+ export type DenyReason = 'cancelled' | 'access_denied' | 'key_revoked' | 'owner_declined' | 'consent_required' | 'interaction_required' | 'signup_limit_reached' | 'login_required';
104
+ export type ConsentReason = 'first_visit' | 'material_change' | 'expired' | 'prompt_consent' | 'policy';
105
+ export interface Disclosure {
106
+ scope: Scope;
107
+ title: string;
108
+ description: string;
109
+ }
110
+ export interface Transaction {
111
+ id: string;
112
+ /** Incremented by every accepted change; a change is accepted only against the version it was computed from. */
113
+ version: number;
114
+ clientId: string;
115
+ /** Immutable displayed metadata. Missing only on legacy records, which issuer startup cancels without backfilling. */
116
+ clientPresentation?: ClientPresentation;
117
+ redirectUri: string;
118
+ scopes: Scope[];
119
+ state: string;
120
+ nonce: string;
121
+ codeChallenge: string;
122
+ prompt?: 'consent' | 'none';
123
+ /** Immutable requested authentication age in seconds; zero requires this transaction's fresh assertion. */
124
+ maxAge?: number;
125
+ loginHint?: string;
126
+ disclosure: Disclosure[];
127
+ requester?: {
128
+ user_agent?: string;
129
+ network?: string;
130
+ };
131
+ status: TxStatus;
132
+ reason?: DenyReason;
133
+ consentReason?: ConsentReason;
134
+ challenge?: string;
135
+ createdAt: number;
136
+ expiresAt: number;
137
+ terminalAt?: number;
138
+ handle?: string;
139
+ kid?: string;
140
+ /** The enrollment of `kid` that asserted (see AgentKey.enrollment). */
141
+ keyEnrollment?: string;
142
+ agentSub?: string;
143
+ agentVersion?: number;
144
+ accountVersion?: number;
145
+ ownerId?: string;
146
+ assertedAt?: number;
147
+ acceptDisclosure: boolean;
148
+ deliverToSession: boolean;
149
+ ownerApprovalOutstanding: boolean;
150
+ reviewOutstanding: boolean;
151
+ /** The sign-in proceeds on the strength of an existing Consent Record; its last use is renewed when the transaction becomes ready. */
152
+ consentRelied?: boolean;
153
+ passedCap: boolean;
154
+ transactionTokenHash?: string;
155
+ sessionSecretHash?: string;
156
+ /** Chosen at creation; released to a requester only as Section 6.5 allows. */
157
+ continueSecret: string;
158
+ codeIssued: boolean;
159
+ }
160
+ export interface Code {
161
+ hash: string;
162
+ txnId: string;
163
+ clientId: string;
164
+ redirectUri: string;
165
+ codeChallenge: string;
166
+ nonce: string;
167
+ scopes: Scope[];
168
+ handle: string;
169
+ kid: string;
170
+ keyEnrollment: string;
171
+ agentSub: string;
172
+ agentVersion: number;
173
+ accountVersion: number;
174
+ authTime: number;
175
+ /** Bound to the original Authentication Request, never supplied by the redeeming caller. */
176
+ maxAge?: number;
177
+ expiresAt: number;
178
+ used: boolean;
179
+ voided: boolean;
180
+ /** Presented again after it was used (Section 5.4): the tokens issued for it are revoked, whichever request notices. */
181
+ replayed?: boolean;
182
+ }
183
+ export interface AccessToken {
184
+ hash: string;
185
+ txnId: string;
186
+ clientId: string;
187
+ handle: string;
188
+ kid: string;
189
+ keyEnrollment: string;
190
+ agentSub: string;
191
+ agentVersion: number;
192
+ accountVersion: number;
193
+ scopes: Scope[];
194
+ /** The store's clock when issuance was accepted. Older persisted snapshots may omit this field. */
195
+ issuedAt?: number;
196
+ expiresAt: number;
197
+ revoked: boolean;
198
+ }
199
+ export interface Consent {
200
+ handle: string;
201
+ clientId: string;
202
+ scopes: Scope[];
203
+ redirectHost: string;
204
+ clientName?: string;
205
+ tosUri?: string;
206
+ policyUri?: string;
207
+ createdAt: number;
208
+ lastUsedAt: number;
209
+ }
210
+ type ConsentMetadata = Pick<Consent, 'redirectHost' | 'clientName' | 'tosUri' | 'policyUri'>;
211
+ /** Section 6.4: scopes may accumulate only under the same material disclosure terms. */
212
+ export declare function sameConsentMetadata(a: ConsentMetadata, b: ConsentMetadata): boolean;
213
+ /** The material consent preconditions, shared by gate planning and atomic acceptance. */
214
+ export declare function consentRecordReason(record: Consent | undefined, expected: ConsentMetadata & Pick<Consent, 'scopes'>, expireBefore?: number): 'first_visit' | 'material_change' | 'expired' | undefined;
215
+ export interface Account {
216
+ handle: string;
217
+ clientId: string;
218
+ status: 'enabled' | 'disabled';
219
+ authorizationVersion: number;
220
+ firstSignedInAt: number;
221
+ lastSignedInAt: number;
222
+ signInCount: number;
223
+ }
224
+ /** Selects transactions, codes or Access Tokens by what they are bound to. Absent members match everything. */
225
+ export interface SubjectFilter {
226
+ kid?: string;
227
+ keyEnrollment?: string;
228
+ handle?: string;
229
+ clientId?: string;
230
+ ownerId?: string;
231
+ txnId?: string;
232
+ }
233
+ export type InsertKeyResult = 'unauthorized' | 'ok' | 'enrolled' | 'other_handle' | 'revoked';
234
+ export type RedeemResult = 'ok' | 'used' | 'void' | 'expired' | 'unknown';
235
+ /** Section 5.1: equality is allowed; zero requests fresh authentication, not instantaneous delivery. */
236
+ export declare function isAuthenticationTooOld(maxAge: number | undefined, authTime: number | undefined, now: number): boolean;
237
+ export interface ManagementAuthorization extends Credential {
238
+ agentSub: string;
239
+ }
240
+ export interface TransitionEffects {
241
+ now: number;
242
+ /** Already verified signed assertion window, rechecked by the store at its atomic acceptance point. */
243
+ assertionWindow?: {
244
+ notBefore: number;
245
+ expiresAt: number;
246
+ };
247
+ /** Review bearer, checked against current authority before version or terminal checks. */
248
+ transactionTokenHash?: string;
249
+ /** Key permission used to plan first-assertion Owner approval; mismatch returns a conflict without effects. */
250
+ expectedShareOwner?: boolean;
251
+ maxSignups?: number;
252
+ consent?: Omit<Consent, 'createdAt' | 'lastUsedAt'>;
253
+ /** The disclosure whose existing consent a transition relies on; revalidated atomically before acceptance. */
254
+ consentContext?: Omit<Consent, 'createdAt' | 'lastUsedAt'>;
255
+ touchConsent?: boolean;
256
+ expireConsentBefore?: number;
257
+ }
258
+ export declare class AssertionWindowError extends Error {
259
+ readonly code = "ASSERTION_WINDOW";
260
+ constructor();
261
+ }
262
+ /** Stable across separate copies of this package loaded by an external adapter. */
263
+ export declare function isAssertionWindowError(error: unknown): error is AssertionWindowError;
264
+ /** Step 2 authority is rechecked atomically before consuming a first assertion. */
265
+ export declare class AssertionSubjectError extends Error {
266
+ readonly error: 'unknown_key' | 'key_revoked';
267
+ readonly code = "ASSERTION_SUBJECT";
268
+ constructor(error: 'unknown_key' | 'key_revoked');
269
+ }
270
+ export declare function isAssertionSubjectError(error: unknown): error is AssertionSubjectError;
271
+ /** First assertions cannot take effect at or after the transaction deadline. */
272
+ export declare class TransactionExpiredError extends Error {
273
+ readonly code = "TRANSACTION_EXPIRED";
274
+ constructor();
275
+ }
276
+ export declare function isTransactionExpiredError(error: unknown): error is TransactionExpiredError;
277
+ /** A previously identified transaction bearer no longer has authority. */
278
+ export declare class TransactionTokenError extends Error {
279
+ readonly code = "TRANSACTION_TOKEN";
280
+ constructor();
281
+ }
282
+ export declare function isTransactionTokenError(error: unknown): error is TransactionTokenError;
283
+ /** The public key is already active for another Agent; pending enrollments confer no exclusivity. */
284
+ export declare class KeyActivationConflictError extends Error {
285
+ readonly code = "KEY_ACTIVATION_CONFLICT";
286
+ constructor();
287
+ }
288
+ /** Stable across separate copies of this package loaded by an external adapter. */
289
+ export declare function isKeyActivationConflictError(error: unknown): error is KeyActivationConflictError;
290
+ export interface Store {
291
+ /** Authoritative clock for protocol timestamps; local adapters use the supplied application/test clock. */
292
+ currentTime(fallbackNow: number): Promise<number>;
293
+ /** Shared issuer-key lifecycle. Private keys never enter Store. First initialization may select bootstrapKid. */
294
+ publishSigningKeys(keys: SigningKeyPublication[], bootstrapKid: string, now: number, cacheLifetime: number): Promise<void>;
295
+ listSigningKeys(): Promise<PublishedSigningKey[]>;
296
+ activateSigningKey(kid: string, now: number): Promise<boolean>;
297
+ removeSigningKey(kid: string, compromised: boolean, now: number): Promise<boolean>;
298
+ /** Reserves maximum expiration before signing; callers recheck compromise before delivery. */
299
+ reserveSigningKey(expiresAt: number, now: number): Promise<PublishedSigningKey | undefined>;
300
+ putOwner(o: Owner, now?: number): Promise<void>;
301
+ /** ATOMIC identity deletion; retained address linkage expires at this fixed deadline. */
302
+ deleteOwner(id: string, ownerSub: string, now: number, retentionSeconds: number): Promise<boolean>;
303
+ getOwner(id: string): Promise<Owner | undefined>;
304
+ /** The Owner with this address (parsed local-part exact, domain case-insensitive); the most recently created when several share it. */
305
+ getOwnerByEmail(email: string): Promise<Owner | undefined>;
306
+ putOwnerCode(c: OwnerCode): Promise<void>;
307
+ /** ATOMIC. Returns and deletes the Owner's outstanding code. */
308
+ takeOwnerCode(ownerId: string): Promise<OwnerCode | undefined>;
309
+ /** ATOMIC insert-if-absent; an existing handle is never overwritten. */
310
+ putAgent(a: Agent): Promise<boolean>;
311
+ getAgent(handle: string): Promise<Agent | undefined>;
312
+ /** ATOMIC. Requires this immutable Agent identity; disabling increments its fence and invalidates all dependent state. */
313
+ setAgentEnabled(handle: string, enabled: boolean, agentSub: string, now: number): Promise<Agent | undefined>;
314
+ /** ATOMIC. Delete only this identity, with all keys, accounts, consents and delegated credentials; retain signup counts. */
315
+ deleteAgent(handle: string, agentSub: string, now: number): Promise<boolean>;
316
+ listAgentsByOwner(ownerId: string): Promise<Agent[]>;
317
+ putCredential(c: Credential): Promise<void>;
318
+ getCredential(hash: string): Promise<Credential | undefined>;
319
+ listCredentials(filter: {
320
+ ownerId?: string;
321
+ handle?: string;
322
+ kind?: 'owner' | 'delegated';
323
+ }): Promise<Credential[]>;
324
+ deleteCredential(hash: string): Promise<void>;
325
+ /** Read the credential and current Agent in one snapshot; never derive a new identity from a stale credential. */
326
+ resolveManagement(hash: string, handle: string): Promise<ManagementAuthorization | 'invalid_token' | 'not_found'>;
327
+ /** Protected reads validate the supplied authorization and return resources from the same snapshot. */
328
+ readManagementKeys(handle: string, authorization: ManagementAuthorization, kid?: string): Promise<AgentKey[] | undefined>;
329
+ readManagementAccounts(handle: string, authorization: ManagementAuthorization): Promise<Account[] | undefined>;
330
+ /** Lookup is scoped to the current Agent at handle. Legacy unscoped lookup returns the active or sole record; ambiguous pending records return undefined. */
331
+ getKey(kid: string, handle?: string): Promise<AgentKey | undefined>;
332
+ listKeys(handle: string): Promise<AgentKey[]>;
333
+ /**
334
+ * ATOMIC. Stores a `pending` key unless the public key was revoked for this handle (`revoked`) or is enrolled
335
+ * now, pending with an unexpired challenge or active and unexpired, for this Agent (`enrolled`). Other Agents'
336
+ * pending or active records do not prevent insertion. A dead record of this Agent's same key is replaced.
337
+ * The Agent sub, Owner and credential must still match the authorization snapshot, otherwise `unauthorized`;
338
+ * check and insert are one transaction.
339
+ */
340
+ insertKey(k: AgentKey, now: number, authorization: ManagementAuthorization): Promise<InsertKeyResult>;
341
+ /**
342
+ * ATOMIC. `pending` becomes `active`, only if this enrollment is still pending and `challenge` is its unconsumed,
343
+ * unexpired challenge and the verified proof window still holds. False otherwise, or AssertionWindowError when
344
+ * the signed proof expired: in particular a revocation while the proof was verified cannot be overwritten.
345
+ * Exclusivity is claimed here: throws KeyActivationConflictError if another Agent has this key active and unexpired.
346
+ */
347
+ activateKey(kid: string, enrollment: string, challenge: string, now: number, assertionWindow?: {
348
+ notBefore: number;
349
+ expiresAt: number;
350
+ }): Promise<boolean>;
351
+ /**
352
+ * ATOMIC. A `pending`, `active` or `expired` key becomes `revoked` and is remembered as revoked for its handle. Returns the
353
+ * key when this call revoked it (or an authorized repeat finds it already revoked), otherwise nothing. Requires the same enrollment and
354
+ * invalidates its transactions, codes and tokens in the same atomic operation. Protocol callers supply authorization,
355
+ * which is revalidated with the enrollment; omission is reserved for trusted internal lifecycle operations.
356
+ */
357
+ revokeKey(kid: string, enrollment: string, now: number, authorization?: ManagementAuthorization): Promise<AgentKey | undefined>;
358
+ /** ATOMIC. Revoke pending/active keys and, for an Owner request, delegated credentials of the authorized Agent identity. */
359
+ revokeAllKeys(handle: string, authorization: ManagementAuthorization, now: number): Promise<boolean>;
360
+ /** ATOMIC. Changes this enrollment, validating authorization and permission when supplied by a protocol caller. */
361
+ patchKey(kid: string, enrollment: string, patch: {
362
+ name?: string;
363
+ shareOwner?: boolean;
364
+ }, authorization?: ManagementAuthorization): Promise<AgentKey | undefined>;
365
+ /** Deletes the selected record, requiring enrollment when supplied. With pendingExpiredAt, deletes only a
366
+ * still-pending challenge expired by that time; database adapters recheck their authoritative clock. */
367
+ deleteKey(kid: string, enrollment?: string, pendingExpiredAt?: number): Promise<void>;
368
+ wasRevoked(handle: string, kid: string): Promise<boolean>;
369
+ putClient(c: Client): Promise<void>;
370
+ getClient(clientId: string): Promise<Client | undefined>;
371
+ insertTransaction(t: Transaction): Promise<void>;
372
+ getTransaction(id: string): Promise<Transaction | undefined>;
373
+ /** Read-only effective state, including lifecycle expiry and terminal retention. Never persists a transition. */
374
+ readTransaction(id: string, options: {
375
+ now: number;
376
+ retainFor: number;
377
+ }): Promise<Transaction | undefined>;
378
+ /**
379
+ * ATOMIC. Compare-and-swap against the open transaction version; return undefined on conflict. Validate subject
380
+ * identity/lifecycle and expiry, admit the signup cap on the FIRST assertion, and commit the transaction,
381
+ * reservation, and consent effects together. Existing consent relied on must still cover the supplied context;
382
+ * otherwise return a review state (or denial for prompt=none). Return the actual state (which may be denied/expired). Completion
383
+ * must use completeTransaction. A losing caller never creates or releases a reservation. A first assertion whose
384
+ * original transaction expired throws TransactionExpiredError without any write, before assertion-window checks.
385
+ * First assertions with invalid key/Agent authority throw AssertionSubjectError without consuming the challenge;
386
+ * Account denial remains an accepted gate. A changed expectedShareOwner returns a conflict with no effects,
387
+ * so the caller can recompute gates using the same enrollment's current permission. A supplied transactionTokenHash is checked before version/terminal checks and throws TransactionTokenError
388
+ * if explicit revocation removed its authority. The original maxAge is immutable; only first assertion acceptance
389
+ * sets assertedAt, using the store's fresh clock. Subsequent transitions cannot refresh that time.
390
+ */
391
+ updateTransaction(t: Transaction, expectedVersion: number, effects: TransitionEffects): Promise<Transaction | undefined>;
392
+ /** Transactions whose stored status is not terminal (an expired one may still be listed; callers look at `expiresAt`). */
393
+ listOpenTransactions(filter: SubjectFilter): Promise<Transaction[]>;
394
+ /** Upgrade only: at most limit (1..256) nonterminal records lacking a pinned presentation, from a filtered index. */
395
+ listLegacyTransactions(limit: number): Promise<Transaction[]>;
396
+ /** ATOMIC. Validate subject, reservation, expiry and maxAge; complete, insert code, count Account and release
397
+ * reservation together. Stale authentication ends denied/login_required without code, Account or count changes. */
398
+ completeTransaction(t: Transaction, expectedVersion: number, code: Code, now: number, transactionTokenHash?: string): Promise<Transaction | undefined>;
399
+ getCode(hash: string): Promise<Code | undefined>;
400
+ /**
401
+ * ATOMIC. Marks the code used if it is neither used, void nor expired (including maxAge); otherwise says which. One redemption wins.
402
+ * A code that was used already is also marked `replayed`, and its Access Tokens are revoked in the same operation.
403
+ */
404
+ redeemCode(hash: string, now: number): Promise<RedeemResult>;
405
+ /** ATOMIC per code. Voids every matching code that has not been used. */
406
+ voidCodes(filter: SubjectFilter): Promise<void>;
407
+ /** ATOMIC. Issue only for a redeemed, unreplayed code, a current subject lifecycle and its stored maxAge. */
408
+ insertAccessToken(t: AccessToken, now: number): Promise<boolean>;
409
+ getAccessToken(hash: string): Promise<AccessToken | undefined>;
410
+ /** ATOMIC per token. Revokes every matching Access Token. */
411
+ revokeAccessTokens(filter: SubjectFilter): Promise<void>;
412
+ getConsent(handle: string, clientId: string): Promise<Consent | undefined>;
413
+ listConsents(handle: string): Promise<Consent[]>;
414
+ deleteConsent(handle: string, clientId: string, authorization: ManagementAuthorization): Promise<boolean>;
415
+ getAccount(handle: string, clientId: string): Promise<Account | undefined>;
416
+ listAccounts(handle: string): Promise<Account[]>;
417
+ /** ATOMIC. Authorize against the same Agent identity; disabling increments the Account fence and invalidates dependents. */
418
+ setAccountStatus(handle: string, clientId: string, status: 'enabled' | 'disabled', authorization: ManagementAuthorization, now: number): Promise<Account | undefined>;
419
+ deleteAccount(handle: string, clientId: string): Promise<void>;
420
+ /** Number of Accounts ever created for (Owner, client), including disabled and deleted ones. */
421
+ signupsEver(ownerId: string, clientId: string): Promise<number>;
422
+ }
423
+ /** Public operations only: internal reservation/count helpers cannot be called outside their atomic transition. */
424
+ export declare const STORE_METHODS: readonly (keyof Store)[];
425
+ /** Reads never rewrite state, including polling an effectively expired transaction. */
426
+ export declare const READ_METHODS: ReadonlySet<keyof Store>;
427
+ /**
428
+ * The in-memory store. Every method body runs without awaiting anything, so each is atomic within the process; it
429
+ * stores and returns copies, so nothing a caller holds changes behind its back or changes the store by itself.
430
+ */
431
+ export type SyncStore = {
432
+ [K in keyof Store]: (...args: Parameters<Store[K]>) => Awaited<ReturnType<Store[K]>>;
433
+ };
434
+ export declare class StoreState implements SyncStore {
435
+ private signingKeys;
436
+ private activeSigningKid?;
437
+ private legacySigningHistory;
438
+ private deletedIdentities;
439
+ private owners;
440
+ private agents;
441
+ private ownerCodes;
442
+ private credentials;
443
+ private keys;
444
+ private revoked;
445
+ private clients;
446
+ private transactions;
447
+ private legacyTransactions;
448
+ private codes;
449
+ private tokens;
450
+ private consents;
451
+ private accounts;
452
+ private signups;
453
+ private signupEvents;
454
+ private deletedOwners;
455
+ private reservations;
456
+ constructor(snapshot?: string);
457
+ exportSnapshot(): string;
458
+ currentTime(fallbackNow: number): number;
459
+ publishSigningKeys(keys: SigningKeyPublication[], bootstrapKid: string, now: number, cacheLifetime: number): void;
460
+ listSigningKeys(): PublishedSigningKey[];
461
+ activateSigningKey(kid: string, now: number): boolean;
462
+ removeSigningKey(kid: string, compromised: boolean, now: number): boolean;
463
+ reserveSigningKey(expiresAt: number, now: number): PublishedSigningKey | undefined;
464
+ private quotaOwners;
465
+ private expireOwnerLinks;
466
+ putOwner(o: Owner, now?: number): void;
467
+ deleteOwner(id: string, ownerSub: string, now: number, retentionSeconds: number): boolean;
468
+ getOwner(id: string): Owner | undefined;
469
+ getOwnerByEmail(email: string): Owner;
470
+ putOwnerCode(c: OwnerCode): void;
471
+ takeOwnerCode(ownerId: string): OwnerCode | undefined;
472
+ putAgent(a: Agent): boolean;
473
+ getAgent(handle: string): Agent | undefined;
474
+ setAgentEnabled(handle: string, enabled: boolean, agentSub: string, now: number): Agent | undefined;
475
+ deleteAgent(handle: string, agentSub: string, now: number): boolean;
476
+ private managementCurrent;
477
+ resolveManagement(hash: string, handle: string): ManagementAuthorization | 'invalid_token' | 'not_found';
478
+ readManagementKeys(handle: string, authorization: ManagementAuthorization, kid?: string): AgentKey[] | undefined;
479
+ readManagementAccounts(handle: string, authorization: ManagementAuthorization): Account[] | undefined;
480
+ private invalidate;
481
+ private expiredTransaction;
482
+ private assertionSubjectReason;
483
+ private subjectReason;
484
+ listAgentsByOwner(ownerId: string): Agent[];
485
+ putCredential(c: Credential): void;
486
+ getCredential(hash: string): Credential | undefined;
487
+ listCredentials(f: {
488
+ ownerId?: string;
489
+ handle?: string;
490
+ kind?: 'owner' | 'delegated';
491
+ }): Credential[];
492
+ deleteCredential(hash: string): void;
493
+ private keyIndex;
494
+ private enrollmentKey;
495
+ getKey(kid: string, handle?: string): AgentKey | undefined;
496
+ listKeys(handle: string): AgentKey[];
497
+ insertKey(k: AgentKey, now: number, authorization: ManagementAuthorization): InsertKeyResult;
498
+ activateKey(kid: string, enrollment: string, challenge: string, now: number, assertionWindow?: {
499
+ notBefore: number;
500
+ expiresAt: number;
501
+ }): boolean;
502
+ revokeKey(kid: string, enrollment: string, now: number, authorization?: ManagementAuthorization): AgentKey | undefined;
503
+ private revokeTransactionTokens;
504
+ private transactionTokenRevoked;
505
+ revokeAllKeys(handle: string, authorization: ManagementAuthorization, now: number): boolean;
506
+ patchKey(kid: string, enrollment: string, patch: {
507
+ name?: string;
508
+ shareOwner?: boolean;
509
+ }, authorization?: ManagementAuthorization): AgentKey | undefined;
510
+ deleteKey(kid: string, enrollment?: string, pendingExpiredAt?: number): void;
511
+ wasRevoked(handle: string, kid: string): boolean;
512
+ putClient(c: Client): void;
513
+ getClient(clientId: string): Client | undefined;
514
+ insertTransaction(t: Transaction): void;
515
+ getTransaction(id: string): Transaction | undefined;
516
+ readTransaction(id: string, { now, retainFor }: {
517
+ now: number;
518
+ retainFor: number;
519
+ }): Transaction | undefined;
520
+ updateTransaction(t: Transaction, expectedVersion: number, effects: TransitionEffects): {
521
+ clientPresentation: ClientPresentation | undefined;
522
+ maxAge: number | undefined;
523
+ assertedAt: number | undefined;
524
+ id: string;
525
+ /** Incremented by every accepted change; a change is accepted only against the version it was computed from. */
526
+ version: number;
527
+ clientId: string;
528
+ redirectUri: string;
529
+ scopes: Scope[];
530
+ state: string;
531
+ nonce: string;
532
+ codeChallenge: string;
533
+ prompt?: "consent" | "none";
534
+ loginHint?: string;
535
+ disclosure: Disclosure[];
536
+ requester?: {
537
+ user_agent?: string;
538
+ network?: string;
539
+ };
540
+ status: TxStatus;
541
+ reason?: DenyReason;
542
+ consentReason?: ConsentReason;
543
+ challenge?: string;
544
+ createdAt: number;
545
+ expiresAt: number;
546
+ terminalAt?: number;
547
+ handle?: string;
548
+ kid?: string;
549
+ /** The enrollment of `kid` that asserted (see AgentKey.enrollment). */
550
+ keyEnrollment?: string;
551
+ agentSub?: string;
552
+ agentVersion?: number;
553
+ accountVersion?: number;
554
+ ownerId?: string;
555
+ acceptDisclosure: boolean;
556
+ deliverToSession: boolean;
557
+ ownerApprovalOutstanding: boolean;
558
+ reviewOutstanding: boolean;
559
+ /** The sign-in proceeds on the strength of an existing Consent Record; its last use is renewed when the transaction becomes ready. */
560
+ consentRelied?: boolean;
561
+ passedCap: boolean;
562
+ transactionTokenHash?: string;
563
+ sessionSecretHash?: string;
564
+ /** Chosen at creation; released to a requester only as Section 6.5 allows. */
565
+ continueSecret: string;
566
+ codeIssued: boolean;
567
+ } | undefined;
568
+ completeTransaction(t: Transaction, expectedVersion: number, code: Code, now: number, transactionTokenHash?: string): {
569
+ clientPresentation: ClientPresentation | undefined;
570
+ maxAge: number | undefined;
571
+ assertedAt: number | undefined;
572
+ id: string;
573
+ /** Incremented by every accepted change; a change is accepted only against the version it was computed from. */
574
+ version: number;
575
+ clientId: string;
576
+ redirectUri: string;
577
+ scopes: Scope[];
578
+ state: string;
579
+ nonce: string;
580
+ codeChallenge: string;
581
+ prompt?: "consent" | "none";
582
+ loginHint?: string;
583
+ disclosure: Disclosure[];
584
+ requester?: {
585
+ user_agent?: string;
586
+ network?: string;
587
+ };
588
+ status: TxStatus;
589
+ reason?: DenyReason;
590
+ consentReason?: ConsentReason;
591
+ challenge?: string;
592
+ createdAt: number;
593
+ expiresAt: number;
594
+ terminalAt?: number;
595
+ handle?: string;
596
+ kid?: string;
597
+ /** The enrollment of `kid` that asserted (see AgentKey.enrollment). */
598
+ keyEnrollment?: string;
599
+ agentSub?: string;
600
+ agentVersion?: number;
601
+ accountVersion?: number;
602
+ ownerId?: string;
603
+ acceptDisclosure: boolean;
604
+ deliverToSession: boolean;
605
+ ownerApprovalOutstanding: boolean;
606
+ reviewOutstanding: boolean;
607
+ /** The sign-in proceeds on the strength of an existing Consent Record; its last use is renewed when the transaction becomes ready. */
608
+ consentRelied?: boolean;
609
+ passedCap: boolean;
610
+ transactionTokenHash?: string;
611
+ sessionSecretHash?: string;
612
+ /** Chosen at creation; released to a requester only as Section 6.5 allows. */
613
+ continueSecret: string;
614
+ codeIssued: boolean;
615
+ } | {
616
+ version: number;
617
+ status: "completed";
618
+ terminalAt: number;
619
+ challenge: undefined;
620
+ codeIssued: boolean;
621
+ id: string;
622
+ clientId: string;
623
+ /** Immutable displayed metadata. Missing only on legacy records, which issuer startup cancels without backfilling. */
624
+ clientPresentation?: ClientPresentation;
625
+ redirectUri: string;
626
+ scopes: Scope[];
627
+ state: string;
628
+ nonce: string;
629
+ codeChallenge: string;
630
+ prompt?: "consent" | "none";
631
+ /** Immutable requested authentication age in seconds; zero requires this transaction's fresh assertion. */
632
+ maxAge?: number;
633
+ loginHint?: string;
634
+ disclosure: Disclosure[];
635
+ requester?: {
636
+ user_agent?: string;
637
+ network?: string;
638
+ };
639
+ reason?: DenyReason;
640
+ consentReason?: ConsentReason;
641
+ createdAt: number;
642
+ expiresAt: number;
643
+ handle?: string;
644
+ kid?: string;
645
+ /** The enrollment of `kid` that asserted (see AgentKey.enrollment). */
646
+ keyEnrollment?: string;
647
+ agentSub?: string;
648
+ agentVersion?: number;
649
+ accountVersion?: number;
650
+ ownerId?: string;
651
+ assertedAt?: number;
652
+ acceptDisclosure: boolean;
653
+ deliverToSession: boolean;
654
+ ownerApprovalOutstanding: boolean;
655
+ reviewOutstanding: boolean;
656
+ /** The sign-in proceeds on the strength of an existing Consent Record; its last use is renewed when the transaction becomes ready. */
657
+ consentRelied?: boolean;
658
+ passedCap: boolean;
659
+ transactionTokenHash?: string;
660
+ sessionSecretHash?: string;
661
+ /** Chosen at creation; released to a requester only as Section 6.5 allows. */
662
+ continueSecret: string;
663
+ } | undefined;
664
+ listOpenTransactions(f: SubjectFilter): Transaction[];
665
+ listLegacyTransactions(limit: number): Transaction[];
666
+ getCode(hash: string): Code | undefined;
667
+ redeemCode(hash: string, now: number): RedeemResult;
668
+ voidCodes(f: SubjectFilter): void;
669
+ insertAccessToken(t: AccessToken, now: number): boolean;
670
+ getAccessToken(hash: string): AccessToken | undefined;
671
+ revokeAccessTokens(f: SubjectFilter): void;
672
+ getConsent(handle: string, clientId: string): Consent | undefined;
673
+ listConsents(handle: string): Consent[];
674
+ private grantConsent;
675
+ private touchConsent;
676
+ deleteConsent(handle: string, clientId: string, authorization: ManagementAuthorization): boolean;
677
+ getAccount(handle: string, clientId: string): Account | undefined;
678
+ listAccounts(handle: string): Account[];
679
+ setAccountStatus(handle: string, clientId: string, status: 'enabled' | 'disabled', authorization: ManagementAuthorization, now: number): Account | undefined;
680
+ deleteAccount(handle: string, clientId: string): void;
681
+ signupsEver(ownerId: string, clientId: string): number;
682
+ private reserveSignup;
683
+ private extendSignup;
684
+ private releaseSignup;
685
+ private recordSignIn;
686
+ }
687
+ /** Promise facade; every state transition finishes synchronously before it yields to its caller. */
688
+ export interface MemoryStore extends Store {
689
+ }
690
+ export declare class MemoryStore {
691
+ constructor();
692
+ }
693
+ export {};