UNPKG

atp-sdk

Version:

Official TypeScript SDK for Agent Trust Protocolâ„¢ - Build secure, verifiable, and trustworthy applications with decentralized identity, verifiable credentials, and robust access control

190 lines • 6.94 kB
import axios from 'axios'; import { createJWT } from 'did-jwt'; import { ATPError, ATPNetworkError, ATPAuthenticationError } from '../types.js'; export class BaseClient { constructor(config, serviceKey) { this.config = this.normalizeConfig(config); const baseURL = this.config.services[serviceKey] || `${this.config.baseUrl}${this.getServicePath(serviceKey)}`; this.http = axios.create({ baseURL, timeout: this.config.timeout, headers: { 'Content-Type': 'application/json', 'User-Agent': 'ATP-SDK/0.1.0', ...this.config.headers } }); this.setupInterceptors(); } normalizeConfig(config) { return { baseUrl: config.baseUrl, services: { identity: config.services?.identity, credentials: config.services?.credentials, permissions: config.services?.permissions, audit: config.services?.audit, gateway: config.services?.gateway, ...config.services }, auth: { did: config.auth?.did, privateKey: config.auth?.privateKey, token: config.auth?.token, ...config.auth }, timeout: config.timeout || 30000, retries: config.retries || 3, retryDelay: config.retryDelay || 1000, debug: config.debug || false, headers: config.headers || {} }; } getServicePath(serviceKey) { const paths = { identity: ':3001', credentials: ':3002', permissions: ':3003', audit: ':3005', gateway: ':3000' }; return paths[serviceKey] || ''; } setupInterceptors() { // Request interceptor for authentication this.http.interceptors.request.use(async (config) => { if (this.config.auth.token) { config.headers.Authorization = `Bearer ${this.config.auth.token}`; } else if (this.config.auth.did && this.config.auth.privateKey) { const token = await this.generateJWT(); config.headers.Authorization = `Bearer ${token}`; } if (this.config.debug) { console.log(`[ATP SDK] ${config.method?.toUpperCase()} ${config.url}`, config.data); } return config; }, (error) => Promise.reject(error)); // Response interceptor for error handling this.http.interceptors.response.use((response) => { if (this.config.debug) { console.log(`[ATP SDK] Response:`, response.data); } return response; }, (error) => { const atpError = this.handleError(error); if (this.config.debug) { console.error(`[ATP SDK] Error:`, atpError); } return Promise.reject(atpError); }); } async generateJWT() { if (!this.config.auth.did || !this.config.auth.privateKey) { throw new ATPAuthenticationError('DID and private key required for JWT generation'); } try { // Create JWT payload const payload = { iss: this.config.auth.did, sub: this.config.auth.did, aud: 'atp-services', iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour nbf: Math.floor(Date.now() / 1000) }; // Sign JWT with DID private key const jwt = await createJWT(payload, { issuer: this.config.auth.did, signer: () => Promise.resolve(this.config.auth.privateKey) }, { alg: 'EdDSA' }); return jwt; } catch (error) { throw new ATPAuthenticationError(`Failed to generate JWT: ${error}`); } } handleError(error) { if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') { return new ATPNetworkError(`Network error: ${error.message}`, error); } if (error.response) { const { status, data } = error.response; switch (status) { case 401: return new ATPAuthenticationError(data?.error || 'Authentication failed'); case 403: return new ATPError(data?.error || 'Authorization failed', 'AUTHZ_ERROR', 403); case 400: return new ATPError(data?.error || 'Bad request', 'VALIDATION_ERROR', 400, data?.details); case 404: return new ATPError(data?.error || 'Not found', 'NOT_FOUND', 404); case 500: return new ATPError(data?.error || 'Internal server error', 'SERVER_ERROR', 500); default: return new ATPError(data?.error || 'Unknown error', 'UNKNOWN_ERROR', status); } } return new ATPError(error.message || 'Unknown error occurred'); } async request(method, url, data, config) { try { const response = await this.http.request({ method, url, data, ...config }); return response.data; } catch (error) { throw error; // Re-throw as it's already handled by interceptor } } async get(url, config) { return this.request('GET', url, undefined, config); } async post(url, data, config) { return this.request('POST', url, data, config); } async put(url, data, config) { return this.request('PUT', url, data, config); } async delete(url, config) { return this.request('DELETE', url, undefined, config); } async patch(url, data, config) { return this.request('PATCH', url, data, config); } /** * Update authentication token */ setAuthToken(token) { this.config.auth.token = token; } /** * Update DID and private key for authentication */ setDIDAuth(did, privateKey) { this.config.auth.did = did; this.config.auth.privateKey = privateKey; this.config.auth.token = undefined; // Clear token when using DID auth } /** * Update authentication configuration */ updateAuth(auth) { this.config.auth = { ...this.config.auth, ...auth }; } /** * Check if client is authenticated */ isAuthenticated() { return !!(this.config.auth.token || (this.config.auth.did && this.config.auth.privateKey)); } /** * Get current configuration */ getConfig() { return { ...this.config }; } } //# sourceMappingURL=base.js.map