(target: Target, email: string)
| 209 | }; |
| 210 | |
| 211 | const mintBearerFlow = async (target: Target, email: string): Promise<string> => { |
| 212 | const consent = target.mcpConsent?.({ |
| 213 | label: email, |
| 214 | credentials: { email, password: "" }, |
| 215 | }); |
| 216 | if (!consent) throw new Error(`target ${target.name} has no mcpConsent strategy`); |
| 217 | |
| 218 | const mcpPath = new URL(target.mcpUrl).pathname; |
| 219 | let resourceResponse = await fetch( |
| 220 | new URL(`/.well-known/oauth-protected-resource${mcpPath}`, target.baseUrl), |
| 221 | ); |
| 222 | if (resourceResponse.status === 404) { |
| 223 | resourceResponse = await fetch( |
| 224 | new URL("/.well-known/oauth-protected-resource", target.baseUrl), |
| 225 | ); |
| 226 | } |
| 227 | const resource = await jsonFrom<{ authorization_servers?: ReadonlyArray<string> }>( |
| 228 | resourceResponse, |
| 229 | "mintBearer: protected-resource metadata", |
| 230 | ); |
| 231 | const issuer = resource.authorization_servers?.[0]; |
| 232 | if (!issuer) throw new Error("mintBearer: no authorization server advertised"); |
| 233 | const metadata = await jsonFrom<{ |
| 234 | readonly authorization_endpoint: string; |
| 235 | readonly token_endpoint: string; |
| 236 | readonly registration_endpoint: string; |
| 237 | }>( |
| 238 | await fetch(new URL("/.well-known/oauth-authorization-server", issuer)), |
| 239 | "mintBearer: authorization-server metadata", |
| 240 | ); |
| 241 | |
| 242 | const redirectUri = "http://127.0.0.1:9/callback"; |
| 243 | const registered = await jsonFrom<{ readonly client_id: string }>( |
| 244 | await fetch(metadata.registration_endpoint, { |
| 245 | method: "POST", |
| 246 | headers: { "content-type": "application/json" }, |
| 247 | body: JSON.stringify({ |
| 248 | client_name: "executor-e2e", |
| 249 | redirect_uris: [redirectUri], |
| 250 | grant_types: ["authorization_code", "refresh_token"], |
| 251 | response_types: ["code"], |
| 252 | token_endpoint_auth_method: "none", |
| 253 | }), |
| 254 | }), |
| 255 | "mintBearer: dynamic client registration", |
| 256 | ); |
| 257 | |
| 258 | const verifier = randomBytes(32).toString("base64url"); |
| 259 | const authorizeUrl = new URL(metadata.authorization_endpoint); |
| 260 | authorizeUrl.searchParams.set("client_id", registered.client_id); |
| 261 | authorizeUrl.searchParams.set("redirect_uri", redirectUri); |
| 262 | authorizeUrl.searchParams.set("response_type", "code"); |
| 263 | authorizeUrl.searchParams.set("state", randomUUID()); |
| 264 | authorizeUrl.searchParams.set( |
| 265 | "code_challenge", |
| 266 | createHash("sha256").update(verifier).digest("base64url"), |
| 267 | ); |
| 268 | authorizeUrl.searchParams.set("code_challenge_method", "S256"); |
no test coverage detected