Skip to main content
Glama
dyeoman2

Clerk MCP Server Template

by dyeoman2
utils.ts2.82 kB
import { type AuthRequest } from '@cloudflare/workers-oauth-provider' import { type KVNamespace } from '@cloudflare/workers-types' export interface AuthSession { sessionId: string clientId: string timestamp: number expiresAt: number } interface AuthState { sessionId: string authRequest: AuthRequest } /** * Encode auth state with HMAC signature for tamper protection */ export async function encodeAuthState( state: AuthState, secret: string, ): Promise<string> { const stateJson = JSON.stringify(state) const encoder = new TextEncoder() // Create HMAC key const key = await crypto.subtle.importKey( 'raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'], ) // Sign the state const signature = await crypto.subtle.sign( 'HMAC', key, encoder.encode(stateJson), ) // Combine state and signature const combined = { data: btoa(stateJson), sig: btoa(String.fromCharCode(...new Uint8Array(signature))), } return btoa(JSON.stringify(combined)) } /** * Decode and verify auth state */ export async function decodeAuthState( encodedState: string, secret: string, ): Promise<AuthState | null> { try { const combined = JSON.parse(atob(encodedState)) const stateJson = atob(combined.data) const signature = new Uint8Array( atob(combined.sig) .split('') .map((c) => c.charCodeAt(0)), ) const encoder = new TextEncoder() // Create HMAC key const key = await crypto.subtle.importKey( 'raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify'], ) // Verify signature const valid = await crypto.subtle.verify( 'HMAC', key, signature, encoder.encode(stateJson), ) if (!valid) { return null } return JSON.parse(stateJson) as AuthState } catch { log.error('Error decoding auth state') return null } } /** * Clean up expired auth sessions from KV */ export async function cleanupExpiredRequests(kv: KVNamespace): Promise<void> { // This is a simple implementation - in production you might want // to use a more efficient approach like a scheduled worker const { keys } = await kv.list({ prefix: 'auth_session:' }) for (const key of keys) { const data = await kv.get(key.name) if (data) { try { const session: AuthSession = JSON.parse(data) if (Date.now() > session.expiresAt) { await kv.delete(key.name) } } catch { // Invalid data, delete it await kv.delete(key.name) } } } } export const log = { info: (msg: string, data?: any) => console.log('INFO:', msg, data ? JSON.stringify(data) : ''), error: (msg: string, data?: any) => console.error('ERROR:', msg, data ? JSON.stringify(data) : ''), warn: (msg: string, data?: any) => console.warn('WARN:', msg, data ? JSON.stringify(data) : ''), }

Latest Blog Posts

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/dyeoman2/clerk-mcp-template'

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