Split full/partial client information

This commit is contained in:
Justin Spahr-Summers 2025-02-17 11:07:32 +00:00
commit a0069e2341
No known key found for this signature in database
2 changed files with 16 additions and 9 deletions

View file

@ -1,7 +1,7 @@
import pkceChallenge from "pkce-challenge";
import { LATEST_PROTOCOL_VERSION } from "../types.js";
import type { OAuthClientMetadata, OAuthClientInformation, OAuthTokens, OAuthMetadata } from "../shared/auth.js";
import { OAuthClientInformationSchema, OAuthMetadataSchema, OAuthTokensSchema } from "../shared/auth.js";
import type { OAuthClientMetadata, OAuthClientInformation, OAuthTokens, OAuthMetadata, OAuthClientInformationFull } from "../shared/auth.js";
import { OAuthClientInformationFullSchema, OAuthMetadataSchema, OAuthTokensSchema } from "../shared/auth.js";
/**
* Implements an end-to-end OAuth client to be used with one MCP server.
@ -36,7 +36,7 @@ export interface OAuthClientProvider {
* This method is not required to be implemented if client information is
* statically known (e.g., pre-registered).
*/
saveClientInformation?(clientInformation: OAuthClientInformation): void | Promise<void>;
saveClientInformation?(clientInformation: OAuthClientInformationFull): void | Promise<void>;
/**
* Loads any existing OAuth tokens for the current session, or returns
@ -98,12 +98,13 @@ export async function auth(
throw new Error("OAuth client information must be saveable for dynamic registration");
}
clientInformation = await registerClient(serverUrl, {
const fullInformation = await registerClient(serverUrl, {
metadata,
clientMetadata: provider.clientMetadata,
});
await provider.saveClientInformation(clientInformation);
await provider.saveClientInformation(fullInformation);
clientInformation = fullInformation;
}
// Exchange authorization code for tokens
@ -371,7 +372,7 @@ export async function registerClient(
metadata?: OAuthMetadata;
clientMetadata: OAuthClientMetadata;
},
): Promise<OAuthClientInformation> {
): Promise<OAuthClientInformationFull> {
let registrationUrl: URL;
if (metadata) {
@ -396,5 +397,5 @@ export async function registerClient(
throw new Error(`Dynamic client registration failed: HTTP ${response.status}`);
}
return OAuthClientInformationSchema.parse(await response.json());
return OAuthClientInformationFullSchema.parse(await response.json());
}

View file

@ -79,15 +79,20 @@ export const OAuthClientMetadataSchema = z.object({
}).passthrough();
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration response
* RFC 7591 OAuth 2.0 Dynamic Client Registration client information
*/
export const OAuthClientInformationSchema = OAuthClientMetadataSchema.extend({
export const OAuthClientInformationSchema = z.object({
client_id: z.string(),
client_secret: z.string().optional(),
client_id_issued_at: z.number().optional(),
client_secret_expires_at: z.number().optional(),
}).passthrough();
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
*/
export const OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(OAuthClientInformationSchema);
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration error response
*/
@ -101,4 +106,5 @@ export type OAuthTokens = z.infer<typeof OAuthTokensSchema>;
export type OAuthError = z.infer<typeof OAuthErrorSchema>;
export type OAuthClientMetadata = z.infer<typeof OAuthClientMetadataSchema>;
export type OAuthClientInformation = z.infer<typeof OAuthClientInformationSchema>;
export type OAuthClientInformationFull = z.infer<typeof OAuthClientInformationFullSchema>;
export type OAuthClientRegistrationError = z.infer<typeof OAuthClientRegistrationErrorSchema>;