From 0bc3a74ac32c5d6a1b2607e4f5dbcbbd72a1810e Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Thu, 6 Feb 2025 13:38:21 +0000 Subject: [PATCH] OAuth metadata discovery --- src/client/auth/auth.ts | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/client/auth/auth.ts diff --git a/src/client/auth/auth.ts b/src/client/auth/auth.ts new file mode 100644 index 00000000..842abe9d --- /dev/null +++ b/src/client/auth/auth.ts @@ -0,0 +1,58 @@ +import { z } from "zod"; + +export const OAuthMetadataSchema = z + .object({ + issuer: z.string(), + authorization_endpoint: z.string(), + token_endpoint: z.string(), + registration_endpoint: z.string().optional(), + scopes_supported: z.array(z.string()).optional(), + response_types_supported: z.array(z.string()), + response_modes_supported: z.array(z.string()).optional(), + grant_types_supported: z.array(z.string()).optional(), + token_endpoint_auth_methods_supported: z.array(z.string()).optional(), + token_endpoint_auth_signing_alg_values_supported: z + .array(z.string()) + .optional(), + service_documentation: z.string().optional(), + revocation_endpoint: z.string().optional(), + revocation_endpoint_auth_methods_supported: z.array(z.string()).optional(), + revocation_endpoint_auth_signing_alg_values_supported: z + .array(z.string()) + .optional(), + introspection_endpoint: z.string().optional(), + introspection_endpoint_auth_methods_supported: z + .array(z.string()) + .optional(), + introspection_endpoint_auth_signing_alg_values_supported: z + .array(z.string()) + .optional(), + code_challenge_methods_supported: z.array(z.string()).optional(), + }) + .passthrough(); + +export type OAuthMetadata = z.infer; + +/** + * Looks up RFC 8414 OAuth 2.0 Authorization Server Metadata. + * + * If the server returns a 404 for the well-known endpoint, this function will + * return `undefined`. Any other errors will be thrown as exceptions. + */ +export async function discoverOAuthMetadata( + serverUrl: string | URL, +): Promise { + const url = new URL("/.well-known/oauth-authorization-server", serverUrl); + const response = await fetch(url); + if (response.status === 404) { + return undefined; + } + + if (!response.ok) { + throw new Error( + `HTTP ${response.status} trying to load well-known OAuth metadata`, + ); + } + + return OAuthMetadataSchema.parse(await response.json()); +}