@spfn/auth 0.2.0-beta.90 → 0.2.0-beta.91

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 CHANGED
@@ -856,6 +856,13 @@ HTTP status).
856
856
  - `createClientProofGuard(state)` — Hono middleware for mounting `requiresSession` operations
857
857
  on an SPFN server; tags admitted requests `clientType: 'mobile'` (the attestation slot
858
858
  proxy-guard reserved). hono is a type-only import here.
859
+ - A refusal is **answered**, never thrown: `authenticate` / `optionalAuth` answer a request that
860
+ named this profile with the canonical envelope (`error.code` is one of the six codes, and the
861
+ body carries nothing else), and the guard and dev handler do the same. Handing the refusal to
862
+ the generic error handler instead would put the carrying error class's name in `error.code`
863
+ (`UnauthorizedError`) — a code no generated SDK can classify (#106). Errors raised **after**
864
+ admission (account status, application errors) are ordinary SPFN errors and keep the REST
865
+ envelope.
859
866
  - Replay ledger is module-local, NOT core's `NonceStore` — `checkAndSet` records on check,
860
867
  which would spend a nonce on a refused request; the contract requires spending only on
861
868
  admission.
@@ -904,6 +911,26 @@ app.use('*', createClientVersionMiddleware());
904
911
  Response header names are deliberately distinct from the request ones: a proxy that echoes a request
905
912
  header into the response would otherwise make the client's own version look like the server's.
906
913
 
914
+ ### When each operation became available (contract 0.6.1)
915
+
916
+ Every operation in the exported bundle carries `since` — the contract version it first appeared in.
917
+ `deprecatedIn` and `removedIn` are optional and absent today, because nothing has been deprecated.
918
+
919
+ | Operation | `since` |
920
+ |-----------|---------|
921
+ | `auth.clientProof.handshake`, `echo.send`, `items.list` | 0.1.0 |
922
+ | `auth.enroll.register`, `auth.enroll.login`, `auth.enroll.oauthNative`, `auth.keys.rotate` | 0.3.0 |
923
+ | `auth.keys.list`, `auth.keys.revoke`, `auth.keys.revokeAll` | 0.4.1 |
924
+
925
+ - **This is history, not policy.** The mobile contract's compatibility policy is `allOrNothing`: one
926
+ contract version passes or refuses the whole surface, so these three fields change no verdict here.
927
+ An app contract generated from SPFN routes decides `perOperation` and reads the same fields as an
928
+ input — the shape is shared so the two never diverge.
929
+ - **A removal is mark, then wait, then remove.** `deprecatedIn` in one version with the operation
930
+ still served, `removedIn` in a later one. Nothing is removed in the version that deprecates it.
931
+ - **A removed operation leaves the operations list**, so no entry carries `removedIn` today. It is
932
+ where the fact gets recorded when the first removal happens.
933
+
907
934
  ### Usage — dev surface (mobile integration target)
908
935
 
909
936
  The fastest path: run the packaged dev handler, which already serves the three contract
@@ -1,5 +1,5 @@
1
1
  import * as _spfn_core_route from '@spfn/core/route';
2
- import { K as KeyAlgorithmType, d as KeyPlatformType, h as SocialProvider } from './types-CD95yudz.js';
2
+ import { K as KeyAlgorithmType, h as KeyPlatformType, j as SocialProvider } from './types-DYyhze28.js';
3
3
  import * as _sinclair_typebox from '@sinclair/typebox';
4
4
  import { Static } from '@sinclair/typebox';
5
5
  import { Context } from 'hono';
@@ -879,7 +879,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
879
879
  id: number;
880
880
  name: string;
881
881
  displayName: string;
882
- category: "custom" | "user" | "auth" | "rbac" | "system" | undefined;
882
+ category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
883
883
  }[];
884
884
  userId: number;
885
885
  publicId: string;
@@ -1179,8 +1179,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
1179
1179
  }, {}, {
1180
1180
  roles: {
1181
1181
  description: string | null;
1182
- id: number;
1183
1182
  name: string;
1183
+ id: number;
1184
1184
  displayName: string;
1185
1185
  isBuiltin: boolean;
1186
1186
  isSystem: boolean;
@@ -1201,8 +1201,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
1201
1201
  }, {}, {
1202
1202
  role: {
1203
1203
  description: string | null;
1204
- id: number;
1205
1204
  name: string;
1205
+ id: number;
1206
1206
  displayName: string;
1207
1207
  isBuiltin: boolean;
1208
1208
  isSystem: boolean;
@@ -1225,8 +1225,8 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
1225
1225
  }, {}, {
1226
1226
  role: {
1227
1227
  description: string | null;
1228
- id: number;
1229
1228
  name: string;
1229
+ id: number;
1230
1230
  displayName: string;
1231
1231
  isBuiltin: boolean;
1232
1232
  isSystem: boolean;
@@ -1314,6 +1314,26 @@ declare function resolveAuthenticatedUser(userId: number): Promise<{
1314
1314
  role: string | null;
1315
1315
  locale: string;
1316
1316
  }>;
1317
+ /** What the profile path produced for one request. */
1318
+ type AuthProfileOutcome = {
1319
+ kind: 'none';
1320
+ } | {
1321
+ kind: 'authenticated';
1322
+ auth: AuthContext;
1323
+ } | {
1324
+ kind: 'refused';
1325
+ response: Response;
1326
+ };
1327
+ /**
1328
+ * The profile path from dispatch to answer — what `authenticate` and
1329
+ * `optionalAuth` both run before their own Bearer code.
1330
+ *
1331
+ * `none` means the request named no profile and the caller continues on the
1332
+ * Bearer path. A refusal comes back as a built response rather than a throw:
1333
+ * the answer a proven call gets is the contract's own envelope, and an error
1334
+ * handed to the generic error handler is classified by its class name instead.
1335
+ */
1336
+ declare function runAuthProfile(c: Context): Promise<AuthProfileOutcome>;
1317
1337
 
1318
1338
  declare module 'hono' {
1319
1339
  interface ContextVariableMap {
@@ -1380,4 +1400,4 @@ declare const authenticate: _spfn_core_route.NamedMiddleware<"auth">;
1380
1400
  */
1381
1401
  declare const optionalAuth: _spfn_core_route.NamedMiddleware<"optionalAuth">;
1382
1402
 
1383
- export { requireEnabledProvider as $, type AuthSession as A, registerPublicKeyService as B, type ChangePasswordParams as C, rotateKeyService as D, revokeKeyService as E, listKeysService as F, revokeAllKeysService as G, KEY_FINGERPRINT_PREFIX_LENGTH as H, type IssueOneTimeTokenResult as I, type RegisterPublicKeyParams as J, type KeySummary as K, type LoginResult as L, type RotateKeyParams as M, type RevokeKeyParams as N, type OAuthStartResult as O, type PermissionConfig as P, type RevokeAllKeysParams as Q, type RoleConfig as R, type SendVerificationCodeResult as S, issueOneTimeTokenService as T, type UserProfile as U, type VerificationTargetType as V, verifyOneTimeTokenService as W, oauthStartService as X, oauthCallbackService as Y, buildOAuthErrorUrl as Z, isOAuthProviderEnabled as _, type RegisterResult as a, getEnabledOAuthProviders as a0, getGoogleAccessToken as a1, oauthUnlinkNotifyService as a2, type OAuthStartParams as a3, type OAuthCallbackParams as a4, type OAuthCallbackResult as a5, type UnlinkNotifyResult as a6, oauthNativeService as a7, type OAuthNativeParams as a8, selectAuthProfile as a9, resolveAuthenticatedUser as aa, type AuthProfileVerifier as ab, authenticate as ac, optionalAuth as ad, EmailSchema as ae, PhoneSchema as af, DeviceNameSchema as ag, PlatformSchema as ah, PasswordSchema as ai, TargetTypeSchema as aj, VerificationPurposeSchema as ak, type NormalizedIdentity as al, type OAuthTokens as am, type NativeVerifyOptions as an, type OAuthCodeExchangeOptions as ao, type UnlinkNotifyRequest as ap, type UnlinkNotification as aq, UnlinkNotifyRejection as ar, registerOAuthProvider as as, getOAuthProvider as at, getRegisteredProviders as au, type RotateKeyResult as b, type RevokeAllKeysResult as c, type OAuthNativeResult as d, type ProfileInfo as e, type VerificationPurpose as f, VERIFICATION_TARGET_TYPES as g, VERIFICATION_PURPOSES as h, PERMISSION_CATEGORIES as i, type PermissionCategory as j, type AuthInitOptions as k, type OAuthProvider as l, mainAuthRouter as m, type AuthContext as n, loginService as o, logoutService as p, changePasswordService as q, registerService as r, type RegisterParams as s, type LoginParams as t, type LogoutParams as u, sendVerificationCodeService as v, verifyCodeService as w, type SendVerificationCodeParams as x, type VerifyCodeParams as y, type VerifyCodeResult as z };
1403
+ export { type UnlinkNotifyResult as $, type AuthInitOptions as A, PasswordSchema as B, type ChangePasswordParams as C, DeviceNameSchema as D, EmailSchema as E, PhoneSchema as F, PlatformSchema as G, type RegisterParams as H, type IssueOneTimeTokenResult as I, type RegisterPublicKeyParams as J, type KeySummary as K, type LoginResult as L, type RevokeAllKeysParams as M, type NativeVerifyOptions as N, type OAuthStartResult as O, type PermissionConfig as P, type RevokeKeyParams as Q, type RoleConfig as R, type SendVerificationCodeResult as S, type RotateKeyParams as T, type UserProfile as U, VERIFICATION_PURPOSES as V, type SendVerificationCodeParams as W, TargetTypeSchema as X, type UnlinkNotification as Y, UnlinkNotifyRejection as Z, type UnlinkNotifyRequest as _, type RegisterResult as a, VerificationPurposeSchema as a0, type VerifyCodeParams as a1, type VerifyCodeResult as a2, authenticate as a3, buildOAuthErrorUrl as a4, changePasswordService as a5, getEnabledOAuthProviders as a6, getGoogleAccessToken as a7, getOAuthProvider as a8, getRegisteredProviders as a9, isOAuthProviderEnabled as aa, issueOneTimeTokenService as ab, listKeysService as ac, loginService as ad, logoutService as ae, oauthCallbackService as af, oauthNativeService as ag, oauthStartService as ah, oauthUnlinkNotifyService as ai, optionalAuth as aj, registerOAuthProvider as ak, registerPublicKeyService as al, registerService as am, requireEnabledProvider as an, resolveAuthenticatedUser as ao, revokeAllKeysService as ap, revokeKeyService as aq, rotateKeyService as ar, runAuthProfile as as, selectAuthProfile as at, sendVerificationCodeService as au, verifyCodeService as av, verifyOneTimeTokenService as aw, type RotateKeyResult as b, type RevokeAllKeysResult as c, type OAuthNativeResult as d, type ProfileInfo as e, type AuthSession as f, PERMISSION_CATEGORIES as g, type PermissionCategory as h, VERIFICATION_TARGET_TYPES as i, type VerificationPurpose as j, type VerificationTargetType as k, type OAuthProvider as l, mainAuthRouter as m, type AuthContext as n, type AuthProfileOutcome as o, type AuthProfileVerifier as p, KEY_FINGERPRINT_PREFIX_LENGTH as q, type LoginParams as r, type LogoutParams as s, type NormalizedIdentity as t, type OAuthCallbackParams as u, type OAuthCallbackResult as v, type OAuthCodeExchangeOptions as w, type OAuthNativeParams as x, type OAuthStartParams as y, type OAuthTokens as z };
@@ -1,5 +1,7 @@
1
1
  import { KeyObject } from 'node:crypto';
2
- import { MiddlewareHandler } from 'hono';
2
+ import { C as ClientProofRefusal } from './wire-version-CtzMKvBB.js';
3
+ export { a as CLIENT_IDENTITY_HEADERS, b as CLIENT_KINDS, c as ClientIdentity, d as ClientKind, e as ClientProofErrorCode, S as SERVER_CONTRACT_HEADERS, f as applyServerContractHeaders, i as isAppKind, g as isContractVersionSupported, j as judgeClientIdentity, n as newHexId, r as readClientIdentity, s as serverContractHeaders } from './wire-version-CtzMKvBB.js';
4
+ import { MiddlewareHandler, Context } from 'hono';
3
5
 
4
6
  /**
5
7
  * SPFN-CANON-JSON-1 — the canonical JSON form the mobile contract pins.
@@ -121,49 +123,6 @@ declare function signClientProof(input: ClientProofInput, privateKeyPkcs8DerBase
121
123
  /** Lowercase base16 SHA-256 of `bytes`. */
122
124
  declare function sha256Hex(bytes: Uint8Array): string;
123
125
 
124
- /** The six wire codes. The SDKs classify by code, never HTTP status. */
125
- type ClientProofErrorCode = 'PROOF_INVALID' | 'PROOF_REPLAYED' | 'PROOF_EXPIRED' | 'SESSION_REVOKED' | 'PROFILE_REJECTED' | 'CONTRACT_UNSUPPORTED';
126
- /** 128 random bits as lowercase base16 — request ids and control tokens. */
127
- declare function newHexId(): string;
128
- declare class ClientProofRefusal {
129
- readonly code: ClientProofErrorCode;
130
- readonly message: string;
131
- constructor(code: ClientProofErrorCode, message: string);
132
- get httpStatus(): number;
133
- /** The canonical bytes of `{"error":{"code":…,"message":…,"requestId":…}}`. */
134
- envelopeBytes(requestId: string): Uint8Array;
135
- /** Nothing request-derived reaches a log through this. */
136
- toString(): string;
137
- static unroutable(): ClientProofRefusal;
138
- static malformedHeaders(): ClientProofRefusal;
139
- static missingContentType(): ClientProofRefusal;
140
- static bodyTooLarge(): ClientProofRefusal;
141
- /**
142
- * The body parsed but its bytes are not the canonical form of what it
143
- * parsed to. Not PROOF_INVALID even though it is discovered next to the
144
- * proof: the proof over these bytes verifies perfectly well, and an
145
- * auth-family answer would tell the client to re-handshake and send the
146
- * same non-canonical bytes again.
147
- */
148
- static bodyNotCanonical(): ClientProofRefusal;
149
- static bodyNotTheDeclaredType(): ClientProofRefusal;
150
- static sessionHeaderMisplaced(): ClientProofRefusal;
151
- static unprocessable(): ClientProofRefusal;
152
- /**
153
- * A client that ships separately from the server said nothing about which
154
- * contract it was built against. Without it the server cannot tell whether
155
- * the two ends agree, and answering as though they do is what produces the
156
- * undecodable body this check exists to replace.
157
- */
158
- static contractVersionMissing(): ClientProofRefusal;
159
- static contractVersionUnsupported(): ClientProofRefusal;
160
- static profileRejected(): ClientProofRefusal;
161
- static sessionRevoked(): ClientProofRefusal;
162
- static proofExpired(): ClientProofRefusal;
163
- static proofReplayed(): ClientProofRefusal;
164
- static proofInvalid(): ClientProofRefusal;
165
- }
166
-
167
126
  /** Millisecond clock. Injectable so expiry paths are testable without waiting. */
168
127
  interface ClientProofClock {
169
128
  nowMillis(): number;
@@ -452,6 +411,33 @@ interface ContractOperation {
452
411
  requestType: string;
453
412
  responseType: string;
454
413
  summary: string;
414
+ /**
415
+ * The contract version this operation first appeared in. Required, so an
416
+ * operation added later cannot ship without one: omitting it is a compile
417
+ * error rather than a hole a consumer discovers.
418
+ *
419
+ * It is history, not policy. This contract's compatibility policy is
420
+ * `allOrNothing` — one version passes or refuses the whole surface — so
421
+ * nothing here changes a verdict. It exists so a deprecation has somewhere
422
+ * to be recorded, and as the precedent an app contract's `perOperation`
423
+ * policy reads.
424
+ */
425
+ since: string;
426
+ /**
427
+ * The contract version that marked this operation deprecated, if one has.
428
+ * A deprecated operation is still served: the mark is the notice that opens
429
+ * the grace period before removal.
430
+ */
431
+ deprecatedIn?: string;
432
+ /**
433
+ * The contract version that removed this operation, if one has.
434
+ *
435
+ * A removed operation leaves this list, so nothing here carries the field
436
+ * today. When the first removal happens, `removedIn` is where the fact is
437
+ * recorded — how a removed operation stays visible after leaving the list
438
+ * is decided then, not invented in advance.
439
+ */
440
+ removedIn?: string;
455
441
  }
456
442
  declare const CONTRACT_OPERATIONS: readonly ContractOperation[];
457
443
  /**
@@ -563,87 +549,30 @@ interface ClientProofGuardOptions {
563
549
  declare function createClientProofGuard(state: ClientProofState, options?: ClientProofGuardOptions): MiddlewareHandler;
564
550
 
565
551
  /**
566
- * The header names each end announces itself under.
552
+ * One place turns a clientProofV1 refusal into a response.
567
553
  *
568
- * Separated from the logic that reads them so the contract bundle can name them
569
- * without importing the version comparison, which reads the bundle back. These
570
- * are declarations and depend on nothing.
554
+ * A proven call is answered by a generated SDK that classifies a failure by
555
+ * `error.code` alone and refuses a code it does not know. So a refusal must
556
+ * leave this server as the contract's own envelope — the canonical bytes of
557
+ * `{"error":{"code","message","requestId"}}` carrying one of the six refusal
558
+ * codes — and nothing else. Routing a refusal through the generic error
559
+ * handler instead puts the wrapper error class's name in `error.code`
560
+ * (`UnauthorizedError`), which no SDK can classify (#106).
571
561
  *
572
- * @module server/client-proof/wire-headers
573
- */
574
- /** What a client says about itself, one header each. */
575
- declare const CLIENT_IDENTITY_HEADERS: {
576
- readonly kind: "x-spfn-client-kind";
577
- readonly version: "x-spfn-client-version";
578
- readonly contractVersion: "x-spfn-client-contract-version";
579
- };
580
- /**
581
- * What the server says about itself, on every response.
562
+ * Every refusal surface (the guard, the profile middleware) builds its answer
563
+ * here rather than assembling one of its own, so a code path added later
564
+ * cannot reintroduce a body that says something else.
582
565
  *
583
- * Distinct names from the request headers on purpose: a proxy that echoes a
584
- * request header into the response would otherwise make the client's own
585
- * version look like the server's.
586
- */
587
- declare const SERVER_CONTRACT_HEADERS: {
588
- readonly version: "x-spfn-server-contract-version";
589
- readonly supportedRange: "x-spfn-supported-contract-range";
590
- };
591
- /**
592
- * The client kinds the server distinguishes.
566
+ * hono is imported as types only, so this module adds no runtime dependency.
593
567
  *
594
- * `web` is separated from the two app kinds because it carries no contract
595
- * version: a browser bundle is deployed with the server that serves it, so
596
- * there is no second version to reconcile.
568
+ * @module server/client-proof/refusal-response
597
569
  */
598
- declare const CLIENT_KINDS: readonly ["web", "ios", "android"];
599
- type ClientKind = typeof CLIENT_KINDS[number];
600
- /** A kind that ships independently of the server, so its contract version matters. */
601
- declare function isAppKind(kind: ClientKind): boolean;
602
570
 
603
- /** What one request announced about the client that sent it. */
604
- interface ClientIdentity {
605
- kind: ClientKind;
606
- /** The client's own release — a store version, or a bundle build. */
607
- version: string | null;
608
- /** The contract version the client was generated from. Never set for `web`. */
609
- contractVersion: string | null;
610
- }
611
571
  /**
612
- * Reads the identity headers, or null when the kind is absent or unrecognised.
613
- *
614
- * Null is not by itself a refusal — a request from something that predates
615
- * these headers reaches here too. `judgeClientIdentity` decides.
616
- */
617
- declare function readClientIdentity(headers: Headers): ClientIdentity | null;
618
- /**
619
- * Whether the server serves what the client was generated against.
620
- *
621
- * Under 0.x the minor carries breaking changes, so a supported client agrees on
622
- * major and minor. From 1.0.0 the major alone decides. This is the rule
623
- * `CONTRACT_SUPPORTED_RANGE` spells out; keeping it as a comparison rather than
624
- * parsing that string leaves one place to change when the line reaches 1.0.0.
625
- */
626
- declare function isContractVersionSupported(clientVersion: string): boolean;
627
- /**
628
- * The refusal a request's announced identity earns, or null to let it through.
629
- *
630
- * An app kind must state a contract version this server serves. A version it
631
- * does not serve, and the absence of one, are the same answer: the two ends do
632
- * not agree on what the contract is, which is what CONTRACT_UNSUPPORTED means.
633
- * The response carries the server's version and range, so the client can say
634
- * which way the gap runs.
635
- *
636
- * `web` is exempt from the contract check by construction, not by leniency.
637
- *
638
- * A request with no recognised kind passes. The check is on what a client says
639
- * about itself, and a caller that says nothing — a curl, a health probe, a
640
- * server-to-server call — is not a deployed client this rule is about.
572
+ * The canonical contract envelope for one refusal, with the server's contract
573
+ * announcement — a refused client needs the range most.
641
574
  */
642
- declare function judgeClientIdentity(identity: ClientIdentity | null): ClientProofRefusal | null;
643
- /** Writes the server's own announcement onto a response's headers. */
644
- declare function applyServerContractHeaders(headers: Headers): void;
645
- /** The same announcement as a plain object, for a response built from one. */
646
- declare function serverContractHeaders(): Record<string, string>;
575
+ declare function clientProofRefusalResponse(c: Context, refusal: ClientProofRefusal): Response;
647
576
 
648
577
  /**
649
578
  * The version announcement, applied to every request rather than to the proven
@@ -674,4 +603,4 @@ declare const CLIENT_IDENTITY_CONTEXT_KEY = "clientIdentity";
674
603
  */
675
604
  declare function createClientVersionMiddleware(): MiddlewareHandler;
676
605
 
677
- export { ABSENT_BODY_SHA256, AUTH_SURFACE_OPERATIONS, type Admission, CLIENT_IDENTITY_CONTEXT_KEY, CLIENT_IDENTITY_HEADERS, CLIENT_KINDS, CLIENT_PROOF_CONTENT_TYPE, CLIENT_PROOF_HEADERS, CLIENT_PROOF_PROFILE, CONTRACT_OPERATIONS, CONTROL_PREFIX, CONTROL_TOKEN_HEADER, CanonicalJsonError, type CanonicalJsonErrorCode, type CanonicalObject, type CanonicalValue, type ClientIdentity, type ClientKind, type ClientProofClock, type ClientProofContext, type ClientProofCredentials, type ClientProofDevHandler, type ClientProofDevHandlerOptions, type ClientProofErrorCode, type ClientProofGuardOptions, type ClientProofInput, ClientProofRefusal, type ClientProofReplayStore, ClientProofState, type ClientProofStateOptions, type ClientProofStats, type ContractItem, type ContractOperation, ContractTypeError, DEFAULT_REPLAY_WINDOW_MILLIS, DEFAULT_SESSION_TTL_MILLIS, DEV_CATALOGUE, DEV_MAX_LIMIT, type EchoRequest, type HandshakeRequest, type ListItemsRequest, MemoryReplayLedger, MemoryReplayStore, PROOF_SIGNATURE_BYTES, PROOF_SIGNATURE_HEX_LENGTH, ProofInputError, RedisReplayStore, SERVER_CONTRACT_HEADERS, TestClock, admitClientProofRequest, applyServerContractHeaders, canonicalProofInput, configureClientProofReplayStore, createClientProofDevHandler, createClientProofGuard, createClientVersionMiddleware, decodeEchoRequest, decodeHandshakeRequest, decodeListItemsRequest, encodeCanonicalJson, encodeEchoResponse, encodeHandshakeResponse, encodeListItemsResponse, getClientProofReplayStore, isAppKind, isCanonicalBytes, isContractVersionSupported, isRequestContentType, judgeClientIdentity, newHexId, parseCanonicalJson, parseClientProofPublicKey, readClientIdentity, readCredentials, replayLedgerKey, serverContractHeaders, sha256Hex, signClientProof, systemClock, verifyClientProof };
606
+ export { ABSENT_BODY_SHA256, AUTH_SURFACE_OPERATIONS, type Admission, CLIENT_IDENTITY_CONTEXT_KEY, CLIENT_PROOF_CONTENT_TYPE, CLIENT_PROOF_HEADERS, CLIENT_PROOF_PROFILE, CONTRACT_OPERATIONS, CONTROL_PREFIX, CONTROL_TOKEN_HEADER, CanonicalJsonError, type CanonicalJsonErrorCode, type CanonicalObject, type CanonicalValue, type ClientProofClock, type ClientProofContext, type ClientProofCredentials, type ClientProofDevHandler, type ClientProofDevHandlerOptions, type ClientProofGuardOptions, type ClientProofInput, ClientProofRefusal, type ClientProofReplayStore, ClientProofState, type ClientProofStateOptions, type ClientProofStats, type ContractItem, type ContractOperation, ContractTypeError, DEFAULT_REPLAY_WINDOW_MILLIS, DEFAULT_SESSION_TTL_MILLIS, DEV_CATALOGUE, DEV_MAX_LIMIT, type EchoRequest, type HandshakeRequest, type ListItemsRequest, MemoryReplayLedger, MemoryReplayStore, PROOF_SIGNATURE_BYTES, PROOF_SIGNATURE_HEX_LENGTH, ProofInputError, RedisReplayStore, TestClock, admitClientProofRequest, canonicalProofInput, clientProofRefusalResponse, configureClientProofReplayStore, createClientProofDevHandler, createClientProofGuard, createClientVersionMiddleware, decodeEchoRequest, decodeHandshakeRequest, decodeListItemsRequest, encodeCanonicalJson, encodeEchoResponse, encodeHandshakeResponse, encodeListItemsResponse, getClientProofReplayStore, isCanonicalBytes, isRequestContentType, parseCanonicalJson, parseClientProofPublicKey, readCredentials, replayLedgerKey, sha256Hex, signClientProof, systemClock, verifyClientProof };
@@ -512,6 +512,18 @@ var ClientProofRefusal = class _ClientProofRefusal {
512
512
  static profileRejected() {
513
513
  return new _ClientProofRefusal("PROFILE_REJECTED", "the named auth profile is not on this contract's allowlist");
514
514
  }
515
+ /**
516
+ * A request that names a profile and presents Bearer credentials as well.
517
+ * The profile named is a real one, so this is not a shape the two ends
518
+ * disagree about: the request asked to be authenticated two ways at once
519
+ * and the profile it named is the one refused.
520
+ */
521
+ static credentialsMixed() {
522
+ return new _ClientProofRefusal(
523
+ "PROFILE_REJECTED",
524
+ "an auth profile and Bearer credentials must not be mixed in one request"
525
+ );
526
+ }
515
527
  // ---- auth: a new session might clear it (rule 1) -------------------------
516
528
  static sessionRevoked() {
517
529
  return new _ClientProofRefusal("SESSION_REVOKED", "the key or session was revoked");
@@ -932,7 +944,8 @@ var CONTRACT_OPERATIONS = [
932
944
  requiresSession: false,
933
945
  requestType: "HandshakeRequest",
934
946
  responseType: "HandshakeResponse",
935
- summary: "Presents a client proof and opens a session."
947
+ summary: "Presents a client proof and opens a session.",
948
+ since: "0.1.0"
936
949
  },
937
950
  {
938
951
  id: "echo.send",
@@ -942,7 +955,8 @@ var CONTRACT_OPERATIONS = [
942
955
  requiresSession: true,
943
956
  requestType: "EchoRequest",
944
957
  responseType: "EchoResponse",
945
- summary: "Authenticated round trip used as the smallest real vertical slice."
958
+ summary: "Authenticated round trip used as the smallest real vertical slice.",
959
+ since: "0.1.0"
946
960
  },
947
961
  {
948
962
  id: "items.list",
@@ -952,7 +966,8 @@ var CONTRACT_OPERATIONS = [
952
966
  requiresSession: true,
953
967
  requestType: "ListItemsRequest",
954
968
  responseType: "ListItemsResponse",
955
- summary: "Authenticated paged read covering optional fields and arrays."
969
+ summary: "Authenticated paged read covering optional fields and arrays.",
970
+ since: "0.1.0"
956
971
  }
957
972
  ];
958
973
  var AUTH_SURFACE_OPERATIONS = [
@@ -964,7 +979,8 @@ var AUTH_SURFACE_OPERATIONS = [
964
979
  requiresSession: false,
965
980
  requestType: "RegisterRequest",
966
981
  responseType: "RegisterResponse",
967
- summary: "Registers an account with a verification token and enrolls the client-generated public key."
982
+ summary: "Registers an account with a verification token and enrolls the client-generated public key.",
983
+ since: "0.3.0"
968
984
  },
969
985
  {
970
986
  id: "auth.enroll.login",
@@ -974,7 +990,8 @@ var AUTH_SURFACE_OPERATIONS = [
974
990
  requiresSession: false,
975
991
  requestType: "LoginRequest",
976
992
  responseType: "LoginResponse",
977
- summary: "Authenticates with password credentials and enrolls a fresh client-generated public key."
993
+ summary: "Authenticates with password credentials and enrolls a fresh client-generated public key.",
994
+ since: "0.3.0"
978
995
  },
979
996
  {
980
997
  id: "auth.enroll.oauthNative",
@@ -984,7 +1001,8 @@ var AUTH_SURFACE_OPERATIONS = [
984
1001
  requiresSession: false,
985
1002
  requestType: "OauthNativeRequest",
986
1003
  responseType: "OauthNativeResponse",
987
- summary: "Verifies a native/web social id_token server-side and enrolls the client-generated public key."
1004
+ summary: "Verifies a native/web social id_token server-side and enrolls the client-generated public key.",
1005
+ since: "0.3.0"
988
1006
  },
989
1007
  {
990
1008
  id: "auth.keys.rotate",
@@ -994,7 +1012,8 @@ var AUTH_SURFACE_OPERATIONS = [
994
1012
  requiresSession: false,
995
1013
  requestType: "RotateKeyRequest",
996
1014
  responseType: "RotateKeyResponse",
997
- summary: "Replaces the authenticated key with a new client-generated public key before its TTL runs out."
1015
+ summary: "Replaces the authenticated key with a new client-generated public key before its TTL runs out.",
1016
+ since: "0.3.0"
998
1017
  },
999
1018
  {
1000
1019
  id: "auth.keys.list",
@@ -1004,7 +1023,8 @@ var AUTH_SURFACE_OPERATIONS = [
1004
1023
  requiresSession: false,
1005
1024
  requestType: "ListKeysRequest",
1006
1025
  responseType: "ListKeysResponse",
1007
- summary: "Lists the keys registered to the caller, one per device that can sign for them."
1026
+ summary: "Lists the keys registered to the caller, one per device that can sign for them.",
1027
+ since: "0.4.1"
1008
1028
  },
1009
1029
  {
1010
1030
  id: "auth.keys.revoke",
@@ -1014,7 +1034,8 @@ var AUTH_SURFACE_OPERATIONS = [
1014
1034
  requiresSession: false,
1015
1035
  requestType: "RevokeKeyRequest",
1016
1036
  responseType: "RevokeKeyResponse",
1017
- summary: "Revokes one of the caller's keys, signing that device out."
1037
+ summary: "Revokes one of the caller's keys, signing that device out.",
1038
+ since: "0.4.1"
1018
1039
  },
1019
1040
  {
1020
1041
  id: "auth.keys.revokeAll",
@@ -1024,7 +1045,8 @@ var AUTH_SURFACE_OPERATIONS = [
1024
1045
  requiresSession: false,
1025
1046
  requestType: "RevokeAllKeysRequest",
1026
1047
  responseType: "RevokeAllKeysResponse",
1027
- summary: "Revokes every key the caller has, sparing the calling device unless asked otherwise."
1048
+ summary: "Revokes every key the caller has, sparing the calling device unless asked otherwise.",
1049
+ since: "0.4.1"
1028
1050
  }
1029
1051
  ];
1030
1052
  var ContractTypeError = class extends Error {
@@ -1285,9 +1307,9 @@ function isAppKind(kind) {
1285
1307
  }
1286
1308
 
1287
1309
  // src/server/client-proof/contract-bundle.ts
1288
- var CONTRACT_VERSION = "0.6.0";
1310
+ var CONTRACT_VERSION = "0.8.0";
1289
1311
  var CONTRACT_MAJOR = 0;
1290
- var CONTRACT_SUPPORTED_RANGE = ">=0.6.0 <0.7.0";
1312
+ var CONTRACT_SUPPORTED_RANGE = ">=0.8.0 <0.9.0";
1291
1313
  function required(name, type) {
1292
1314
  return { name, type, optional: false };
1293
1315
  }
@@ -1697,6 +1719,16 @@ function toArrayBuffer(bytes) {
1697
1719
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
1698
1720
  }
1699
1721
 
1722
+ // src/server/client-proof/refusal-response.ts
1723
+ function clientProofRefusalResponse(c, refusal) {
1724
+ const bytes = refusal.envelopeBytes(newHexId());
1725
+ const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
1726
+ return c.newResponse(buffer, refusal.httpStatus, {
1727
+ "content-type": "application/json",
1728
+ ...serverContractHeaders()
1729
+ });
1730
+ }
1731
+
1700
1732
  // src/server/client-proof/guard.ts
1701
1733
  function createClientProofGuard(state, options = {}) {
1702
1734
  return async (c, next) => {
@@ -1711,12 +1743,7 @@ function createClientProofGuard(state, options = {}) {
1711
1743
  });
1712
1744
  if (!admission.admitted) {
1713
1745
  state.recordRefusal();
1714
- const bytes = admission.refusal.envelopeBytes(newHexId());
1715
- const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
1716
- return c.newResponse(buffer, admission.refusal.httpStatus, {
1717
- "content-type": "application/json",
1718
- ...serverContractHeaders()
1719
- });
1746
+ return clientProofRefusalResponse(c, admission.refusal);
1720
1747
  }
1721
1748
  c.set("clientType", "mobile");
1722
1749
  c.set("clientProof", {
@@ -1782,6 +1809,7 @@ export {
1782
1809
  admitClientProofRequest,
1783
1810
  applyServerContractHeaders,
1784
1811
  canonicalProofInput,
1812
+ clientProofRefusalResponse,
1785
1813
  configureClientProofReplayStore,
1786
1814
  createClientProofDevHandler,
1787
1815
  createClientProofGuard,