Skip to main content
Glama

Practera MCP Server

by intersective
auth.ts6.66 kB
import jwt from 'jsonwebtoken'; import type { Request, Response, NextFunction } from 'express'; interface AuthConfig { apikey?: string; accessToken?: string; appkey?: string; } interface AuthOptions { allowApikey?: boolean; allowOAuth?: boolean; } /** * Authentication helper for Practera API * Handles both API key and OAuth authentication */ export class PracteraAuth { private apikey?: string; private accessToken?: string; private appkey: string; public timelineId?: string; public role?: string; /** * Create a new authentication helper * @param {Object} config Configuration options * @param {string} [config.apikey] API key for direct auth * @param {string} [config.accessToken] OAuth access token * @param {string} [config.appKey] App key (if needed) */ constructor(config: AuthConfig = {}) { this.apikey = config.apikey; this.accessToken = config.accessToken; this.appkey = config.appkey || ''; if (typeof config.apikey === 'string') { const decoded = jwt.decode(config.apikey) as any; this.timelineId = decoded.timeline_id || null; this.role = decoded.role || 'none'; } // Verify we have at least one auth method if (!this.apikey && !this.accessToken) { throw new Error('Either apikey or accessToken is required for authentication'); } } /** * Get headers for GraphQL requests * @returns {Object} Headers object */ getHeaders(): Record<string, string> { // Prefer OAuth token if available if (this.accessToken) { return { 'Authorization': `Bearer ${this.accessToken}`, 'appkey': this.appkey }; } if (!this.apikey) { throw new Error('No API key provided'); } // Fall back to API key return { 'apikey': this.apikey || '', 'appkey': this.appkey, 'timelineId': this.timelineId || '' }; } /** * Verify an OAuth token and extract claims * @param {string} token OAuth access token * @returns {Promise<Object>} Token claims */ static async verifyToken(token: string): Promise<Record<string, any>> { try { // This is a placeholder - in a real implementation, you would verify // the token with the issuer or use a library specific to your OAuth provider // For JWT tokens, you might do something like: const decoded = jwt.decode(token); if (!decoded) { throw new Error('Invalid token format'); } // Check token expiration if (typeof decoded === 'object') { if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) { throw new Error('Token expired'); } } return decoded as Record<string, any>; } catch (error) { console.error('Token verification failed:', error); throw new Error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Verify an API key and extract claims * @param {string} apikey API key * @returns {Promise<Object>} Token claims */ static async verifyApikey(apikey: string, role?: string): Promise<Record<string, any>> { try { // This is a placeholder - in a real implementation, you would verify // the token with the issuer or use a library specific to your OAuth provider // For JWT tokens, you might do something like: const decoded = jwt.decode(apikey); if (!decoded) { throw new Error('Invalid token format'); } // Check token expiration if (typeof decoded === 'object') { if (decoded.exp && decoded.exp < Math.floor(Date.now() / 1000)) { throw new Error('Token expired'); } if (role && decoded.role !== role) { throw new Error('Unauthorized'); } } return decoded as Record<string, any>; } catch (error) { console.error('Token verification failed:', error); throw new Error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Create an auth object from request headers * @param {Object} headers Request headers * @returns {PracteraAuth} Authentication helper */ static fromHeaders(headers: Record<string, string>): PracteraAuth { // Extract Bearer token if present const authHeader = headers.authorization || headers.Authorization; if (authHeader && authHeader.startsWith('Bearer ')) { const token = authHeader.substring(7); return new PracteraAuth({ accessToken: token }); } // Extract API key if present const apikey = headers.apikey || headers.apikey || headers['x-api-key']; if (apikey) { return new PracteraAuth({ apikey }); } throw new Error('No authentication credentials found in headers'); } } /** * Express middleware to require authentication * @param {Object} options Middleware options * @param {boolean} [options.allowApikey=true] Allow API key authentication * @param {boolean} [options.allowOAuth=true] Allow OAuth authentication * @returns {Function} Express middleware */ export function requireAuth(options: AuthOptions = { allowApikey: true, allowOAuth: true }) { return async (req: Request, res: Response, next: NextFunction) => { try { // Check for OAuth token const authHeader = req.headers.authorization || req.headers.Authorization as string | undefined; if (options.allowOAuth && authHeader && authHeader.startsWith('Bearer ')) { const token = authHeader.substring(7); // Verify token const claims = await PracteraAuth.verifyToken(token); // Attach claims to request (req as any).user = claims; return next(); } // Check for API key const apikey = req.headers.apikey || req.headers.apikey || req.headers['x-api-key'] as string | undefined; if (options.allowApikey && apikey) { // API key auth is simpler - just verify it exists // In a real implementation, you might validate it against a database // Verify token const user = await PracteraAuth.verifyApikey(apikey as string, 'admin'); (req as any).apikey = apikey; (req as any).user = user; return next(); } // No valid authentication res.status(401).json({ error: 'Unauthorized: Valid authentication required' }); } catch (error) { console.error('Authentication error:', error); res.status(401).json({ error: `Unauthorized: ${error instanceof Error ? error.message : String(error)}` }); } }; }

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/intersective/practera-mcp-server'

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