Skip to main content
Glama

Curupira

by drzln
jwt.ts5.66 kB
/** * @fileoverview JWT token management * * This file provides JWT token creation, validation, and refresh functionality. */ import jwt from 'jsonwebtoken' import type { JwtPayload, AuthConfig, AuthResult, AuthUser, TokenValidation, AuthErrorCode } from './types.js' import { createUserId, createSessionId, type UserId, type SessionId } from '../../types/index.js' import { SecurityErrors } from '../../errors/index.js' /** * JWT token manager */ export class JwtManager { constructor(private readonly config: AuthConfig) {} /** * Create authentication tokens */ async createTokens( userId: UserId, sessionId: SessionId, roles: string[] = [], claims?: Record<string, unknown> ): Promise<AuthResult> { const now = Math.floor(Date.now() / 1000) // Create access token const accessPayload: JwtPayload = { sub: userId, sessionId, iat: now, exp: now + this.config.accessTokenTTL, type: 'access', roles, claims } const accessToken = jwt.sign(accessPayload, this.config.secret, { algorithm: this.config.algorithm, issuer: this.config.issuer, audience: this.config.audience }) // Create refresh token if enabled let refreshToken: string | undefined if (this.config.enableRefresh) { const refreshPayload: JwtPayload = { sub: userId, sessionId, iat: now, exp: now + this.config.refreshTokenTTL, type: 'refresh' } refreshToken = jwt.sign(refreshPayload, this.config.secret, { algorithm: this.config.algorithm, issuer: this.config.issuer, audience: this.config.audience }) } // Create user object const user: AuthUser = { id: userId, sessionId, roles, permissions: this.calculatePermissions(roles), metadata: claims } return { accessToken, refreshToken, expiresIn: this.config.accessTokenTTL, tokenType: 'Bearer', user } } /** * Validate a token */ async validateToken(token: string): Promise<TokenValidation> { try { const payload = jwt.verify(token, this.config.secret, { algorithms: [this.config.algorithm], issuer: this.config.issuer, audience: this.config.audience }) as JwtPayload return { valid: true, payload } } catch (error: unknown) { if (error instanceof jwt.TokenExpiredError) { return { valid: false, error: 'TOKEN_EXPIRED' as AuthErrorCode, message: 'Token has expired' } } if (error instanceof jwt.JsonWebTokenError) { return { valid: false, error: 'INVALID_TOKEN' as AuthErrorCode, message: error.message } } return { valid: false, error: 'INVALID_TOKEN' as AuthErrorCode, message: 'Token validation failed' } } } /** * Refresh access token using refresh token */ async refreshToken(refreshToken: string): Promise<AuthResult> { if (!this.config.enableRefresh) { throw SecurityErrors.unauthorized('Token refresh is not enabled') } const validation = await this.validateToken(refreshToken) if (!validation.valid || !validation.payload) { throw SecurityErrors.unauthorized( validation.message || 'Invalid refresh token' ) } if (validation.payload.type !== 'refresh') { throw SecurityErrors.unauthorized('Token is not a refresh token') } // Create new tokens with same user info return this.createTokens( validation.payload.sub, validation.payload.sessionId, validation.payload.roles || [], validation.payload.claims ) } /** * Extract user from validated token */ extractUser(payload: JwtPayload): AuthUser { return { id: payload.sub, sessionId: payload.sessionId, roles: payload.roles || [], permissions: this.calculatePermissions(payload.roles || []), metadata: payload.claims } } /** * Calculate permissions from roles */ private calculatePermissions(roles: string[]): string[] { // Default role-based permissions const rolePermissions: Record<string, string[]> = { admin: ['*'], developer: [ 'debug:read', 'debug:write', 'state:read', 'state:write', 'tools:execute' ], viewer: ['debug:read', 'state:read'] } const permissions = new Set<string>() for (const role of roles) { const perms = rolePermissions[role] || [] for (const perm of perms) { permissions.add(perm) } } return Array.from(permissions) } } /** * Create JWT manager instance */ export function createJwtManager(config: AuthConfig): JwtManager { return new JwtManager(config) } /** * Parse authorization header */ export function parseAuthHeader(header: string): string | null { const parts = header.split(' ') if (parts.length !== 2 || parts[0] !== 'Bearer') { return null } return parts[1] } /** * Check if user has permission */ export function hasPermission( user: AuthUser, resource: string, action: string ): boolean { const permission = `${resource}:${action}` // Check wildcard permission if (user.permissions.includes('*')) { return true } // Check specific permission if (user.permissions.includes(permission)) { return true } // Check resource wildcard if (user.permissions.includes(`${resource}:*`)) { return true } return false }

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/drzln/curupira'

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