- Created shared/types.ts with well-documented type definitions - Created shared/auth-core.ts with core auth functions (PKCE, tokens, encryption) - Created shared/redis-auth.ts with Redis operations for auth data - Updated src/services/auth.ts to use shared modules - Added TokenIntrospectionResponse interface for Mode 2
87 lines
No EOL
2.6 KiB
TypeScript
87 lines
No EOL
2.6 KiB
TypeScript
import crypto from "crypto";
|
|
import { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
|
|
/**
|
|
* Generates a PKCE code challenge from a verifier string.
|
|
* Uses S256 method as specified in RFC 7636.
|
|
* @param verifier The code verifier string
|
|
* @returns Base64url-encoded SHA256 hash of the verifier
|
|
*/
|
|
export function generatePKCEChallenge(verifier: string): string {
|
|
const buffer = Buffer.from(verifier);
|
|
const hash = crypto.createHash("sha256").update(buffer);
|
|
return hash.digest("base64url");
|
|
}
|
|
|
|
/**
|
|
* Generates a cryptographically secure random token.
|
|
* @returns 64-character hexadecimal string
|
|
*/
|
|
export function generateToken(): string {
|
|
return crypto.randomBytes(32).toString("hex");
|
|
}
|
|
|
|
/**
|
|
* Computes SHA256 hash of input data.
|
|
* @param data The string to hash
|
|
* @returns Hexadecimal representation of the hash
|
|
*/
|
|
export function sha256(data: string): string {
|
|
return crypto.createHash("sha256").update(data).digest("hex");
|
|
}
|
|
|
|
/**
|
|
* Encrypts a string using AES-256-CBC encryption.
|
|
* @param text The plaintext to encrypt
|
|
* @param key The encryption key (64 hex characters)
|
|
* @returns Encrypted string in format "iv:ciphertext"
|
|
*/
|
|
export function encryptString({ text, key }: { text: string; key: string }): string {
|
|
const iv = crypto.randomBytes(16);
|
|
const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(key, "hex"), iv);
|
|
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
encrypted += cipher.final("hex");
|
|
return `${iv.toString("hex")}:${encrypted}`;
|
|
}
|
|
|
|
/**
|
|
* Decrypts a string encrypted with encryptString.
|
|
* @param encryptedText The encrypted string in format "iv:ciphertext"
|
|
* @param key The encryption key (64 hex characters)
|
|
* @returns Decrypted plaintext
|
|
*/
|
|
export function decryptString({
|
|
encryptedText,
|
|
key,
|
|
}: {
|
|
encryptedText: string;
|
|
key: string;
|
|
}): string {
|
|
const [ivHex, encrypted] = encryptedText.split(":");
|
|
const iv = Buffer.from(ivHex, "hex");
|
|
const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key, "hex"), iv);
|
|
let decrypted = decipher.update(encrypted, "hex", "utf-8");
|
|
decrypted += decipher.final("utf-8");
|
|
return decrypted;
|
|
}
|
|
|
|
/**
|
|
* Access token expiry time in seconds (1 hour)
|
|
*/
|
|
export const ACCESS_TOKEN_EXPIRY_SEC = 60 * 60;
|
|
|
|
/**
|
|
* Generates a complete set of MCP OAuth tokens.
|
|
* @returns OAuth tokens with access token, refresh token, and expiry
|
|
*/
|
|
export function generateMcpTokens(): OAuthTokens {
|
|
const mcpAccessToken = generateToken();
|
|
const mcpRefreshToken = generateToken();
|
|
|
|
return {
|
|
access_token: mcpAccessToken,
|
|
refresh_token: mcpRefreshToken,
|
|
expires_in: ACCESS_TOKEN_EXPIRY_SEC,
|
|
token_type: "Bearer",
|
|
};
|
|
} |