Skip to main content
Glama
AlyssonM

HiveAuth MCP Server

by AlyssonM
refreshCredential.tsβ€’10.7 kB
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { RefreshCredentialInputSchema } from '../schemas/toolSchemas.js'; import { validateAndSanitizeInput, createValidationErrorResult } from '../utils/validation.js'; /** * Refresh an expiring or expired credential using W3C Credential Refresh specification * Supports automatic refresh service discovery and manual refresh endpoints */ export async function refreshCredential(args: any): Promise<CallToolResult> { // Validate and sanitize input const validation = validateAndSanitizeInput(RefreshCredentialInputSchema, args, 'refresh_credential'); if (!validation.success) { return createValidationErrorResult(validation.error!); } const data = validation.data!; const { credentialId, refreshService } = data; const HIVEAUTH_API_BASE_URL = process.env.HIVEAUTH_API_BASE_URL || 'http://localhost:3000'; const REFRESH_ENDPOINT = `${HIVEAUTH_API_BASE_URL}/api/refresh`; try { console.log(`[RefreshCredential] Starting credential refresh for: ${credentialId}`); const payload = { credentialId, ...(refreshService && { refreshService }) }; const response = await fetch(REFRESH_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: response.statusText })); throw new Error(`Failed to refresh credential: ${errorData.message}`); } const result = await response.json(); // Analyze the refresh operation const oldCredential = result.originalCredential; const newCredential = result.refreshedCredential; const refreshInfo = result.refreshInfo || {}; const summary = [ `πŸ”„ **Credential Refresh Results**`, ``, `β€’ Original Credential ID: ${credentialId}`, `β€’ New Credential ID: ${newCredential?.id || 'Generated automatically'}`, `β€’ Refresh Method: ${refreshInfo.method || 'W3C Credential Refresh'}`, `β€’ Refresh Service: ${refreshInfo.serviceEndpoint || refreshService || 'Auto-discovered'}`, `β€’ Refresh Status: ${result.success ? 'βœ… Successful' : '❌ Failed'}`, `` ]; if (result.success && oldCredential && newCredential) { // Compare expiration dates const oldExpiry = oldCredential.expirationDate || oldCredential.validUntil; const newExpiry = newCredential.expirationDate || newCredential.validUntil; summary.push(`**πŸ“… Validity Period Comparison:**`); if (oldExpiry) { summary.push(`β€’ Original Expiry: ${oldExpiry}`); const wasExpired = new Date(oldExpiry) < new Date(); summary.push(`β€’ Original Status: ${wasExpired ? '❌ Expired' : '⚠️ Expiring soon'}`); } else { summary.push(`β€’ Original Expiry: No expiration date set`); } if (newExpiry) { summary.push(`β€’ New Expiry: ${newExpiry}`); const timeUntilExpiry = new Date(newExpiry).getTime() - new Date().getTime(); const daysUntilExpiry = Math.ceil(timeUntilExpiry / (1000 * 60 * 60 * 24)); summary.push(`β€’ New Status: βœ… Valid for ${daysUntilExpiry} more days`); } else { summary.push(`β€’ New Expiry: No expiration date (indefinite validity)`); } summary.push(``); // Compare credential content summary.push(`**πŸ” Content Comparison:**`); const contentChanges = compareCredentialContent(oldCredential, newCredential); if (contentChanges.length === 0) { summary.push(`β€’ Content: βœ… Unchanged (only validity period updated)`); } else { summary.push(`β€’ Content: ⚠️ ${contentChanges.length} changes detected`); contentChanges.forEach((change, index) => { summary.push(` ${index + 1}. ${change}`); }); } summary.push(``); // Issuer and signature information summary.push(`**πŸ” Security Information:**`); summary.push(`β€’ Original Issuer: ${getIssuerInfo(oldCredential)}`); summary.push(`β€’ New Issuer: ${getIssuerInfo(newCredential)}`); summary.push(`β€’ Issuer Consistency: ${getIssuerInfo(oldCredential) === getIssuerInfo(newCredential) ? 'βœ… Same issuer' : '⚠️ Different issuer'}`); if (newCredential.proof) { summary.push(`β€’ New Signature: βœ… Fresh cryptographic signature`); summary.push(`β€’ Proof Type: ${newCredential.proof.type || 'Unknown'}`); } summary.push(``); // Refresh service information if (refreshInfo.serviceDiscovered) { summary.push(`**πŸ”Ž Service Discovery:**`); summary.push(`β€’ Service Discovery: βœ… Automatic discovery successful`); summary.push(`β€’ Discovered Endpoint: ${refreshInfo.serviceEndpoint}`); summary.push(`β€’ Discovery Method: ${refreshInfo.discoveryMethod || 'DID Document'}`); summary.push(``); } // Chain of trust if (result.trustChain) { summary.push(`**πŸ”— Trust Chain:**`); summary.push(`β€’ Original credential linked: βœ… Cryptographic reference maintained`); summary.push(`β€’ Chain integrity: ${result.trustChain.valid ? 'βœ… Valid' : '❌ Broken'}`); summary.push(`β€’ Refresh authorization: ${result.trustChain.authorized ? 'βœ… Authorized' : '❌ Unauthorized'}`); summary.push(``); } } else if (!result.success) { summary.push(`**❌ Refresh Failed:**`); summary.push(`β€’ Reason: ${result.error || 'Unknown error'}`); if (result.retryable) { summary.push(`β€’ Retry Possible: βœ… Yes, try again later`); summary.push(`β€’ Suggested Retry: ${result.retryAfter || 'No specific time suggested'}`); } else { summary.push(`β€’ Retry Possible: ❌ No, manual intervention required`); } summary.push(``); } // Usage recommendations summary.push(`**πŸ’‘ Next Steps:**`); if (result.success) { summary.push(`β€’ Replace the original credential with the refreshed version`); summary.push(`β€’ Update any stored references to use the new credential ID`); summary.push(`β€’ Verify the refreshed credential independently`); summary.push(`β€’ Set up monitoring for the new expiration date`); if (result.originalCredential) { summary.push(`β€’ Consider revoking the original credential if policy requires it`); } } else { summary.push(`β€’ Check credential refresh service availability`); summary.push(`β€’ Verify the credential is eligible for refresh`); summary.push(`β€’ Contact the issuer if automatic refresh fails`); summary.push(`β€’ Consider manual credential renewal process`); } return { content: [ { type: 'text', text: summary.join('\n') }, { type: 'text', text: `\`\`\`json\n${JSON.stringify({ refreshSummary: { credentialId, success: result.success, refreshMethod: refreshInfo.method, serviceEndpoint: refreshInfo.serviceEndpoint, originalExpiry: oldCredential?.expirationDate || oldCredential?.validUntil, newExpiry: newCredential?.expirationDate || newCredential?.validUntil, contentChanges: result.success ? compareCredentialContent(oldCredential, newCredential).length : 0 }, refreshedCredential: result.success ? newCredential : null, refreshInfo: refreshInfo }, null, 2)}\n\`\`\`` } ] }; } catch (error: any) { // Check if it's a network error (HiveAuth API not available) if (error.message.includes('fetch failed') || error.message.includes('ECONNREFUSED')) { return { content: [ { type: 'text', text: `πŸ”„ **Credential Refresh (Simulation Mode)**\n\n` + `Since the HiveAuth API is not available, here's what the credential refresh would accomplish:\n\n` + `**Input Analysis:**\n` + `β€’ Credential ID: ${credentialId}\n` + `β€’ Refresh Service: ${refreshService || 'Auto-discovery from credential'}\n\n` + `**Expected Refresh Process:**\n` + `β€’ Locate credential by ID in the system\n` + `β€’ Check expiration status and refresh eligibility\n` + `β€’ Discover or use specified refresh service endpoint\n` + `β€’ Request new credential with extended validity period\n` + `β€’ Maintain credential content while updating temporal properties\n` + `β€’ Generate fresh cryptographic signatures\n\n` + `**W3C Credential Refresh Benefits:**\n` + `β€’ Automated credential lifecycle management\n` + `β€’ Seamless validity period extension\n` + `β€’ Maintains trust chain and cryptographic integrity\n` + `β€’ Reduces manual intervention for routine renewals\n\n` + `**To enable full refresh:** Ensure HiveAuth API is running at ${HIVEAUTH_API_BASE_URL}` } ] }; } return { content: [ { type: 'text', text: `Failed to refresh credential: ${error.message}` } ], isError: true }; } } /** * Compare two credentials and return list of changes */ function compareCredentialContent(oldCred: any, newCred: any): string[] { const changes: string[] = []; if (!oldCred || !newCred) return changes; // Compare credential subject if (JSON.stringify(oldCred.credentialSubject) !== JSON.stringify(newCred.credentialSubject)) { changes.push('Credential subject data modified'); } // Compare types if (JSON.stringify(oldCred.type) !== JSON.stringify(newCred.type)) { changes.push('Credential types changed'); } // Compare issuer if (getIssuerInfo(oldCred) !== getIssuerInfo(newCred)) { changes.push('Issuer information changed'); } // Compare context if (JSON.stringify(oldCred['@context']) !== JSON.stringify(newCred['@context'])) { changes.push('JSON-LD context updated'); } return changes; } /** * Extract issuer information from credential */ function getIssuerInfo(credential: any): string { if (!credential?.issuer) return 'Unknown'; if (typeof credential.issuer === 'string') { return credential.issuer; } return credential.issuer.id || credential.issuer.name || 'Unknown'; }

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/AlyssonM/hiveauth-mcp'

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