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