Skip to main content
Glama

VibeDoge MCP Server

by chenxing3060
jwt.ts6.2 kB
import { JWTPayload } from '../types/session.js'; /** * JWT工具类 * 提供JWT token的生成、验证和解析功能 */ export class JWTUtils { private static readonly SECRET_KEY = process.env.JWT_SECRET || 'vibe-coding-mcp-secret'; private static readonly ALGORITHM = 'HS256'; private static readonly DEFAULT_EXPIRES_IN = '24h'; /** * 生成JWT token * @param payload JWT载荷数据 * @param expiresIn 过期时间(默认24小时) * @returns JWT token字符串 */ static generateToken(payload: JWTPayload, expiresIn: string = this.DEFAULT_EXPIRES_IN): string { const header = { alg: this.ALGORITHM, typ: 'JWT' }; const now = Math.floor(Date.now() / 1000); const exp = this.parseExpiresIn(expiresIn); const jwtPayload = { ...payload, iat: now, exp: now + exp, iss: 'vibe-coding-mcp' }; const encodedHeader = this.base64UrlEncode(JSON.stringify(header)); const encodedPayload = this.base64UrlEncode(JSON.stringify(jwtPayload)); const signature = this.createSignature(`${encodedHeader}.${encodedPayload}`); return `${encodedHeader}.${encodedPayload}.${signature}`; } /** * 验证JWT token * @param token JWT token字符串 * @returns 验证结果和解析的载荷 */ static verifyToken(token: string): { valid: boolean; payload?: JWTPayload; error?: string } { try { const parts = token.split('.'); if (parts.length !== 3) { return { valid: false, error: 'Invalid token format' }; } const [encodedHeader, encodedPayload, signature] = parts; // 验证签名 const expectedSignature = this.createSignature(`${encodedHeader}.${encodedPayload}`); if (signature !== expectedSignature) { return { valid: false, error: 'Invalid signature' }; } // 解析载荷 const payload = JSON.parse(this.base64UrlDecode(encodedPayload)) as JWTPayload & { iat: number; exp: number; iss: string; }; // 检查过期时间 const now = Math.floor(Date.now() / 1000); if (payload.exp && payload.exp < now) { return { valid: false, error: 'Token expired' }; } // 检查发行者 if (payload.iss !== 'vibe-coding-mcp') { return { valid: false, error: 'Invalid issuer' }; } return { valid: true, payload: { userId: payload.userId, sessionId: payload.sessionId, deviceId: payload.deviceId, permissions: payload.permissions } }; } catch (error) { return { valid: false, error: 'Token parsing failed' }; } } /** * 解析JWT token(不验证签名) * @param token JWT token字符串 * @returns 解析的载荷 */ static parseToken(token: string): JWTPayload | null { try { const parts = token.split('.'); if (parts.length !== 3) { return null; } const payload = JSON.parse(this.base64UrlDecode(parts[1])) as JWTPayload; return payload; } catch (error) { return null; } } /** * 检查token是否即将过期 * @param token JWT token字符串 * @param thresholdMinutes 阈值分钟数(默认30分钟) * @returns 是否即将过期 */ static isTokenExpiringSoon(token: string, thresholdMinutes: number = 30): boolean { try { const parts = token.split('.'); if (parts.length !== 3) { return true; } const payload = JSON.parse(this.base64UrlDecode(parts[1])) as { exp?: number }; if (!payload.exp) { return false; } const now = Math.floor(Date.now() / 1000); const threshold = thresholdMinutes * 60; return payload.exp - now <= threshold; } catch (error) { return true; } } /** * Base64 URL编码 */ private static base64UrlEncode(str: string): string { return Buffer.from(str) .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } /** * Base64 URL解码 */ private static base64UrlDecode(str: string): string { // 补充padding str += '='.repeat((4 - str.length % 4) % 4); // 替换URL安全字符 str = str.replace(/-/g, '+').replace(/_/g, '/'); return Buffer.from(str, 'base64').toString(); } /** * 创建HMAC签名 */ private static createSignature(data: string): string { const crypto = require('crypto'); const hmac = crypto.createHmac('sha256', this.SECRET_KEY); hmac.update(data); return this.base64UrlEncode(hmac.digest('base64')); } /** * 解析过期时间字符串 */ private static parseExpiresIn(expiresIn: string): number { const match = expiresIn.match(/^(\d+)([smhd])$/); if (!match) { throw new Error('Invalid expiresIn format'); } const value = parseInt(match[1]); const unit = match[2]; switch (unit) { case 's': return value; case 'm': return value * 60; case 'h': return value * 60 * 60; case 'd': return value * 60 * 60 * 24; default: throw new Error('Invalid time unit'); } } } /** * JWT中间件类型 */ export interface JWTMiddleware { /** * 验证请求中的JWT token */ authenticate(authHeader?: string): { success: boolean; payload?: JWTPayload; error?: string; }; } /** * JWT中间件实现 */ export class JWTAuthMiddleware implements JWTMiddleware { /** * 验证请求中的JWT token * @param authHeader Authorization头部值 * @returns 验证结果 */ authenticate(authHeader?: string): { success: boolean; payload?: JWTPayload; error?: string; } { if (!authHeader) { return { success: false, error: 'Authorization header missing' }; } if (!authHeader.startsWith('Bearer ')) { return { success: false, error: 'Invalid authorization format' }; } const token = authHeader.substring(7); const result = JWTUtils.verifyToken(token); if (!result.valid) { return { success: false, error: result.error }; } return { success: true, payload: result.payload }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chenxing3060/vibedoge-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server