@peac/contracts 0.15.1 → 0.15.3
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/dist/codes.d.ts +1 -0
- package/dist/codes.d.ts.map +1 -1
- package/dist/index.cjs +50 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -1
- package/dist/index.js.map +1 -1
- package/dist/issuer-origin.d.ts +31 -0
- package/dist/issuer-origin.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/codes.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export declare const CANONICAL_ERROR_CODES: {
|
|
|
22
22
|
readonly TAP_TAG_UNKNOWN: "E_TAP_TAG_UNKNOWN";
|
|
23
23
|
readonly TAP_ALGORITHM_INVALID: "E_TAP_ALGORITHM_INVALID";
|
|
24
24
|
readonly TAP_KEY_NOT_FOUND: "E_TAP_KEY_NOT_FOUND";
|
|
25
|
+
readonly TAP_KEYID_INVALID: "E_TAP_KEYID_INVALID";
|
|
25
26
|
readonly TAP_NONCE_REPLAY: "E_TAP_NONCE_REPLAY";
|
|
26
27
|
readonly TAP_REPLAY_PROTECTION_REQUIRED: "E_TAP_REPLAY_PROTECTION_REQUIRED";
|
|
27
28
|
readonly ISSUER_NOT_ALLOWED: "E_ISSUER_NOT_ALLOWED";
|
package/dist/codes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../src/codes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../src/codes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;CA8BxB,CAAC;AAEX;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -14,6 +14,7 @@ var CANONICAL_ERROR_CODES = {
|
|
|
14
14
|
TAP_TAG_UNKNOWN: "E_TAP_TAG_UNKNOWN",
|
|
15
15
|
TAP_ALGORITHM_INVALID: "E_TAP_ALGORITHM_INVALID",
|
|
16
16
|
TAP_KEY_NOT_FOUND: "E_TAP_KEY_NOT_FOUND",
|
|
17
|
+
TAP_KEYID_INVALID: "E_TAP_KEYID_INVALID",
|
|
17
18
|
// Replay error (409 - Conflict)
|
|
18
19
|
TAP_NONCE_REPLAY: "E_TAP_NONCE_REPLAY",
|
|
19
20
|
// Replay protection required (401 - requires config change)
|
|
@@ -26,6 +27,47 @@ var CANONICAL_ERROR_CODES = {
|
|
|
26
27
|
INTERNAL_ERROR: "E_INTERNAL_ERROR"
|
|
27
28
|
};
|
|
28
29
|
|
|
30
|
+
// src/issuer-origin.ts
|
|
31
|
+
function hasAsciiControl(value) {
|
|
32
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
33
|
+
const code = value.charCodeAt(index);
|
|
34
|
+
if (code <= 31 || code === 127) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
function toHttpsOrigin(value) {
|
|
41
|
+
if (value.trim() !== value || hasAsciiControl(value)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
let url;
|
|
45
|
+
try {
|
|
46
|
+
url = new URL(value);
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (url.protocol !== "https:") {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (url.username !== "" || url.password !== "") {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return url.origin;
|
|
57
|
+
}
|
|
58
|
+
function isAllowedIssuerOrigin(candidate, allowlist) {
|
|
59
|
+
const candidateOrigin = toHttpsOrigin(candidate);
|
|
60
|
+
if (candidateOrigin === null) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
for (const entry of allowlist) {
|
|
64
|
+
if (toHttpsOrigin(entry) === candidateOrigin) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
29
71
|
// src/internal/error-codes.ts
|
|
30
72
|
var VALUES = Object.values(CANONICAL_ERROR_CODES);
|
|
31
73
|
var PEAC_ERROR_CODE_SET = new Set(VALUES);
|
|
@@ -42,6 +84,7 @@ var CANONICAL_STATUS_MAPPINGS = {
|
|
|
42
84
|
[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 401,
|
|
43
85
|
[CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 401,
|
|
44
86
|
[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 401,
|
|
87
|
+
[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: 401,
|
|
45
88
|
[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 401,
|
|
46
89
|
// 400 - Client errors (malformed)
|
|
47
90
|
[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 400,
|
|
@@ -66,6 +109,7 @@ var CANONICAL_TITLES = {
|
|
|
66
109
|
[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: "Unknown TAP Tag",
|
|
67
110
|
[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: "Invalid Algorithm",
|
|
68
111
|
[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: "Key Not Found",
|
|
112
|
+
[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: "Invalid Key ID",
|
|
69
113
|
[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: "Nonce Replay Detected",
|
|
70
114
|
[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: "Replay Protection Required",
|
|
71
115
|
[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: "Issuer Not Allowed",
|
|
@@ -139,6 +183,11 @@ var ERROR_CATALOG = {
|
|
|
139
183
|
title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],
|
|
140
184
|
defaultDetail: "TAP signing key not found."
|
|
141
185
|
},
|
|
186
|
+
[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: {
|
|
187
|
+
status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],
|
|
188
|
+
title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],
|
|
189
|
+
defaultDetail: "TAP keyid must be an absolute https URL identifying the issuer."
|
|
190
|
+
},
|
|
142
191
|
[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: {
|
|
143
192
|
status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],
|
|
144
193
|
title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],
|
|
@@ -187,6 +236,7 @@ exports.PROBLEM_TYPE_BASE = PROBLEM_TYPE_BASE;
|
|
|
187
236
|
exports.WWW_AUTHENTICATE_STATUSES = WWW_AUTHENTICATE_STATUSES;
|
|
188
237
|
exports.buildWwwAuthenticate = buildWwwAuthenticate;
|
|
189
238
|
exports.getStatusForCode = getStatusForCode;
|
|
239
|
+
exports.isAllowedIssuerOrigin = isAllowedIssuerOrigin;
|
|
190
240
|
exports.isPeacErrorCode = isPeacErrorCode;
|
|
191
241
|
exports.problemTypeFor = problemTypeFor;
|
|
192
242
|
exports.requiresWwwAuthenticate = requiresWwwAuthenticate;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/codes.ts","../src/internal/error-codes.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Canonical PEAC error codes.\n *\n * This is the single source of truth for error code values.\n * All other modules import from here to avoid circular dependencies.\n */\n\n/**\n * Canonical PEAC error codes.\n *\n * Format: E_<CATEGORY>_<ERROR>\n *\n * All error codes use the E_ prefix for consistency with RFC/IETF error code conventions.\n */\nexport const CANONICAL_ERROR_CODES = {\n // Receipt errors (402 - Payment Required)\n RECEIPT_MISSING: 'E_RECEIPT_MISSING',\n RECEIPT_INVALID: 'E_RECEIPT_INVALID',\n RECEIPT_EXPIRED: 'E_RECEIPT_EXPIRED',\n\n // TAP errors (401 - Authentication)\n TAP_SIGNATURE_MISSING: 'E_TAP_SIGNATURE_MISSING',\n TAP_SIGNATURE_INVALID: 'E_TAP_SIGNATURE_INVALID',\n TAP_TIME_INVALID: 'E_TAP_TIME_INVALID',\n TAP_WINDOW_TOO_LARGE: 'E_TAP_WINDOW_TOO_LARGE',\n TAP_TAG_UNKNOWN: 'E_TAP_TAG_UNKNOWN',\n TAP_ALGORITHM_INVALID: 'E_TAP_ALGORITHM_INVALID',\n TAP_KEY_NOT_FOUND: 'E_TAP_KEY_NOT_FOUND',\n\n // Replay error (409 - Conflict)\n TAP_NONCE_REPLAY: 'E_TAP_NONCE_REPLAY',\n\n // Replay protection required (401 - requires config change)\n TAP_REPLAY_PROTECTION_REQUIRED: 'E_TAP_REPLAY_PROTECTION_REQUIRED',\n\n // Issuer errors (403 - Forbidden)\n ISSUER_NOT_ALLOWED: 'E_ISSUER_NOT_ALLOWED',\n\n // Configuration errors (500 - Server misconfiguration)\n CONFIG_ISSUER_ALLOWLIST_REQUIRED: 'E_CONFIG_ISSUER_ALLOWLIST_REQUIRED',\n\n // Internal errors (500)\n INTERNAL_ERROR: 'E_INTERNAL_ERROR',\n} as const;\n\n/**\n * PEAC error code type (union of all canonical error codes).\n *\n * Use this type for compile-time safety when handling error codes.\n */\nexport type PeacErrorCode = (typeof CANONICAL_ERROR_CODES)[keyof typeof CANONICAL_ERROR_CODES];\n","/**\n * Internal error code utilities.\n *\n * @internal - Not part of public API. Consumers should import from `@peac/contracts`.\n */\n\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from '../codes.js';\n\n/**\n * Set of all canonical PEAC error codes for O(1) validation.\n *\n * Typed as ReadonlySet to prevent accidental mutation.\n *\n * @internal\n */\nconst VALUES = Object.values(CANONICAL_ERROR_CODES) as PeacErrorCode[];\nexport const PEAC_ERROR_CODE_SET: ReadonlySet<PeacErrorCode> = new Set(VALUES);\n","/**\n * PEAC Canonical Contracts\n *\n * Single source of truth for error codes, HTTP status mappings, and verification mode behavior.\n * All surface implementations MUST import from this package to prevent drift.\n *\n * @packageDocumentation\n */\n\n// Re-export canonical error codes from internal module (avoids circular dependencies)\nexport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\n\n/**\n * Base URI for RFC 9457 Problem Details type field.\n */\nexport const PROBLEM_TYPE_BASE = 'https://www.peacprotocol.org/problems';\n\n/**\n * Canonical HTTP status code mappings.\n *\n * Status code semantics:\n * - 400: Client error (malformed request, invalid parameters)\n * - 401: Authentication required (missing/invalid credentials)\n * - 402: Payment Required (reserved for PEAC receipt payment flows)\n * - 403: Forbidden (authenticated but not authorized)\n * - 409: Conflict (replay detection - request conflicts with previous state)\n * - 500: Server error (configuration, internal failure)\n */\nexport const CANONICAL_STATUS_MAPPINGS: Record<PeacErrorCode, number> = {\n // 402 - Payment Required (reserved for payment flows)\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 402,\n\n // 401 - Authentication errors\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 401,\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 401,\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 401,\n\n // 400 - Client errors (malformed)\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 400,\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 400,\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 400,\n\n // 403 - Forbidden (issuer not in allowlist)\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 403,\n\n // 409 - Conflict (replay detected)\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 409,\n\n // 500 - Server errors\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 500,\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 500,\n};\n\n/**\n * Canonical error titles for RFC 9457 Problem Details.\n */\nexport const CANONICAL_TITLES: Record<PeacErrorCode, string> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 'Payment Required',\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 'Invalid Receipt',\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 'Receipt Expired',\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 'Signature Missing',\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 'Invalid Signature',\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 'Invalid Signature Time',\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 'Signature Window Too Large',\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 'Unknown TAP Tag',\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 'Invalid Algorithm',\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 'Key Not Found',\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 'Nonce Replay Detected',\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 'Replay Protection Required',\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 'Issuer Not Allowed',\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 'Configuration Error',\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 'Internal Server Error',\n};\n\n/**\n * Verification mode.\n *\n * Determines behavior when TAP headers are missing:\n * - tap_only: Missing TAP → 401 + E_TAP_SIGNATURE_MISSING (authentication required)\n * - receipt_or_tap: Missing TAP → 402 + E_RECEIPT_MISSING (payment remedy)\n */\nexport type VerificationMode = 'tap_only' | 'receipt_or_tap';\n\n/**\n * Handler action type.\n *\n * Determines how the handler should respond:\n * - error: Authentication/authorization error\n * - challenge: Payment challenge (402 only)\n * - pass: Bypass verification (bypass paths)\n * - forward: Verification succeeded, forward request\n */\nexport type HandlerAction = 'error' | 'challenge' | 'pass' | 'forward';\n\n/**\n * Mode behavior for missing TAP headers.\n */\nexport interface ModeBehavior {\n /** HTTP status code */\n status: number;\n /** PEAC error code */\n code: PeacErrorCode;\n /** Handler action */\n action: HandlerAction;\n}\n\n/**\n * Verification mode behavior contract.\n *\n * Defines expected HTTP status, error code, and action when no TAP headers are present.\n * This is the canonical source of truth for mode-based behavior.\n */\nexport const MODE_BEHAVIOR: Record<VerificationMode, ModeBehavior> = {\n receipt_or_tap: {\n status: 402,\n code: CANONICAL_ERROR_CODES.RECEIPT_MISSING,\n action: 'challenge',\n },\n tap_only: {\n status: 401,\n code: CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING,\n action: 'error',\n },\n} as const;\n\n/**\n * HTTP status codes that require WWW-Authenticate header.\n *\n * Per v0.9.27 decision: WWW-Authenticate is sent on both 401 (auth required) and 402 (payment remedy).\n */\nexport const WWW_AUTHENTICATE_STATUSES = [401, 402] as const;\n\n/**\n * Get RFC 9457 Problem Details type URI for an error code.\n *\n * @param code - PEAC error code\n * @returns Problem Details type URI (e.g., \"https://www.peacprotocol.org/problems/E_TAP_SIGNATURE_MISSING\")\n */\nexport function problemTypeFor(code: PeacErrorCode): string {\n return `${PROBLEM_TYPE_BASE}/${code}`;\n}\n\n/**\n * Error catalog entry.\n */\nexport interface ErrorCatalogEntry {\n /** HTTP status code */\n status: number;\n /** RFC 9457 title */\n title: string;\n /** Default detail message (can be overridden at runtime) */\n defaultDetail?: string;\n}\n\n/**\n * Error catalog with HTTP status, title, and default detail for each error code.\n *\n * Use this for constructing RFC 9457 Problem Details responses.\n */\nexport const ERROR_CATALOG: Record<PeacErrorCode, ErrorCatalogEntry> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n defaultDetail: 'A valid PEAC receipt is required to access this resource.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n defaultDetail: 'The provided receipt is invalid.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n defaultDetail: 'The provided receipt has expired.',\n },\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n defaultDetail: 'TAP signature headers are required.',\n },\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n defaultDetail: 'TAP signature verification failed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n defaultDetail: 'TAP signature time is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n defaultDetail: 'TAP signature time window exceeds maximum allowed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n defaultDetail: 'Unknown TAP tag in signature.',\n },\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n defaultDetail: 'TAP signature algorithm is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n defaultDetail: 'TAP signing key not found.',\n },\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n defaultDetail: 'Nonce replay detected.',\n },\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n defaultDetail:\n 'Replay protection required but not configured. Set UNSAFE_ALLOW_NO_REPLAY=true to bypass (UNSAFE for production).',\n },\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n defaultDetail: 'Issuer not in allowlist.',\n },\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n defaultDetail:\n 'Worker misconfigured: ISSUER_ALLOWLIST is required. Set UNSAFE_ALLOW_ANY_ISSUER=true to bypass (UNSAFE for production).',\n },\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n defaultDetail: 'An internal server error occurred.',\n },\n};\n\n/**\n * Get HTTP status code for a PEAC error code.\n *\n * @param code - PEAC error code\n * @returns HTTP status code\n */\nexport function getStatusForCode(code: PeacErrorCode): number {\n return CANONICAL_STATUS_MAPPINGS[code];\n}\n\n/**\n * Check if HTTP status requires WWW-Authenticate header.\n *\n * @param status - HTTP status code\n * @returns true if WWW-Authenticate header is required\n */\nexport function requiresWwwAuthenticate(status: number): boolean {\n return WWW_AUTHENTICATE_STATUSES.includes(status as 401 | 402);\n}\n\n/**\n * Build canonical WWW-Authenticate header value.\n *\n * Format: PEAC realm=\"<realm>\", error=\"<code>\", error_uri=\"<uri>\"\n *\n * @param code - PEAC error code\n * @param realm - Authentication realm (default: \"peac\")\n * @returns Canonical WWW-Authenticate header value\n */\nexport function buildWwwAuthenticate(code: PeacErrorCode, realm = 'peac'): string {\n return `PEAC realm=\"${realm}\", error=\"${code}\", error_uri=\"${problemTypeFor(code)}\"`;\n}\n\n/**\n * Valid PEAC HTTP status codes.\n *\n * All status codes that can be returned by PEAC verification handlers.\n */\nexport type PeacHttpStatus = 400 | 401 | 402 | 403 | 409 | 500;\n\n// Internal Set import - NOT re-exported\nimport { PEAC_ERROR_CODE_SET } from './internal/error-codes.js';\n\n/**\n * Type guard to check if a value is a valid PEAC error code.\n *\n * Uses O(1) Set lookup for performance.\n *\n * @param x - Value to check\n * @returns true if x is a valid PeacErrorCode\n *\n * @example\n * ```typescript\n * if (isPeacErrorCode(code)) {\n * // TypeScript now knows code is PeacErrorCode\n * const status = getStatusForCode(code);\n * }\n * ```\n */\nexport function isPeacErrorCode(x: unknown): x is PeacErrorCode {\n return typeof x === 'string' && PEAC_ERROR_CODE_SET.has(x as PeacErrorCode);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/codes.ts","../src/issuer-origin.ts","../src/internal/error-codes.ts","../src/index.ts"],"names":[],"mappings":";;;AAcO,IAAM,qBAAA,GAAwB;AAAA;AAAA,EAEnC,eAAA,EAAiB,mBAAA;AAAA,EACjB,eAAA,EAAiB,mBAAA;AAAA,EACjB,eAAA,EAAiB,mBAAA;AAAA;AAAA,EAGjB,qBAAA,EAAuB,yBAAA;AAAA,EACvB,qBAAA,EAAuB,yBAAA;AAAA,EACvB,gBAAA,EAAkB,oBAAA;AAAA,EAClB,oBAAA,EAAsB,wBAAA;AAAA,EACtB,eAAA,EAAiB,mBAAA;AAAA,EACjB,qBAAA,EAAuB,yBAAA;AAAA,EACvB,iBAAA,EAAmB,qBAAA;AAAA,EACnB,iBAAA,EAAmB,qBAAA;AAAA;AAAA,EAGnB,gBAAA,EAAkB,oBAAA;AAAA;AAAA,EAGlB,8BAAA,EAAgC,kCAAA;AAAA;AAAA,EAGhC,kBAAA,EAAoB,sBAAA;AAAA;AAAA,EAGpB,gCAAA,EAAkC,oCAAA;AAAA;AAAA,EAGlC,cAAA,EAAgB;AAClB;;;AC3BA,SAAS,gBAAgB,KAAA,EAAwB;AAC/C,EAAA,KAAA,IAAS,QAAQ,CAAA,EAAG,KAAA,GAAQ,KAAA,CAAM,MAAA,EAAQ,SAAS,CAAA,EAAG;AACpD,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,UAAA,CAAW,KAAK,CAAA;AACnC,IAAA,IAAI,IAAA,IAAQ,EAAA,IAAQ,IAAA,KAAS,GAAA,EAAM;AACjC,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AASA,SAAS,cAAc,KAAA,EAA8B;AAInD,EAAA,IAAI,MAAM,IAAA,EAAK,KAAM,KAAA,IAAS,eAAA,CAAgB,KAAK,CAAA,EAAG;AACpD,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,IAAI,IAAI,KAAK,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,GAAA,CAAI,aAAa,QAAA,EAAU;AAC7B,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,GAAA,CAAI,QAAA,KAAa,EAAA,IAAM,GAAA,CAAI,aAAa,EAAA,EAAI;AAC9C,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,GAAA,CAAI,MAAA;AACb;AAmBO,SAAS,qBAAA,CAAsB,WAAmB,SAAA,EAAuC;AAC9F,EAAA,MAAM,eAAA,GAAkB,cAAc,SAAS,CAAA;AAC/C,EAAA,IAAI,oBAAoB,IAAA,EAAM;AAC5B,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,KAAA,MAAW,SAAS,SAAA,EAAW;AAC7B,IAAA,IAAI,aAAA,CAAc,KAAK,CAAA,KAAM,eAAA,EAAiB;AAC5C,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;;;ACrEA,IAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,qBAAqB,CAAA;AAC3C,IAAM,mBAAA,GAAkD,IAAI,GAAA,CAAI,MAAM,CAAA;;;ACGtE,IAAM,iBAAA,GAAoB;AAa1B,IAAM,yBAAA,GAA2D;AAAA;AAAA,EAEtE,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA;AAAA,EAGzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,GAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,GAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,GAAA;AAAA,EAC1C,CAAC,qBAAA,CAAsB,iBAAiB,GAAG,GAAA;AAAA,EAC3C,CAAC,qBAAA,CAAsB,iBAAiB,GAAG,GAAA;AAAA,EAC3C,CAAC,qBAAA,CAAsB,8BAA8B,GAAG,GAAA;AAAA;AAAA,EAGxD,CAAC,qBAAA,CAAsB,oBAAoB,GAAG,GAAA;AAAA,EAC9C,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,GAAA;AAAA;AAAA,EAG/C,CAAC,qBAAA,CAAsB,kBAAkB,GAAG,GAAA;AAAA;AAAA,EAG5C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,GAAA;AAAA;AAAA,EAG1C,CAAC,qBAAA,CAAsB,gCAAgC,GAAG,GAAA;AAAA,EAC1D,CAAC,qBAAA,CAAsB,cAAc,GAAG;AAC1C;AAKO,IAAM,gBAAA,GAAkD;AAAA,EAC7D,CAAC,qBAAA,CAAsB,eAAe,GAAG,kBAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,iBAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,iBAAA;AAAA,EAEzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,mBAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,mBAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,wBAAA;AAAA,EAC1C,CAAC,qBAAA,CAAsB,oBAAoB,GAAG,4BAAA;AAAA,EAC9C,CAAC,qBAAA,CAAsB,eAAe,GAAG,iBAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,mBAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,iBAAiB,GAAG,eAAA;AAAA,EAC3C,CAAC,qBAAA,CAAsB,iBAAiB,GAAG,gBAAA;AAAA,EAC3C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,uBAAA;AAAA,EAC1C,CAAC,qBAAA,CAAsB,8BAA8B,GAAG,4BAAA;AAAA,EAExD,CAAC,qBAAA,CAAsB,kBAAkB,GAAG,oBAAA;AAAA,EAE5C,CAAC,qBAAA,CAAsB,gCAAgC,GAAG,qBAAA;AAAA,EAC1D,CAAC,qBAAA,CAAsB,cAAc,GAAG;AAC1C;AAwCO,IAAM,aAAA,GAAwD;AAAA,EACnE,cAAA,EAAgB;AAAA,IACd,MAAA,EAAQ,GAAA;AAAA,IACR,MAAM,qBAAA,CAAsB,eAAA;AAAA,IAC5B,MAAA,EAAQ;AAAA,GACV;AAAA,EACA,QAAA,EAAU;AAAA,IACR,MAAA,EAAQ,GAAA;AAAA,IACR,MAAM,qBAAA,CAAsB,qBAAA;AAAA,IAC5B,MAAA,EAAQ;AAAA;AAEZ;AAOO,IAAM,yBAAA,GAA4B,CAAC,GAAA,EAAK,GAAG;AAQ3C,SAAS,eAAe,IAAA,EAA6B;AAC1D,EAAA,OAAO,CAAA,EAAG,iBAAiB,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AACrC;AAmBO,IAAM,aAAA,GAA0D;AAAA,EACrE,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EAEA,CAAC,qBAAA,CAAsB,qBAAqB,GAAG;AAAA,IAC7C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IAC7E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IACnE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,qBAAqB,GAAG;AAAA,IAC7C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IAC7E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IACnE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,gBAAgB,GAAG;AAAA,IACxC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IACxE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IAC9D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,oBAAoB,GAAG;AAAA,IAC5C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,oBAAoB,CAAA;AAAA,IAC5E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,oBAAoB,CAAA;AAAA,IAClE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,qBAAqB,GAAG;AAAA,IAC7C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IAC7E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IACnE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,iBAAiB,GAAG;AAAA,IACzC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,iBAAiB,CAAA;AAAA,IACzE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,iBAAiB,CAAA;AAAA,IAC/D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,iBAAiB,GAAG;AAAA,IACzC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,iBAAiB,CAAA;AAAA,IACzE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,iBAAiB,CAAA;AAAA,IAC/D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,gBAAgB,GAAG;AAAA,IACxC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IACxE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IAC9D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,8BAA8B,GAAG;AAAA,IACtD,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,8BAA8B,CAAA;AAAA,IACtF,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,8BAA8B,CAAA;AAAA,IAC5E,aAAA,EACE;AAAA,GACJ;AAAA,EAEA,CAAC,qBAAA,CAAsB,kBAAkB,GAAG;AAAA,IAC1C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,kBAAkB,CAAA;AAAA,IAC1E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,kBAAkB,CAAA;AAAA,IAChE,aAAA,EAAe;AAAA,GACjB;AAAA,EAEA,CAAC,qBAAA,CAAsB,gCAAgC,GAAG;AAAA,IACxD,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,gCAAgC,CAAA;AAAA,IACxF,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,gCAAgC,CAAA;AAAA,IAC9E,aAAA,EACE;AAAA,GACJ;AAAA,EACA,CAAC,qBAAA,CAAsB,cAAc,GAAG;AAAA,IACtC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,cAAc,CAAA;AAAA,IACtE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,cAAc,CAAA;AAAA,IAC5D,aAAA,EAAe;AAAA;AAEnB;AAQO,SAAS,iBAAiB,IAAA,EAA6B;AAC5D,EAAA,OAAO,0BAA0B,IAAI,CAAA;AACvC;AAQO,SAAS,wBAAwB,MAAA,EAAyB;AAC/D,EAAA,OAAO,yBAAA,CAA0B,SAAS,MAAmB,CAAA;AAC/D;AAWO,SAAS,oBAAA,CAAqB,IAAA,EAAqB,KAAA,GAAQ,MAAA,EAAgB;AAChF,EAAA,OAAO,eAAe,KAAK,CAAA,UAAA,EAAa,IAAI,CAAA,cAAA,EAAiB,cAAA,CAAe,IAAI,CAAC,CAAA,CAAA,CAAA;AACnF;AA4BO,SAAS,gBAAgB,CAAA,EAAgC;AAC9D,EAAA,OAAO,OAAO,CAAA,KAAM,QAAA,IAAY,mBAAA,CAAoB,IAAI,CAAkB,CAAA;AAC5E","file":"index.cjs","sourcesContent":["/**\n * Canonical PEAC error codes.\n *\n * This is the single source of truth for error code values.\n * All other modules import from here to avoid circular dependencies.\n */\n\n/**\n * Canonical PEAC error codes.\n *\n * Format: E_<CATEGORY>_<ERROR>\n *\n * All error codes use the E_ prefix for consistency with RFC/IETF error code conventions.\n */\nexport const CANONICAL_ERROR_CODES = {\n // Receipt errors (402 - Payment Required)\n RECEIPT_MISSING: 'E_RECEIPT_MISSING',\n RECEIPT_INVALID: 'E_RECEIPT_INVALID',\n RECEIPT_EXPIRED: 'E_RECEIPT_EXPIRED',\n\n // TAP errors (401 - Authentication)\n TAP_SIGNATURE_MISSING: 'E_TAP_SIGNATURE_MISSING',\n TAP_SIGNATURE_INVALID: 'E_TAP_SIGNATURE_INVALID',\n TAP_TIME_INVALID: 'E_TAP_TIME_INVALID',\n TAP_WINDOW_TOO_LARGE: 'E_TAP_WINDOW_TOO_LARGE',\n TAP_TAG_UNKNOWN: 'E_TAP_TAG_UNKNOWN',\n TAP_ALGORITHM_INVALID: 'E_TAP_ALGORITHM_INVALID',\n TAP_KEY_NOT_FOUND: 'E_TAP_KEY_NOT_FOUND',\n TAP_KEYID_INVALID: 'E_TAP_KEYID_INVALID',\n\n // Replay error (409 - Conflict)\n TAP_NONCE_REPLAY: 'E_TAP_NONCE_REPLAY',\n\n // Replay protection required (401 - requires config change)\n TAP_REPLAY_PROTECTION_REQUIRED: 'E_TAP_REPLAY_PROTECTION_REQUIRED',\n\n // Issuer errors (403 - Forbidden)\n ISSUER_NOT_ALLOWED: 'E_ISSUER_NOT_ALLOWED',\n\n // Configuration errors (500 - Server misconfiguration)\n CONFIG_ISSUER_ALLOWLIST_REQUIRED: 'E_CONFIG_ISSUER_ALLOWLIST_REQUIRED',\n\n // Internal errors (500)\n INTERNAL_ERROR: 'E_INTERNAL_ERROR',\n} as const;\n\n/**\n * PEAC error code type (union of all canonical error codes).\n *\n * Use this type for compile-time safety when handling error codes.\n */\nexport type PeacErrorCode = (typeof CANONICAL_ERROR_CODES)[keyof typeof CANONICAL_ERROR_CODES];\n","/**\n * Canonical issuer-origin allowlist check.\n *\n * Surfaces use this helper to decide whether a receipt or TAP issuer origin is\n * configured as trusted. The comparison is intentionally origin-based\n * (scheme + host + port), not host-only, and fails closed for malformed inputs.\n *\n * Runtime-neutral by construction: it uses only the standard URL API and runs\n * unchanged on Node, Workers, Deno, and Bun. It performs no DNS resolution, no\n * network access, and no private-IP checks; outbound-fetch SSRF protection is a\n * separate fetch-layer concern.\n */\n\n// True if the string contains an ASCII control character (C0 range or DEL).\n// Used to reject smuggled control characters that the URL API might otherwise\n// strip during parsing. Implemented as a code-unit scan rather than a regex\n// escape range to keep this security-sensitive source free of escape sequences.\nfunction hasAsciiControl(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code <= 0x1f || code === 0x7f) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Normalize a value to its https origin, or null if it is not an acceptable\n * issuer origin. Acceptable means: no surrounding or embedded ASCII control\n * characters or whitespace, a parseable absolute URL, the `https:` scheme, and\n * no userinfo (username/password). Path, query, and fragment are not part of an\n * origin and are ignored.\n */\nfunction toHttpsOrigin(value: string): string | null {\n // Reject surrounding whitespace and embedded ASCII control characters before\n // parsing. The URL API would otherwise strip some of these, which could let a\n // padded or smuggled-control-character entry compare equal to a clean one.\n if (value.trim() !== value || hasAsciiControl(value)) {\n return null;\n }\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return null;\n }\n if (url.protocol !== 'https:') {\n return null;\n }\n if (url.username !== '' || url.password !== '') {\n return null;\n }\n return url.origin;\n}\n\n/**\n * Returns true iff `candidate` matches an entry in `allowlist` by https origin.\n *\n * Comparison is by URL origin (scheme + host + port); a default `:443` is\n * normalized away by the `URL` API, a non-default port must match exactly, and\n * a scheme mismatch (e.g. an `http:` candidate against an `https:` entry) never\n * matches.\n *\n * Fails closed:\n * - an empty allowlist returns false;\n * - a candidate that is not a parseable absolute https URL, or that carries\n * userinfo or control characters, returns false;\n * - a malformed or non-https allowlist entry is skipped (it never matches) but\n * does not prevent a later valid entry from matching.\n *\n * There is no wildcard, suffix, regex, or raw-string fallback matching.\n */\nexport function isAllowedIssuerOrigin(candidate: string, allowlist: readonly string[]): boolean {\n const candidateOrigin = toHttpsOrigin(candidate);\n if (candidateOrigin === null) {\n return false;\n }\n for (const entry of allowlist) {\n if (toHttpsOrigin(entry) === candidateOrigin) {\n return true;\n }\n }\n return false;\n}\n","/**\n * Internal error code utilities.\n *\n * @internal - Not part of public API. Consumers should import from `@peac/contracts`.\n */\n\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from '../codes.js';\n\n/**\n * Set of all canonical PEAC error codes for O(1) validation.\n *\n * Typed as ReadonlySet to prevent accidental mutation.\n *\n * @internal\n */\nconst VALUES = Object.values(CANONICAL_ERROR_CODES) as PeacErrorCode[];\nexport const PEAC_ERROR_CODE_SET: ReadonlySet<PeacErrorCode> = new Set(VALUES);\n","/**\n * PEAC Canonical Contracts\n *\n * Single source of truth for error codes, HTTP status mappings, and verification mode behavior.\n * All surface implementations MUST import from this package to prevent drift.\n *\n * @packageDocumentation\n */\n\n// Re-export canonical error codes from internal module (avoids circular dependencies)\nexport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\n\n// Canonical issuer-origin allowlist check shared across surfaces.\nexport { isAllowedIssuerOrigin } from './issuer-origin.js';\n\n/**\n * Base URI for RFC 9457 Problem Details type field.\n */\nexport const PROBLEM_TYPE_BASE = 'https://www.peacprotocol.org/problems';\n\n/**\n * Canonical HTTP status code mappings.\n *\n * Status code semantics:\n * - 400: Client error (malformed request, invalid parameters)\n * - 401: Authentication required (missing/invalid credentials)\n * - 402: Payment Required (reserved for PEAC receipt payment flows)\n * - 403: Forbidden (authenticated but not authorized)\n * - 409: Conflict (replay detection - request conflicts with previous state)\n * - 500: Server error (configuration, internal failure)\n */\nexport const CANONICAL_STATUS_MAPPINGS: Record<PeacErrorCode, number> = {\n // 402 - Payment Required (reserved for payment flows)\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 402,\n\n // 401 - Authentication errors\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 401,\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 401,\n [CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 401,\n\n // 400 - Client errors (malformed)\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 400,\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 400,\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 400,\n\n // 403 - Forbidden (issuer not in allowlist)\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 403,\n\n // 409 - Conflict (replay detected)\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 409,\n\n // 500 - Server errors\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 500,\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 500,\n};\n\n/**\n * Canonical error titles for RFC 9457 Problem Details.\n */\nexport const CANONICAL_TITLES: Record<PeacErrorCode, string> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 'Payment Required',\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 'Invalid Receipt',\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 'Receipt Expired',\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 'Signature Missing',\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 'Invalid Signature',\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 'Invalid Signature Time',\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 'Signature Window Too Large',\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 'Unknown TAP Tag',\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 'Invalid Algorithm',\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 'Key Not Found',\n [CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: 'Invalid Key ID',\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 'Nonce Replay Detected',\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 'Replay Protection Required',\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 'Issuer Not Allowed',\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 'Configuration Error',\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 'Internal Server Error',\n};\n\n/**\n * Verification mode.\n *\n * Determines behavior when TAP headers are missing:\n * - tap_only: Missing TAP → 401 + E_TAP_SIGNATURE_MISSING (authentication required)\n * - receipt_or_tap: Missing TAP → 402 + E_RECEIPT_MISSING (payment remedy)\n */\nexport type VerificationMode = 'tap_only' | 'receipt_or_tap';\n\n/**\n * Handler action type.\n *\n * Determines how the handler should respond:\n * - error: Authentication/authorization error\n * - challenge: Payment challenge (402 only)\n * - pass: Bypass verification (bypass paths)\n * - forward: Verification succeeded, forward request\n */\nexport type HandlerAction = 'error' | 'challenge' | 'pass' | 'forward';\n\n/**\n * Mode behavior for missing TAP headers.\n */\nexport interface ModeBehavior {\n /** HTTP status code */\n status: number;\n /** PEAC error code */\n code: PeacErrorCode;\n /** Handler action */\n action: HandlerAction;\n}\n\n/**\n * Verification mode behavior contract.\n *\n * Defines expected HTTP status, error code, and action when no TAP headers are present.\n * This is the canonical source of truth for mode-based behavior.\n */\nexport const MODE_BEHAVIOR: Record<VerificationMode, ModeBehavior> = {\n receipt_or_tap: {\n status: 402,\n code: CANONICAL_ERROR_CODES.RECEIPT_MISSING,\n action: 'challenge',\n },\n tap_only: {\n status: 401,\n code: CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING,\n action: 'error',\n },\n} as const;\n\n/**\n * HTTP status codes that require WWW-Authenticate header.\n *\n * Per v0.9.27 decision: WWW-Authenticate is sent on both 401 (auth required) and 402 (payment remedy).\n */\nexport const WWW_AUTHENTICATE_STATUSES = [401, 402] as const;\n\n/**\n * Get RFC 9457 Problem Details type URI for an error code.\n *\n * @param code - PEAC error code\n * @returns Problem Details type URI (e.g., \"https://www.peacprotocol.org/problems/E_TAP_SIGNATURE_MISSING\")\n */\nexport function problemTypeFor(code: PeacErrorCode): string {\n return `${PROBLEM_TYPE_BASE}/${code}`;\n}\n\n/**\n * Error catalog entry.\n */\nexport interface ErrorCatalogEntry {\n /** HTTP status code */\n status: number;\n /** RFC 9457 title */\n title: string;\n /** Default detail message (can be overridden at runtime) */\n defaultDetail?: string;\n}\n\n/**\n * Error catalog with HTTP status, title, and default detail for each error code.\n *\n * Use this for constructing RFC 9457 Problem Details responses.\n */\nexport const ERROR_CATALOG: Record<PeacErrorCode, ErrorCatalogEntry> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n defaultDetail: 'A valid PEAC receipt is required to access this resource.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n defaultDetail: 'The provided receipt is invalid.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n defaultDetail: 'The provided receipt has expired.',\n },\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n defaultDetail: 'TAP signature headers are required.',\n },\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n defaultDetail: 'TAP signature verification failed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n defaultDetail: 'TAP signature time is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n defaultDetail: 'TAP signature time window exceeds maximum allowed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n defaultDetail: 'Unknown TAP tag in signature.',\n },\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n defaultDetail: 'TAP signature algorithm is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n defaultDetail: 'TAP signing key not found.',\n },\n [CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],\n defaultDetail: 'TAP keyid must be an absolute https URL identifying the issuer.',\n },\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n defaultDetail: 'Nonce replay detected.',\n },\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n defaultDetail:\n 'Replay protection required but not configured. Set UNSAFE_ALLOW_NO_REPLAY=true to bypass (UNSAFE for production).',\n },\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n defaultDetail: 'Issuer not in allowlist.',\n },\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n defaultDetail:\n 'Worker misconfigured: ISSUER_ALLOWLIST is required. Set UNSAFE_ALLOW_ANY_ISSUER=true to bypass (UNSAFE for production).',\n },\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n defaultDetail: 'An internal server error occurred.',\n },\n};\n\n/**\n * Get HTTP status code for a PEAC error code.\n *\n * @param code - PEAC error code\n * @returns HTTP status code\n */\nexport function getStatusForCode(code: PeacErrorCode): number {\n return CANONICAL_STATUS_MAPPINGS[code];\n}\n\n/**\n * Check if HTTP status requires WWW-Authenticate header.\n *\n * @param status - HTTP status code\n * @returns true if WWW-Authenticate header is required\n */\nexport function requiresWwwAuthenticate(status: number): boolean {\n return WWW_AUTHENTICATE_STATUSES.includes(status as 401 | 402);\n}\n\n/**\n * Build canonical WWW-Authenticate header value.\n *\n * Format: PEAC realm=\"<realm>\", error=\"<code>\", error_uri=\"<uri>\"\n *\n * @param code - PEAC error code\n * @param realm - Authentication realm (default: \"peac\")\n * @returns Canonical WWW-Authenticate header value\n */\nexport function buildWwwAuthenticate(code: PeacErrorCode, realm = 'peac'): string {\n return `PEAC realm=\"${realm}\", error=\"${code}\", error_uri=\"${problemTypeFor(code)}\"`;\n}\n\n/**\n * Valid PEAC HTTP status codes.\n *\n * All status codes that can be returned by PEAC verification handlers.\n */\nexport type PeacHttpStatus = 400 | 401 | 402 | 403 | 409 | 500;\n\n// Internal Set import - NOT re-exported\nimport { PEAC_ERROR_CODE_SET } from './internal/error-codes.js';\n\n/**\n * Type guard to check if a value is a valid PEAC error code.\n *\n * Uses O(1) Set lookup for performance.\n *\n * @param x - Value to check\n * @returns true if x is a valid PeacErrorCode\n *\n * @example\n * ```typescript\n * if (isPeacErrorCode(code)) {\n * // TypeScript now knows code is PeacErrorCode\n * const status = getStatusForCode(code);\n * }\n * ```\n */\nexport function isPeacErrorCode(x: unknown): x is PeacErrorCode {\n return typeof x === 'string' && PEAC_ERROR_CODE_SET.has(x as PeacErrorCode);\n}\n"]}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAGvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,iBAAiB,0CAA0C,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CA4BnE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAoB1D,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,qBAAqB;IACrB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,gBAAgB,EAAE,YAAY,CAWvD,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,qBAAsB,CAAC;AAE7D;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,iBAAiB,CAsFlE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,SAAS,GAAG,MAAM,CAEhF;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAK/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAE9D"}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ var CANONICAL_ERROR_CODES = {
|
|
|
12
12
|
TAP_TAG_UNKNOWN: "E_TAP_TAG_UNKNOWN",
|
|
13
13
|
TAP_ALGORITHM_INVALID: "E_TAP_ALGORITHM_INVALID",
|
|
14
14
|
TAP_KEY_NOT_FOUND: "E_TAP_KEY_NOT_FOUND",
|
|
15
|
+
TAP_KEYID_INVALID: "E_TAP_KEYID_INVALID",
|
|
15
16
|
// Replay error (409 - Conflict)
|
|
16
17
|
TAP_NONCE_REPLAY: "E_TAP_NONCE_REPLAY",
|
|
17
18
|
// Replay protection required (401 - requires config change)
|
|
@@ -24,6 +25,47 @@ var CANONICAL_ERROR_CODES = {
|
|
|
24
25
|
INTERNAL_ERROR: "E_INTERNAL_ERROR"
|
|
25
26
|
};
|
|
26
27
|
|
|
28
|
+
// src/issuer-origin.ts
|
|
29
|
+
function hasAsciiControl(value) {
|
|
30
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
31
|
+
const code = value.charCodeAt(index);
|
|
32
|
+
if (code <= 31 || code === 127) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
function toHttpsOrigin(value) {
|
|
39
|
+
if (value.trim() !== value || hasAsciiControl(value)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
let url;
|
|
43
|
+
try {
|
|
44
|
+
url = new URL(value);
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (url.protocol !== "https:") {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (url.username !== "" || url.password !== "") {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return url.origin;
|
|
55
|
+
}
|
|
56
|
+
function isAllowedIssuerOrigin(candidate, allowlist) {
|
|
57
|
+
const candidateOrigin = toHttpsOrigin(candidate);
|
|
58
|
+
if (candidateOrigin === null) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
for (const entry of allowlist) {
|
|
62
|
+
if (toHttpsOrigin(entry) === candidateOrigin) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
27
69
|
// src/internal/error-codes.ts
|
|
28
70
|
var VALUES = Object.values(CANONICAL_ERROR_CODES);
|
|
29
71
|
var PEAC_ERROR_CODE_SET = new Set(VALUES);
|
|
@@ -40,6 +82,7 @@ var CANONICAL_STATUS_MAPPINGS = {
|
|
|
40
82
|
[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 401,
|
|
41
83
|
[CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 401,
|
|
42
84
|
[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 401,
|
|
85
|
+
[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: 401,
|
|
43
86
|
[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 401,
|
|
44
87
|
// 400 - Client errors (malformed)
|
|
45
88
|
[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 400,
|
|
@@ -64,6 +107,7 @@ var CANONICAL_TITLES = {
|
|
|
64
107
|
[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: "Unknown TAP Tag",
|
|
65
108
|
[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: "Invalid Algorithm",
|
|
66
109
|
[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: "Key Not Found",
|
|
110
|
+
[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: "Invalid Key ID",
|
|
67
111
|
[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: "Nonce Replay Detected",
|
|
68
112
|
[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: "Replay Protection Required",
|
|
69
113
|
[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: "Issuer Not Allowed",
|
|
@@ -137,6 +181,11 @@ var ERROR_CATALOG = {
|
|
|
137
181
|
title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],
|
|
138
182
|
defaultDetail: "TAP signing key not found."
|
|
139
183
|
},
|
|
184
|
+
[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: {
|
|
185
|
+
status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],
|
|
186
|
+
title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],
|
|
187
|
+
defaultDetail: "TAP keyid must be an absolute https URL identifying the issuer."
|
|
188
|
+
},
|
|
140
189
|
[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: {
|
|
141
190
|
status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],
|
|
142
191
|
title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],
|
|
@@ -176,6 +225,6 @@ function isPeacErrorCode(x) {
|
|
|
176
225
|
return typeof x === "string" && PEAC_ERROR_CODE_SET.has(x);
|
|
177
226
|
}
|
|
178
227
|
|
|
179
|
-
export { CANONICAL_ERROR_CODES, CANONICAL_STATUS_MAPPINGS, CANONICAL_TITLES, ERROR_CATALOG, MODE_BEHAVIOR, PROBLEM_TYPE_BASE, WWW_AUTHENTICATE_STATUSES, buildWwwAuthenticate, getStatusForCode, isPeacErrorCode, problemTypeFor, requiresWwwAuthenticate };
|
|
228
|
+
export { CANONICAL_ERROR_CODES, CANONICAL_STATUS_MAPPINGS, CANONICAL_TITLES, ERROR_CATALOG, MODE_BEHAVIOR, PROBLEM_TYPE_BASE, WWW_AUTHENTICATE_STATUSES, buildWwwAuthenticate, getStatusForCode, isAllowedIssuerOrigin, isPeacErrorCode, problemTypeFor, requiresWwwAuthenticate };
|
|
180
229
|
//# sourceMappingURL=index.js.map
|
|
181
230
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/codes.ts","../src/internal/error-codes.ts","../src/index.ts"],"names":[],"mappings":";AAcO,IAAM,qBAAA,GAAwB;AAAA;AAAA,EAEnC,eAAA,EAAiB,mBAAA;AAAA,EACjB,eAAA,EAAiB,mBAAA;AAAA,EACjB,eAAA,EAAiB,mBAAA;AAAA;AAAA,EAGjB,qBAAA,EAAuB,yBAAA;AAAA,EACvB,qBAAA,EAAuB,yBAAA;AAAA,EACvB,gBAAA,EAAkB,oBAAA;AAAA,EAClB,oBAAA,EAAsB,wBAAA;AAAA,EACtB,eAAA,EAAiB,mBAAA;AAAA,EACjB,qBAAA,EAAuB,yBAAA;AAAA,EACvB,iBAAA,EAAmB,qBAAA;AAAA;AAAA,EAGnB,gBAAA,EAAkB,oBAAA;AAAA;AAAA,EAGlB,8BAAA,EAAgC,kCAAA;AAAA;AAAA,EAGhC,kBAAA,EAAoB,sBAAA;AAAA;AAAA,EAGpB,gCAAA,EAAkC,oCAAA;AAAA;AAAA,EAGlC,cAAA,EAAgB;AAClB;;;AC5BA,IAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,qBAAqB,CAAA;AAC3C,IAAM,mBAAA,GAAkD,IAAI,GAAA,CAAI,MAAM,CAAA;;;ACAtE,IAAM,iBAAA,GAAoB;AAa1B,IAAM,yBAAA,GAA2D;AAAA;AAAA,EAEtE,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA;AAAA,EAGzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,GAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,GAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,GAAA;AAAA,EAC1C,CAAC,qBAAA,CAAsB,iBAAiB,GAAG,GAAA;AAAA,EAC3C,CAAC,qBAAA,CAAsB,8BAA8B,GAAG,GAAA;AAAA;AAAA,EAGxD,CAAC,qBAAA,CAAsB,oBAAoB,GAAG,GAAA;AAAA,EAC9C,CAAC,qBAAA,CAAsB,eAAe,GAAG,GAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,GAAA;AAAA;AAAA,EAG/C,CAAC,qBAAA,CAAsB,kBAAkB,GAAG,GAAA;AAAA;AAAA,EAG5C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,GAAA;AAAA;AAAA,EAG1C,CAAC,qBAAA,CAAsB,gCAAgC,GAAG,GAAA;AAAA,EAC1D,CAAC,qBAAA,CAAsB,cAAc,GAAG;AAC1C;AAKO,IAAM,gBAAA,GAAkD;AAAA,EAC7D,CAAC,qBAAA,CAAsB,eAAe,GAAG,kBAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,iBAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,eAAe,GAAG,iBAAA;AAAA,EAEzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,mBAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,mBAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,wBAAA;AAAA,EAC1C,CAAC,qBAAA,CAAsB,oBAAoB,GAAG,4BAAA;AAAA,EAC9C,CAAC,qBAAA,CAAsB,eAAe,GAAG,iBAAA;AAAA,EACzC,CAAC,qBAAA,CAAsB,qBAAqB,GAAG,mBAAA;AAAA,EAC/C,CAAC,qBAAA,CAAsB,iBAAiB,GAAG,eAAA;AAAA,EAC3C,CAAC,qBAAA,CAAsB,gBAAgB,GAAG,uBAAA;AAAA,EAC1C,CAAC,qBAAA,CAAsB,8BAA8B,GAAG,4BAAA;AAAA,EAExD,CAAC,qBAAA,CAAsB,kBAAkB,GAAG,oBAAA;AAAA,EAE5C,CAAC,qBAAA,CAAsB,gCAAgC,GAAG,qBAAA;AAAA,EAC1D,CAAC,qBAAA,CAAsB,cAAc,GAAG;AAC1C;AAwCO,IAAM,aAAA,GAAwD;AAAA,EACnE,cAAA,EAAgB;AAAA,IACd,MAAA,EAAQ,GAAA;AAAA,IACR,MAAM,qBAAA,CAAsB,eAAA;AAAA,IAC5B,MAAA,EAAQ;AAAA,GACV;AAAA,EACA,QAAA,EAAU;AAAA,IACR,MAAA,EAAQ,GAAA;AAAA,IACR,MAAM,qBAAA,CAAsB,qBAAA;AAAA,IAC5B,MAAA,EAAQ;AAAA;AAEZ;AAOO,IAAM,yBAAA,GAA4B,CAAC,GAAA,EAAK,GAAG;AAQ3C,SAAS,eAAe,IAAA,EAA6B;AAC1D,EAAA,OAAO,CAAA,EAAG,iBAAiB,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AACrC;AAmBO,IAAM,aAAA,GAA0D;AAAA,EACrE,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EAEA,CAAC,qBAAA,CAAsB,qBAAqB,GAAG;AAAA,IAC7C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IAC7E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IACnE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,qBAAqB,GAAG;AAAA,IAC7C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IAC7E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IACnE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,gBAAgB,GAAG;AAAA,IACxC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IACxE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IAC9D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,oBAAoB,GAAG;AAAA,IAC5C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,oBAAoB,CAAA;AAAA,IAC5E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,oBAAoB,CAAA;AAAA,IAClE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,eAAe,GAAG;AAAA,IACvC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,eAAe,CAAA;AAAA,IACvE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,eAAe,CAAA;AAAA,IAC7D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,qBAAqB,GAAG;AAAA,IAC7C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IAC7E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,qBAAqB,CAAA;AAAA,IACnE,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,iBAAiB,GAAG;AAAA,IACzC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,iBAAiB,CAAA;AAAA,IACzE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,iBAAiB,CAAA;AAAA,IAC/D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,gBAAgB,GAAG;AAAA,IACxC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IACxE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,gBAAgB,CAAA;AAAA,IAC9D,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,CAAC,qBAAA,CAAsB,8BAA8B,GAAG;AAAA,IACtD,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,8BAA8B,CAAA;AAAA,IACtF,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,8BAA8B,CAAA;AAAA,IAC5E,aAAA,EACE;AAAA,GACJ;AAAA,EAEA,CAAC,qBAAA,CAAsB,kBAAkB,GAAG;AAAA,IAC1C,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,kBAAkB,CAAA;AAAA,IAC1E,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,kBAAkB,CAAA;AAAA,IAChE,aAAA,EAAe;AAAA,GACjB;AAAA,EAEA,CAAC,qBAAA,CAAsB,gCAAgC,GAAG;AAAA,IACxD,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,gCAAgC,CAAA;AAAA,IACxF,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,gCAAgC,CAAA;AAAA,IAC9E,aAAA,EACE;AAAA,GACJ;AAAA,EACA,CAAC,qBAAA,CAAsB,cAAc,GAAG;AAAA,IACtC,MAAA,EAAQ,yBAAA,CAA0B,qBAAA,CAAsB,cAAc,CAAA;AAAA,IACtE,KAAA,EAAO,gBAAA,CAAiB,qBAAA,CAAsB,cAAc,CAAA;AAAA,IAC5D,aAAA,EAAe;AAAA;AAEnB;AAQO,SAAS,iBAAiB,IAAA,EAA6B;AAC5D,EAAA,OAAO,0BAA0B,IAAI,CAAA;AACvC;AAQO,SAAS,wBAAwB,MAAA,EAAyB;AAC/D,EAAA,OAAO,yBAAA,CAA0B,SAAS,MAAmB,CAAA;AAC/D;AAWO,SAAS,oBAAA,CAAqB,IAAA,EAAqB,KAAA,GAAQ,MAAA,EAAgB;AAChF,EAAA,OAAO,eAAe,KAAK,CAAA,UAAA,EAAa,IAAI,CAAA,cAAA,EAAiB,cAAA,CAAe,IAAI,CAAC,CAAA,CAAA,CAAA;AACnF;AA4BO,SAAS,gBAAgB,CAAA,EAAgC;AAC9D,EAAA,OAAO,OAAO,CAAA,KAAM,QAAA,IAAY,mBAAA,CAAoB,IAAI,CAAkB,CAAA;AAC5E","file":"index.js","sourcesContent":["/**\n * Canonical PEAC error codes.\n *\n * This is the single source of truth for error code values.\n * All other modules import from here to avoid circular dependencies.\n */\n\n/**\n * Canonical PEAC error codes.\n *\n * Format: E_<CATEGORY>_<ERROR>\n *\n * All error codes use the E_ prefix for consistency with RFC/IETF error code conventions.\n */\nexport const CANONICAL_ERROR_CODES = {\n // Receipt errors (402 - Payment Required)\n RECEIPT_MISSING: 'E_RECEIPT_MISSING',\n RECEIPT_INVALID: 'E_RECEIPT_INVALID',\n RECEIPT_EXPIRED: 'E_RECEIPT_EXPIRED',\n\n // TAP errors (401 - Authentication)\n TAP_SIGNATURE_MISSING: 'E_TAP_SIGNATURE_MISSING',\n TAP_SIGNATURE_INVALID: 'E_TAP_SIGNATURE_INVALID',\n TAP_TIME_INVALID: 'E_TAP_TIME_INVALID',\n TAP_WINDOW_TOO_LARGE: 'E_TAP_WINDOW_TOO_LARGE',\n TAP_TAG_UNKNOWN: 'E_TAP_TAG_UNKNOWN',\n TAP_ALGORITHM_INVALID: 'E_TAP_ALGORITHM_INVALID',\n TAP_KEY_NOT_FOUND: 'E_TAP_KEY_NOT_FOUND',\n\n // Replay error (409 - Conflict)\n TAP_NONCE_REPLAY: 'E_TAP_NONCE_REPLAY',\n\n // Replay protection required (401 - requires config change)\n TAP_REPLAY_PROTECTION_REQUIRED: 'E_TAP_REPLAY_PROTECTION_REQUIRED',\n\n // Issuer errors (403 - Forbidden)\n ISSUER_NOT_ALLOWED: 'E_ISSUER_NOT_ALLOWED',\n\n // Configuration errors (500 - Server misconfiguration)\n CONFIG_ISSUER_ALLOWLIST_REQUIRED: 'E_CONFIG_ISSUER_ALLOWLIST_REQUIRED',\n\n // Internal errors (500)\n INTERNAL_ERROR: 'E_INTERNAL_ERROR',\n} as const;\n\n/**\n * PEAC error code type (union of all canonical error codes).\n *\n * Use this type for compile-time safety when handling error codes.\n */\nexport type PeacErrorCode = (typeof CANONICAL_ERROR_CODES)[keyof typeof CANONICAL_ERROR_CODES];\n","/**\n * Internal error code utilities.\n *\n * @internal - Not part of public API. Consumers should import from `@peac/contracts`.\n */\n\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from '../codes.js';\n\n/**\n * Set of all canonical PEAC error codes for O(1) validation.\n *\n * Typed as ReadonlySet to prevent accidental mutation.\n *\n * @internal\n */\nconst VALUES = Object.values(CANONICAL_ERROR_CODES) as PeacErrorCode[];\nexport const PEAC_ERROR_CODE_SET: ReadonlySet<PeacErrorCode> = new Set(VALUES);\n","/**\n * PEAC Canonical Contracts\n *\n * Single source of truth for error codes, HTTP status mappings, and verification mode behavior.\n * All surface implementations MUST import from this package to prevent drift.\n *\n * @packageDocumentation\n */\n\n// Re-export canonical error codes from internal module (avoids circular dependencies)\nexport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\n\n/**\n * Base URI for RFC 9457 Problem Details type field.\n */\nexport const PROBLEM_TYPE_BASE = 'https://www.peacprotocol.org/problems';\n\n/**\n * Canonical HTTP status code mappings.\n *\n * Status code semantics:\n * - 400: Client error (malformed request, invalid parameters)\n * - 401: Authentication required (missing/invalid credentials)\n * - 402: Payment Required (reserved for PEAC receipt payment flows)\n * - 403: Forbidden (authenticated but not authorized)\n * - 409: Conflict (replay detection - request conflicts with previous state)\n * - 500: Server error (configuration, internal failure)\n */\nexport const CANONICAL_STATUS_MAPPINGS: Record<PeacErrorCode, number> = {\n // 402 - Payment Required (reserved for payment flows)\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 402,\n\n // 401 - Authentication errors\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 401,\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 401,\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 401,\n\n // 400 - Client errors (malformed)\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 400,\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 400,\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 400,\n\n // 403 - Forbidden (issuer not in allowlist)\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 403,\n\n // 409 - Conflict (replay detected)\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 409,\n\n // 500 - Server errors\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 500,\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 500,\n};\n\n/**\n * Canonical error titles for RFC 9457 Problem Details.\n */\nexport const CANONICAL_TITLES: Record<PeacErrorCode, string> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 'Payment Required',\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 'Invalid Receipt',\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 'Receipt Expired',\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 'Signature Missing',\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 'Invalid Signature',\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 'Invalid Signature Time',\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 'Signature Window Too Large',\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 'Unknown TAP Tag',\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 'Invalid Algorithm',\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 'Key Not Found',\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 'Nonce Replay Detected',\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 'Replay Protection Required',\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 'Issuer Not Allowed',\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 'Configuration Error',\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 'Internal Server Error',\n};\n\n/**\n * Verification mode.\n *\n * Determines behavior when TAP headers are missing:\n * - tap_only: Missing TAP → 401 + E_TAP_SIGNATURE_MISSING (authentication required)\n * - receipt_or_tap: Missing TAP → 402 + E_RECEIPT_MISSING (payment remedy)\n */\nexport type VerificationMode = 'tap_only' | 'receipt_or_tap';\n\n/**\n * Handler action type.\n *\n * Determines how the handler should respond:\n * - error: Authentication/authorization error\n * - challenge: Payment challenge (402 only)\n * - pass: Bypass verification (bypass paths)\n * - forward: Verification succeeded, forward request\n */\nexport type HandlerAction = 'error' | 'challenge' | 'pass' | 'forward';\n\n/**\n * Mode behavior for missing TAP headers.\n */\nexport interface ModeBehavior {\n /** HTTP status code */\n status: number;\n /** PEAC error code */\n code: PeacErrorCode;\n /** Handler action */\n action: HandlerAction;\n}\n\n/**\n * Verification mode behavior contract.\n *\n * Defines expected HTTP status, error code, and action when no TAP headers are present.\n * This is the canonical source of truth for mode-based behavior.\n */\nexport const MODE_BEHAVIOR: Record<VerificationMode, ModeBehavior> = {\n receipt_or_tap: {\n status: 402,\n code: CANONICAL_ERROR_CODES.RECEIPT_MISSING,\n action: 'challenge',\n },\n tap_only: {\n status: 401,\n code: CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING,\n action: 'error',\n },\n} as const;\n\n/**\n * HTTP status codes that require WWW-Authenticate header.\n *\n * Per v0.9.27 decision: WWW-Authenticate is sent on both 401 (auth required) and 402 (payment remedy).\n */\nexport const WWW_AUTHENTICATE_STATUSES = [401, 402] as const;\n\n/**\n * Get RFC 9457 Problem Details type URI for an error code.\n *\n * @param code - PEAC error code\n * @returns Problem Details type URI (e.g., \"https://www.peacprotocol.org/problems/E_TAP_SIGNATURE_MISSING\")\n */\nexport function problemTypeFor(code: PeacErrorCode): string {\n return `${PROBLEM_TYPE_BASE}/${code}`;\n}\n\n/**\n * Error catalog entry.\n */\nexport interface ErrorCatalogEntry {\n /** HTTP status code */\n status: number;\n /** RFC 9457 title */\n title: string;\n /** Default detail message (can be overridden at runtime) */\n defaultDetail?: string;\n}\n\n/**\n * Error catalog with HTTP status, title, and default detail for each error code.\n *\n * Use this for constructing RFC 9457 Problem Details responses.\n */\nexport const ERROR_CATALOG: Record<PeacErrorCode, ErrorCatalogEntry> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n defaultDetail: 'A valid PEAC receipt is required to access this resource.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n defaultDetail: 'The provided receipt is invalid.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n defaultDetail: 'The provided receipt has expired.',\n },\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n defaultDetail: 'TAP signature headers are required.',\n },\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n defaultDetail: 'TAP signature verification failed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n defaultDetail: 'TAP signature time is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n defaultDetail: 'TAP signature time window exceeds maximum allowed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n defaultDetail: 'Unknown TAP tag in signature.',\n },\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n defaultDetail: 'TAP signature algorithm is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n defaultDetail: 'TAP signing key not found.',\n },\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n defaultDetail: 'Nonce replay detected.',\n },\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n defaultDetail:\n 'Replay protection required but not configured. Set UNSAFE_ALLOW_NO_REPLAY=true to bypass (UNSAFE for production).',\n },\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n defaultDetail: 'Issuer not in allowlist.',\n },\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n defaultDetail:\n 'Worker misconfigured: ISSUER_ALLOWLIST is required. Set UNSAFE_ALLOW_ANY_ISSUER=true to bypass (UNSAFE for production).',\n },\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n defaultDetail: 'An internal server error occurred.',\n },\n};\n\n/**\n * Get HTTP status code for a PEAC error code.\n *\n * @param code - PEAC error code\n * @returns HTTP status code\n */\nexport function getStatusForCode(code: PeacErrorCode): number {\n return CANONICAL_STATUS_MAPPINGS[code];\n}\n\n/**\n * Check if HTTP status requires WWW-Authenticate header.\n *\n * @param status - HTTP status code\n * @returns true if WWW-Authenticate header is required\n */\nexport function requiresWwwAuthenticate(status: number): boolean {\n return WWW_AUTHENTICATE_STATUSES.includes(status as 401 | 402);\n}\n\n/**\n * Build canonical WWW-Authenticate header value.\n *\n * Format: PEAC realm=\"<realm>\", error=\"<code>\", error_uri=\"<uri>\"\n *\n * @param code - PEAC error code\n * @param realm - Authentication realm (default: \"peac\")\n * @returns Canonical WWW-Authenticate header value\n */\nexport function buildWwwAuthenticate(code: PeacErrorCode, realm = 'peac'): string {\n return `PEAC realm=\"${realm}\", error=\"${code}\", error_uri=\"${problemTypeFor(code)}\"`;\n}\n\n/**\n * Valid PEAC HTTP status codes.\n *\n * All status codes that can be returned by PEAC verification handlers.\n */\nexport type PeacHttpStatus = 400 | 401 | 402 | 403 | 409 | 500;\n\n// Internal Set import - NOT re-exported\nimport { PEAC_ERROR_CODE_SET } from './internal/error-codes.js';\n\n/**\n * Type guard to check if a value is a valid PEAC error code.\n *\n * Uses O(1) Set lookup for performance.\n *\n * @param x - Value to check\n * @returns true if x is a valid PeacErrorCode\n *\n * @example\n * ```typescript\n * if (isPeacErrorCode(code)) {\n * // TypeScript now knows code is PeacErrorCode\n * const status = getStatusForCode(code);\n * }\n * ```\n */\nexport function isPeacErrorCode(x: unknown): x is PeacErrorCode {\n return typeof x === 'string' && PEAC_ERROR_CODE_SET.has(x as PeacErrorCode);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/codes.ts","../src/issuer-origin.ts","../src/internal/error-codes.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Canonical PEAC error codes.\n *\n * This is the single source of truth for error code values.\n * All other modules import from here to avoid circular dependencies.\n */\n\n/**\n * Canonical PEAC error codes.\n *\n * Format: E_<CATEGORY>_<ERROR>\n *\n * All error codes use the E_ prefix for consistency with RFC/IETF error code conventions.\n */\nexport const CANONICAL_ERROR_CODES = {\n // Receipt errors (402 - Payment Required)\n RECEIPT_MISSING: 'E_RECEIPT_MISSING',\n RECEIPT_INVALID: 'E_RECEIPT_INVALID',\n RECEIPT_EXPIRED: 'E_RECEIPT_EXPIRED',\n\n // TAP errors (401 - Authentication)\n TAP_SIGNATURE_MISSING: 'E_TAP_SIGNATURE_MISSING',\n TAP_SIGNATURE_INVALID: 'E_TAP_SIGNATURE_INVALID',\n TAP_TIME_INVALID: 'E_TAP_TIME_INVALID',\n TAP_WINDOW_TOO_LARGE: 'E_TAP_WINDOW_TOO_LARGE',\n TAP_TAG_UNKNOWN: 'E_TAP_TAG_UNKNOWN',\n TAP_ALGORITHM_INVALID: 'E_TAP_ALGORITHM_INVALID',\n TAP_KEY_NOT_FOUND: 'E_TAP_KEY_NOT_FOUND',\n TAP_KEYID_INVALID: 'E_TAP_KEYID_INVALID',\n\n // Replay error (409 - Conflict)\n TAP_NONCE_REPLAY: 'E_TAP_NONCE_REPLAY',\n\n // Replay protection required (401 - requires config change)\n TAP_REPLAY_PROTECTION_REQUIRED: 'E_TAP_REPLAY_PROTECTION_REQUIRED',\n\n // Issuer errors (403 - Forbidden)\n ISSUER_NOT_ALLOWED: 'E_ISSUER_NOT_ALLOWED',\n\n // Configuration errors (500 - Server misconfiguration)\n CONFIG_ISSUER_ALLOWLIST_REQUIRED: 'E_CONFIG_ISSUER_ALLOWLIST_REQUIRED',\n\n // Internal errors (500)\n INTERNAL_ERROR: 'E_INTERNAL_ERROR',\n} as const;\n\n/**\n * PEAC error code type (union of all canonical error codes).\n *\n * Use this type for compile-time safety when handling error codes.\n */\nexport type PeacErrorCode = (typeof CANONICAL_ERROR_CODES)[keyof typeof CANONICAL_ERROR_CODES];\n","/**\n * Canonical issuer-origin allowlist check.\n *\n * Surfaces use this helper to decide whether a receipt or TAP issuer origin is\n * configured as trusted. The comparison is intentionally origin-based\n * (scheme + host + port), not host-only, and fails closed for malformed inputs.\n *\n * Runtime-neutral by construction: it uses only the standard URL API and runs\n * unchanged on Node, Workers, Deno, and Bun. It performs no DNS resolution, no\n * network access, and no private-IP checks; outbound-fetch SSRF protection is a\n * separate fetch-layer concern.\n */\n\n// True if the string contains an ASCII control character (C0 range or DEL).\n// Used to reject smuggled control characters that the URL API might otherwise\n// strip during parsing. Implemented as a code-unit scan rather than a regex\n// escape range to keep this security-sensitive source free of escape sequences.\nfunction hasAsciiControl(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code <= 0x1f || code === 0x7f) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Normalize a value to its https origin, or null if it is not an acceptable\n * issuer origin. Acceptable means: no surrounding or embedded ASCII control\n * characters or whitespace, a parseable absolute URL, the `https:` scheme, and\n * no userinfo (username/password). Path, query, and fragment are not part of an\n * origin and are ignored.\n */\nfunction toHttpsOrigin(value: string): string | null {\n // Reject surrounding whitespace and embedded ASCII control characters before\n // parsing. The URL API would otherwise strip some of these, which could let a\n // padded or smuggled-control-character entry compare equal to a clean one.\n if (value.trim() !== value || hasAsciiControl(value)) {\n return null;\n }\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return null;\n }\n if (url.protocol !== 'https:') {\n return null;\n }\n if (url.username !== '' || url.password !== '') {\n return null;\n }\n return url.origin;\n}\n\n/**\n * Returns true iff `candidate` matches an entry in `allowlist` by https origin.\n *\n * Comparison is by URL origin (scheme + host + port); a default `:443` is\n * normalized away by the `URL` API, a non-default port must match exactly, and\n * a scheme mismatch (e.g. an `http:` candidate against an `https:` entry) never\n * matches.\n *\n * Fails closed:\n * - an empty allowlist returns false;\n * - a candidate that is not a parseable absolute https URL, or that carries\n * userinfo or control characters, returns false;\n * - a malformed or non-https allowlist entry is skipped (it never matches) but\n * does not prevent a later valid entry from matching.\n *\n * There is no wildcard, suffix, regex, or raw-string fallback matching.\n */\nexport function isAllowedIssuerOrigin(candidate: string, allowlist: readonly string[]): boolean {\n const candidateOrigin = toHttpsOrigin(candidate);\n if (candidateOrigin === null) {\n return false;\n }\n for (const entry of allowlist) {\n if (toHttpsOrigin(entry) === candidateOrigin) {\n return true;\n }\n }\n return false;\n}\n","/**\n * Internal error code utilities.\n *\n * @internal - Not part of public API. Consumers should import from `@peac/contracts`.\n */\n\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from '../codes.js';\n\n/**\n * Set of all canonical PEAC error codes for O(1) validation.\n *\n * Typed as ReadonlySet to prevent accidental mutation.\n *\n * @internal\n */\nconst VALUES = Object.values(CANONICAL_ERROR_CODES) as PeacErrorCode[];\nexport const PEAC_ERROR_CODE_SET: ReadonlySet<PeacErrorCode> = new Set(VALUES);\n","/**\n * PEAC Canonical Contracts\n *\n * Single source of truth for error codes, HTTP status mappings, and verification mode behavior.\n * All surface implementations MUST import from this package to prevent drift.\n *\n * @packageDocumentation\n */\n\n// Re-export canonical error codes from internal module (avoids circular dependencies)\nexport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\nimport { CANONICAL_ERROR_CODES, type PeacErrorCode } from './codes.js';\n\n// Canonical issuer-origin allowlist check shared across surfaces.\nexport { isAllowedIssuerOrigin } from './issuer-origin.js';\n\n/**\n * Base URI for RFC 9457 Problem Details type field.\n */\nexport const PROBLEM_TYPE_BASE = 'https://www.peacprotocol.org/problems';\n\n/**\n * Canonical HTTP status code mappings.\n *\n * Status code semantics:\n * - 400: Client error (malformed request, invalid parameters)\n * - 401: Authentication required (missing/invalid credentials)\n * - 402: Payment Required (reserved for PEAC receipt payment flows)\n * - 403: Forbidden (authenticated but not authorized)\n * - 409: Conflict (replay detection - request conflicts with previous state)\n * - 500: Server error (configuration, internal failure)\n */\nexport const CANONICAL_STATUS_MAPPINGS: Record<PeacErrorCode, number> = {\n // 402 - Payment Required (reserved for payment flows)\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 402,\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 402,\n\n // 401 - Authentication errors\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 401,\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 401,\n [CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: 401,\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 401,\n\n // 400 - Client errors (malformed)\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 400,\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 400,\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 400,\n\n // 403 - Forbidden (issuer not in allowlist)\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 403,\n\n // 409 - Conflict (replay detected)\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 409,\n\n // 500 - Server errors\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 500,\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 500,\n};\n\n/**\n * Canonical error titles for RFC 9457 Problem Details.\n */\nexport const CANONICAL_TITLES: Record<PeacErrorCode, string> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: 'Payment Required',\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: 'Invalid Receipt',\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: 'Receipt Expired',\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: 'Signature Missing',\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: 'Invalid Signature',\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: 'Invalid Signature Time',\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: 'Signature Window Too Large',\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: 'Unknown TAP Tag',\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: 'Invalid Algorithm',\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: 'Key Not Found',\n [CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: 'Invalid Key ID',\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: 'Nonce Replay Detected',\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: 'Replay Protection Required',\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: 'Issuer Not Allowed',\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: 'Configuration Error',\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: 'Internal Server Error',\n};\n\n/**\n * Verification mode.\n *\n * Determines behavior when TAP headers are missing:\n * - tap_only: Missing TAP → 401 + E_TAP_SIGNATURE_MISSING (authentication required)\n * - receipt_or_tap: Missing TAP → 402 + E_RECEIPT_MISSING (payment remedy)\n */\nexport type VerificationMode = 'tap_only' | 'receipt_or_tap';\n\n/**\n * Handler action type.\n *\n * Determines how the handler should respond:\n * - error: Authentication/authorization error\n * - challenge: Payment challenge (402 only)\n * - pass: Bypass verification (bypass paths)\n * - forward: Verification succeeded, forward request\n */\nexport type HandlerAction = 'error' | 'challenge' | 'pass' | 'forward';\n\n/**\n * Mode behavior for missing TAP headers.\n */\nexport interface ModeBehavior {\n /** HTTP status code */\n status: number;\n /** PEAC error code */\n code: PeacErrorCode;\n /** Handler action */\n action: HandlerAction;\n}\n\n/**\n * Verification mode behavior contract.\n *\n * Defines expected HTTP status, error code, and action when no TAP headers are present.\n * This is the canonical source of truth for mode-based behavior.\n */\nexport const MODE_BEHAVIOR: Record<VerificationMode, ModeBehavior> = {\n receipt_or_tap: {\n status: 402,\n code: CANONICAL_ERROR_CODES.RECEIPT_MISSING,\n action: 'challenge',\n },\n tap_only: {\n status: 401,\n code: CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING,\n action: 'error',\n },\n} as const;\n\n/**\n * HTTP status codes that require WWW-Authenticate header.\n *\n * Per v0.9.27 decision: WWW-Authenticate is sent on both 401 (auth required) and 402 (payment remedy).\n */\nexport const WWW_AUTHENTICATE_STATUSES = [401, 402] as const;\n\n/**\n * Get RFC 9457 Problem Details type URI for an error code.\n *\n * @param code - PEAC error code\n * @returns Problem Details type URI (e.g., \"https://www.peacprotocol.org/problems/E_TAP_SIGNATURE_MISSING\")\n */\nexport function problemTypeFor(code: PeacErrorCode): string {\n return `${PROBLEM_TYPE_BASE}/${code}`;\n}\n\n/**\n * Error catalog entry.\n */\nexport interface ErrorCatalogEntry {\n /** HTTP status code */\n status: number;\n /** RFC 9457 title */\n title: string;\n /** Default detail message (can be overridden at runtime) */\n defaultDetail?: string;\n}\n\n/**\n * Error catalog with HTTP status, title, and default detail for each error code.\n *\n * Use this for constructing RFC 9457 Problem Details responses.\n */\nexport const ERROR_CATALOG: Record<PeacErrorCode, ErrorCatalogEntry> = {\n [CANONICAL_ERROR_CODES.RECEIPT_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_MISSING],\n defaultDetail: 'A valid PEAC receipt is required to access this resource.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_INVALID],\n defaultDetail: 'The provided receipt is invalid.',\n },\n [CANONICAL_ERROR_CODES.RECEIPT_EXPIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.RECEIPT_EXPIRED],\n defaultDetail: 'The provided receipt has expired.',\n },\n\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_MISSING],\n defaultDetail: 'TAP signature headers are required.',\n },\n [CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_SIGNATURE_INVALID],\n defaultDetail: 'TAP signature verification failed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TIME_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TIME_INVALID],\n defaultDetail: 'TAP signature time is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_WINDOW_TOO_LARGE],\n defaultDetail: 'TAP signature time window exceeds maximum allowed.',\n },\n [CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_TAG_UNKNOWN],\n defaultDetail: 'Unknown TAP tag in signature.',\n },\n [CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_ALGORITHM_INVALID],\n defaultDetail: 'TAP signature algorithm is invalid.',\n },\n [CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEY_NOT_FOUND],\n defaultDetail: 'TAP signing key not found.',\n },\n [CANONICAL_ERROR_CODES.TAP_KEYID_INVALID]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_KEYID_INVALID],\n defaultDetail: 'TAP keyid must be an absolute https URL identifying the issuer.',\n },\n [CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_NONCE_REPLAY],\n defaultDetail: 'Nonce replay detected.',\n },\n [CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.TAP_REPLAY_PROTECTION_REQUIRED],\n defaultDetail:\n 'Replay protection required but not configured. Set UNSAFE_ALLOW_NO_REPLAY=true to bypass (UNSAFE for production).',\n },\n\n [CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.ISSUER_NOT_ALLOWED],\n defaultDetail: 'Issuer not in allowlist.',\n },\n\n [CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.CONFIG_ISSUER_ALLOWLIST_REQUIRED],\n defaultDetail:\n 'Worker misconfigured: ISSUER_ALLOWLIST is required. Set UNSAFE_ALLOW_ANY_ISSUER=true to bypass (UNSAFE for production).',\n },\n [CANONICAL_ERROR_CODES.INTERNAL_ERROR]: {\n status: CANONICAL_STATUS_MAPPINGS[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n title: CANONICAL_TITLES[CANONICAL_ERROR_CODES.INTERNAL_ERROR],\n defaultDetail: 'An internal server error occurred.',\n },\n};\n\n/**\n * Get HTTP status code for a PEAC error code.\n *\n * @param code - PEAC error code\n * @returns HTTP status code\n */\nexport function getStatusForCode(code: PeacErrorCode): number {\n return CANONICAL_STATUS_MAPPINGS[code];\n}\n\n/**\n * Check if HTTP status requires WWW-Authenticate header.\n *\n * @param status - HTTP status code\n * @returns true if WWW-Authenticate header is required\n */\nexport function requiresWwwAuthenticate(status: number): boolean {\n return WWW_AUTHENTICATE_STATUSES.includes(status as 401 | 402);\n}\n\n/**\n * Build canonical WWW-Authenticate header value.\n *\n * Format: PEAC realm=\"<realm>\", error=\"<code>\", error_uri=\"<uri>\"\n *\n * @param code - PEAC error code\n * @param realm - Authentication realm (default: \"peac\")\n * @returns Canonical WWW-Authenticate header value\n */\nexport function buildWwwAuthenticate(code: PeacErrorCode, realm = 'peac'): string {\n return `PEAC realm=\"${realm}\", error=\"${code}\", error_uri=\"${problemTypeFor(code)}\"`;\n}\n\n/**\n * Valid PEAC HTTP status codes.\n *\n * All status codes that can be returned by PEAC verification handlers.\n */\nexport type PeacHttpStatus = 400 | 401 | 402 | 403 | 409 | 500;\n\n// Internal Set import - NOT re-exported\nimport { PEAC_ERROR_CODE_SET } from './internal/error-codes.js';\n\n/**\n * Type guard to check if a value is a valid PEAC error code.\n *\n * Uses O(1) Set lookup for performance.\n *\n * @param x - Value to check\n * @returns true if x is a valid PeacErrorCode\n *\n * @example\n * ```typescript\n * if (isPeacErrorCode(code)) {\n * // TypeScript now knows code is PeacErrorCode\n * const status = getStatusForCode(code);\n * }\n * ```\n */\nexport function isPeacErrorCode(x: unknown): x is PeacErrorCode {\n return typeof x === 'string' && PEAC_ERROR_CODE_SET.has(x as PeacErrorCode);\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical issuer-origin allowlist check.
|
|
3
|
+
*
|
|
4
|
+
* Surfaces use this helper to decide whether a receipt or TAP issuer origin is
|
|
5
|
+
* configured as trusted. The comparison is intentionally origin-based
|
|
6
|
+
* (scheme + host + port), not host-only, and fails closed for malformed inputs.
|
|
7
|
+
*
|
|
8
|
+
* Runtime-neutral by construction: it uses only the standard URL API and runs
|
|
9
|
+
* unchanged on Node, Workers, Deno, and Bun. It performs no DNS resolution, no
|
|
10
|
+
* network access, and no private-IP checks; outbound-fetch SSRF protection is a
|
|
11
|
+
* separate fetch-layer concern.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Returns true iff `candidate` matches an entry in `allowlist` by https origin.
|
|
15
|
+
*
|
|
16
|
+
* Comparison is by URL origin (scheme + host + port); a default `:443` is
|
|
17
|
+
* normalized away by the `URL` API, a non-default port must match exactly, and
|
|
18
|
+
* a scheme mismatch (e.g. an `http:` candidate against an `https:` entry) never
|
|
19
|
+
* matches.
|
|
20
|
+
*
|
|
21
|
+
* Fails closed:
|
|
22
|
+
* - an empty allowlist returns false;
|
|
23
|
+
* - a candidate that is not a parseable absolute https URL, or that carries
|
|
24
|
+
* userinfo or control characters, returns false;
|
|
25
|
+
* - a malformed or non-https allowlist entry is skipped (it never matches) but
|
|
26
|
+
* does not prevent a later valid entry from matching.
|
|
27
|
+
*
|
|
28
|
+
* There is no wildcard, suffix, regex, or raw-string fallback matching.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isAllowedIssuerOrigin(candidate: string, allowlist: readonly string[]): boolean;
|
|
31
|
+
//# sourceMappingURL=issuer-origin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issuer-origin.d.ts","sourceRoot":"","sources":["../src/issuer-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA6CH;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAW9F"}
|