UNPKG

atp-sdk

Version:

Official TypeScript SDK for Agent Trust Protocol™ - Build secure, verifiable, and trustworthy applications with decentralized identity, verifiable credentials, payment protocols (AP2/ACP), and robust access control

235 lines 7.22 kB
import { SignJWT, jwtVerify } from 'jose'; /** * JWT utilities for ATP™ SDK */ export class JWTUtils { /** * Create a DID-JWT token */ static async createDIDJWT(payload, privateKey, did, options) { const privateKeyBuffer = Buffer.from(privateKey, 'hex'); const jwt = new SignJWT({ ...payload, iss: options?.issuer || did, sub: did, aud: options?.audience || 'atp:services' }) .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT', kid: `${did}#key-1` }) .setIssuedAt(); if (options?.expiresIn) { if (typeof options.expiresIn === 'string') { jwt.setExpirationTime(options.expiresIn); } else { jwt.setExpirationTime(Math.floor(Date.now() / 1000) + options.expiresIn); } } else { jwt.setExpirationTime('1h'); // Default 1 hour } return jwt.sign(privateKeyBuffer); } /** * Verify a DID-JWT token */ static async verifyDIDJWT(token, publicKey, options) { try { const publicKeyBuffer = Buffer.from(publicKey, 'hex'); const { payload } = await jwtVerify(token, publicKeyBuffer, { audience: options?.audience, issuer: options?.issuer, clockTolerance: options?.clockTolerance || 30 // 30 seconds }); return { valid: true, payload }; } catch (error) { return { valid: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } /** * Decode JWT without verification */ static decodeJWT(token) { try { const parts = token.split('.'); if (parts.length !== 3) { return null; } const header = JSON.parse(Buffer.from(parts[0], 'base64url').toString()); const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString()); const signature = parts[2]; return { header, payload, signature }; } catch { return null; } } /** * Check if JWT is expired */ static isExpired(token) { const decoded = this.decodeJWT(token); if (!decoded || !decoded.payload.exp) { return true; } const now = Math.floor(Date.now() / 1000); return decoded.payload.exp < now; } /** * Get time until expiration in seconds */ static getTimeToExpiration(token) { const decoded = this.decodeJWT(token); if (!decoded || !decoded.payload.exp) { return 0; } const now = Math.floor(Date.now() / 1000); return Math.max(0, decoded.payload.exp - now); } /** * Create authentication token for ATP services */ static async createAuthToken(did, privateKey, options) { const payload = { did, permissions: options?.permissions || [], trustLevel: options?.trustLevel || 'BASIC' }; return this.createDIDJWT(payload, privateKey, did, { audience: options?.audience || 'atp:services', expiresIn: options?.expiresIn || '1h' }); } /** * Create capability token */ static async createCapabilityToken(issuer, subject, capabilities, privateKey, options) { const payload = { sub: subject, capabilities, restrictions: options?.restrictions, tokenType: 'capability' }; return this.createDIDJWT(payload, privateKey, issuer, { audience: options?.audience || 'atp:services', expiresIn: options?.expiresIn || '24h' }); } /** * Verify capability token */ static async verifyCapabilityToken(token, publicKey, requiredCapability) { const result = await this.verifyDIDJWT(token, publicKey); if (!result.valid) { return result; } const payload = result.payload; if (payload.tokenType !== 'capability') { return { valid: false, error: 'Not a capability token' }; } const capabilities = payload.capabilities || []; if (requiredCapability && !capabilities.includes(requiredCapability)) { return { valid: false, error: `Missing required capability: ${requiredCapability}` }; } return { valid: true, capabilities, subject: payload.sub, restrictions: payload.restrictions }; } /** * Create presentation token for verifiable credentials */ static async createPresentationToken(holder, audience, credentialIds, privateKey, options) { const payload = { vp: { '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation'], verifiableCredential: credentialIds, holder }, challenge: options?.challenge, tokenType: 'presentation' }; return this.createDIDJWT(payload, privateKey, holder, { audience, expiresIn: options?.expiresIn || '15m' }); } /** * Extract DID from JWT token */ static extractDID(token) { const decoded = this.decodeJWT(token); return decoded?.payload?.iss || decoded?.payload?.sub || null; } /** * Extract trust level from JWT token */ static extractTrustLevel(token) { const decoded = this.decodeJWT(token); return decoded?.payload?.trustLevel || null; } /** * Extract permissions from JWT token */ static extractPermissions(token) { const decoded = this.decodeJWT(token); return decoded?.payload?.permissions || []; } /** * Create refresh token */ static async createRefreshToken(did, privateKey, tokenId, options) { const payload = { tokenType: 'refresh', jti: tokenId, scope: 'refresh' }; return this.createDIDJWT(payload, privateKey, did, { audience: 'atp:auth', expiresIn: options?.expiresIn || '30d' }); } /** * Verify refresh token */ static async verifyRefreshToken(token, publicKey) { const result = await this.verifyDIDJWT(token, publicKey, { audience: 'atp:auth' }); if (!result.valid) { return result; } const payload = result.payload; if (payload.tokenType !== 'refresh') { return { valid: false, error: 'Not a refresh token' }; } return { valid: true, tokenId: payload.jti, did: payload.iss }; } } //# sourceMappingURL=jwt.js.map