Auth code and refresh token flows

This commit is contained in:
Justin Spahr-Summers 2025-02-17 16:40:51 +00:00
commit 26ffe00dc3
No known key found for this signature in database
2 changed files with 28 additions and 16 deletions

View file

@ -1,9 +1,8 @@
import { z } from "zod";
import express, { RequestHandler, Response } from "express";
import express, { RequestHandler } from "express";
import { OAuthServerProvider } from "../provider.js";
import cors from "cors";
import { AuthorizationHandlerOptions } from "./authorize.js";
import { OAuthClientInformationFull } from "../../../shared/auth.js";
import { verifyChallenge } from "pkce-challenge";
export type TokenHandlerOptions = {
provider: OAuthServerProvider;
@ -33,16 +32,6 @@ export function tokenHandler({ provider }: TokenHandlerOptions): RequestHandler
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use(cors());
async function authorizationCodeGrant(client: OAuthClientInformationFull, grant: z.infer<typeof AuthorizationCodeGrantSchema>, res: Response) {
const codeChallenge = await provider.challengeForAuthorizationCode(grant.code);
// TODO
}
async function refreshTokenGrant(client: OAuthClientInformationFull, grant: z.infer<typeof RefreshTokenGrantSchema>, res: Response) {
// TODO
}
// Actual request handler
router.post("/", async (req, res) => {
let client_id, client_secret, grant_type;
try {
@ -86,7 +75,18 @@ export function tokenHandler({ provider }: TokenHandlerOptions): RequestHandler
return;
}
await authorizationCodeGrant(client, grant, res);
const codeChallenge = await provider.challengeForAuthorizationCode(grant.code);
if (!(await verifyChallenge(grant.code_verifier, codeChallenge))) {
res.status(400).json({
error: "invalid_request",
error_description: "code_verifier does not match the challenge",
});
return;
}
const tokens = await provider.exchangeAuthorizationCode(grant.code);
res.status(200).json(tokens);
break;
}
@ -102,7 +102,9 @@ export function tokenHandler({ provider }: TokenHandlerOptions): RequestHandler
return;
}
await refreshTokenGrant(client, grant, res);
const scopes = grant.scope ? grant.scope.split(" ") : undefined;
const tokens = await provider.exchangeRefreshToken(grant.refresh_token, scopes);
res.status(200).json(tokens);
break;
}

View file

@ -1,6 +1,6 @@
import { Response } from "express";
import { OAuthRegisteredClientsStore } from "./clients.js";
import { OAuthClientInformationFull } from "../../shared/auth.js";
import { OAuthClientInformationFull, OAuthTokens } from "../../shared/auth.js";
export type AuthorizationParams = {
client: OAuthClientInformationFull;
@ -34,4 +34,14 @@ export interface OAuthServerProvider {
* Returns the `codeChallenge` that was used when the indicated authorization began.
*/
challengeForAuthorizationCode(authorizationCode: string): Promise<string>;
/**
* Exchanges an authorization code for an access token.
*/
exchangeAuthorizationCode(authorizationCode: string): Promise<OAuthTokens>;
/**
* Exchanges a refresh token for an access token.
*/
exchangeRefreshToken(refreshToken: string, scopes?: string[]): Promise<OAuthTokens>;
}