Skip to main content
Glama
by payware
jwt-factory.js5.74 kB
/** * JWT factory for different partner types * Handles the different authentication patterns required by merchant, ISV, and payment institution partners */ import { createJWTToken } from './jwt-token.js'; import { getPartnerTypeSafe, isISV, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js'; /** * Create JWT token appropriate for the current partner type * @param {Object} options - JWT creation options * @param {string} options.partnerId - Partner ID (defaults to env var) * @param {string} options.privateKey - Private key (defaults to env var) * @param {Object} options.requestBody - Request body for POST/PUT/PATCH requests * @param {string} options.merchantId - Target merchant ID (ISV only) * @param {string} options.oauth2Token - OAuth2 token (ISV only) * @returns {Object} JWT token data */ export async function createJWTForPartner(options = {}) { const partnerType = getPartnerTypeSafe(); const { partnerId = getPartnerIdSafe(), privateKey = getPrivateKeySafe(), requestBody = null, merchantId = null, oauth2Token = null } = options; switch (partnerType) { case 'merchant': case 'payment_institution': return createStandardJWT({ partnerId, privateKey, requestBody }); case 'isv': return await createISVJWT({ isvPartnerId: partnerId, merchantPartnerId: merchantId, oauth2Token, privateKey, requestBody }); default: throw new Error(`Unsupported partner type: ${partnerType}`); } } /** * Create standard JWT for merchant and payment institution partners * @param {Object} options - JWT options * @returns {Object} JWT token data */ function createStandardJWT({ partnerId, privateKey, requestBody }) { if (!partnerId || !privateKey) { throw new Error('Partner ID and private key are required for standard JWT creation'); } return createJWTToken(partnerId, privateKey, requestBody); } /** * Create ISV JWT with OAuth2 token and merchant audience * @param {Object} options - ISV JWT options * @returns {Object} JWT token data with ISV-specific claims */ async function createISVJWT({ isvPartnerId, merchantPartnerId, oauth2Token, privateKey, requestBody }) { if (!isvPartnerId || !privateKey) { throw new Error('ISV Partner ID and private key are required for ISV JWT creation'); } if (!merchantPartnerId) { throw new Error('Merchant Partner ID is required for ISV JWT creation'); } if (!oauth2Token) { throw new Error('OAuth2 token is required for ISV JWT creation'); } // For ISV, we need to create a custom JWT with different audience and subject const now = Math.floor(Date.now() / 1000); // Create the JWT with ISV-specific claims const payload = { iss: isvPartnerId, // ISV's partner ID aud: merchantPartnerId, // Target merchant's partner ID sub: oauth2Token, // OAuth2 token granting permission iat: now }; // Use the base JWT creation with custom payload return await createJWTTokenWithCustomPayload(payload, privateKey, requestBody); } /** * Create JWT token with custom payload (for ISV use case) * @param {Object} payload - Custom JWT payload * @param {string} privateKey - Private key for signing * @param {Object} requestBody - Request body for content MD5 * @returns {Object} JWT token data */ async function createJWTTokenWithCustomPayload(payload, privateKey, requestBody) { // Dynamic imports to avoid circular dependencies const jwt = await import('jsonwebtoken'); const crypto = await import('crypto'); const { normalizePrivateKey } = await import('../utils/key-utils.js'); const normalizedKey = normalizePrivateKey(privateKey); // JWT Header const header = { alg: 'RS256', typ: 'JWT' }; // Add contentMd5 to header if request body exists if (requestBody) { const { createMinimizedJSON } = await import('../utils/json-serializer.js'); const bodyString = createMinimizedJSON(requestBody); header.contentMd5 = crypto.default.createHash('md5').update(bodyString, 'utf8').digest('base64'); } const token = jwt.default.sign(payload, normalizedKey, { algorithm: 'RS256', header: header }); return { token, partnerId: payload.iss, audience: payload.aud, subject: payload.sub, issuedAt: new Date(payload.iat * 1000).toISOString(), contentMd5: header.contentMd5 || null, hasBody: !!requestBody, isISV: true }; } /** * Validate JWT creation parameters based on partner type * @param {Object} options - JWT options to validate * @throws {Error} If validation fails */ export function validateJWTOptions(options = {}) { const partnerType = getPartnerTypeSafe(); const { partnerId, privateKey, merchantId, oauth2Token } = options; // Common validation if (!partnerId) { throw new Error('Partner ID is required'); } if (!privateKey) { throw new Error('Private key is required'); } // ISV-specific validation if (partnerType === 'isv') { if (!merchantId) { throw new Error('Merchant ID is required for ISV partner type'); } if (!oauth2Token) { throw new Error('OAuth2 token is required for ISV partner type'); } } } /** * Get authentication headers for API requests * @param {string} jwt - JWT token * @returns {Object} Headers object */ export function getAuthHeaders(jwt) { return { 'Authorization': `Bearer ${jwt}`, 'Content-Type': 'application/json', 'Api-Version': '1' }; }

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

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