(
auth: McpAuthProvider["Service"],
request: Request,
outcome: Exclude<AuthOutcome, { readonly _tag: "Authenticated" }>,
)
| 56 | : jsonRpcErrorBody(status, code, message, { challenge }); |
| 57 | |
| 58 | const renderAuthError = ( |
| 59 | auth: McpAuthProvider["Service"], |
| 60 | request: Request, |
| 61 | outcome: Exclude<AuthOutcome, { readonly _tag: "Authenticated" }>, |
| 62 | ): Response => { |
| 63 | if (Predicate.isTagged(outcome, "Unauthorized")) { |
| 64 | return jsonRpcResponse( |
| 65 | 401, |
| 66 | -32001, |
| 67 | "Unauthorized", |
| 68 | outcome.challenge ?? `Bearer resource_metadata="${auth.resourceMetadataUrl(request)}"`, |
| 69 | ); |
| 70 | } |
| 71 | if (Predicate.isTagged(outcome, "Forbidden")) { |
| 72 | return jsonRpcResponse(403, outcome.code ?? -32001, outcome.message); |
| 73 | } |
| 74 | // Unavailable: a transient auth-infra failure (JWKS blip OR a WorkOS |
| 75 | // membership-lookup 429/5xx/timeout). Both are retryable, so advertise a |
| 76 | // Retry-After so the client (and any polite retry layer) backs off instead of |
| 77 | // hammering (same rendering as the shared envelope's Unavailable branch). |
| 78 | // Crucially, this path NEVER reaches the session-destroy branch below — a |
| 79 | // transient failure must not condemn a live session. |
| 80 | // |
| 81 | // Note this 503 shares JSON-RPC code -32001 with the terminated-session 404 |
| 82 | // ("Session timed out, please reconnect"); that is intentional — -32001 is |
| 83 | // the generic auth/session envelope code, and the HTTP STATUS is the |
| 84 | // discriminator clients act on: 503 = retry the SAME session id, 404 = the |
| 85 | // id is dead, reconnect. |
| 86 | return jsonRpcErrorBody(503, -32001, outcome.message, { |
| 87 | retryAfterSeconds: UNAVAILABLE_RETRY_AFTER_SECONDS, |
| 88 | }); |
| 89 | }; |
| 90 | |
| 91 | /** |
| 92 | * A Cloudflare *platform* Durable Object failure happened at one of this |
no test coverage detected