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

667 lines (531 loc) 15.9 kB
# Authentication Guide This guide covers all authentication methods and patterns supported by the ATP™ SDK. ## Table of Contents 1. [Authentication Overview](#authentication-overview) 2. [DID-based Authentication](#did-based-authentication) 3. [JWT Token Authentication](#jwt-token-authentication) 4. [Multi-Factor Authentication](#multi-factor-authentication) 5. [Service-to-Service Authentication](#service-to-service-authentication) 6. [Token Management](#token-management) 7. [Security Best Practices](#security-best-practices) ## Authentication Overview The ATP™ SDK supports multiple authentication methods: - **DID + Private Key**: Self-sovereign identity authentication - **JWT Tokens**: Bearer token authentication - **API Keys**: Simple API key authentication - **Certificate-based**: X.509 client certificate authentication - **Multi-Factor**: Additional security layers ## DID-based Authentication ### Basic DID Authentication The primary authentication method uses Decentralized Identifiers (DIDs) with cryptographic key pairs: ```javascript import { ATPClient, DIDUtils } from '@atp/sdk'; // Generate a new DID and key pair const { did, keyPair } = await DIDUtils.generateDID({ network: 'testnet', method: 'atp' }); // Configure client with DID authentication const client = new ATPClient({ baseUrl: 'http://localhost', auth: { did: did, privateKey: keyPair.privateKey } }); ``` ### Loading Existing DIDs ```javascript // Load from environment variables const client = new ATPClient({ baseUrl: 'http://localhost', auth: { did: process.env.ATP_DID, privateKey: process.env.ATP_PRIVATE_KEY } }); // Load from secure key storage import { loadKeyFromVault } from './key-management'; const privateKey = await loadKeyFromVault(process.env.KEY_ID); client.setAuthentication({ did: 'did:atp:mainnet:your-identifier', privateKey: privateKey }); ``` ### DID Document Registration Before using a DID for authentication, it must be registered: ```javascript // Generate DID and register with ATP network const { did, document, keyPair } = await DIDUtils.generateDID(); // Register identity await client.identity.register({ did: did, document: document, metadata: { name: 'Your Application', type: 'service', purpose: 'API access' } }); console.log(`DID registered: ${did}`); ``` ### DID Key Rotation Regularly rotate cryptographic keys for security: ```javascript // Generate new key pair const newKeyPair = await CryptoUtils.generateKeyPair(); // Add new verification method to DID document const updatedDocument = DIDUtils.addVerificationMethod( currentDocument, newKeyPair.publicKey, ['authentication', 'assertionMethod'] ); // Update DID document await client.identity.update(did, { document: updatedDocument }); // Update client authentication client.setAuthentication({ did: did, privateKey: newKeyPair.privateKey }); console.log('DID key rotated successfully'); ``` ## JWT Token Authentication ### Using Pre-issued Tokens If you have a JWT token from another system: ```javascript const client = new ATPClient({ baseUrl: 'http://localhost', auth: { token: 'eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...' } }); ``` ### Creating JWT Tokens Create JWT tokens programmatically: ```javascript import { JWTUtils } from '@atp/sdk'; // Create authentication token const token = await JWTUtils.createAuthToken( 'did:atp:testnet:user', privateKey, { audience: 'atp:services', expiresIn: '1h', permissions: ['read', 'write'], trustLevel: 'VERIFIED' } ); // Use token with client client.setAuthentication({ token }); ``` ### Token Verification Verify incoming tokens: ```javascript // Verify token authenticity const verification = await JWTUtils.verifyDIDJWT( token, publicKey, { audience: 'atp:services', issuer: 'did:atp:testnet:trusted-issuer' } ); if (verification.valid) { console.log('Token is valid'); console.log('Payload:', verification.payload); } else { console.error('Token verification failed:', verification.error); } ``` ### Capability Tokens Use capability tokens for delegation: ```javascript // Create capability token const capabilityToken = await JWTUtils.createCapabilityToken( 'did:atp:testnet:issuer', // Issuer 'did:atp:testnet:delegate', // Subject/delegate ['document:read', 'report:generate'], // Capabilities issuerPrivateKey, { expiresIn: '24h', audience: 'atp:services', restrictions: { ipRange: '192.168.1.0/24', timeWindow: { start: '09:00', end: '17:00' } } } ); // Use capability token client.setAuthentication({ token: capabilityToken }); ``` ## Multi-Factor Authentication ### Setting Up MFA Enable MFA for enhanced security: ```javascript // Set up TOTP (Time-based One-Time Password) const mfaSetup = await client.identity.setupMFA({ method: 'totp', label: 'MyApp Production' }); console.log('MFA Secret:', mfaSetup.data.secret); console.log('QR Code:', mfaSetup.data.qrCodeUrl); // Set up SMS MFA const smsMfa = await client.identity.setupMFA({ method: 'sms', phoneNumber: '+1234567890' }); // Set up Email MFA const emailMfa = await client.identity.setupMFA({ method: 'email', emailAddress: 'user@example.com' }); ``` ### MFA Authentication Flow ```javascript // Step 1: Initial authentication with DID client.setAuthentication({ did: 'did:atp:testnet:user', privateKey: userPrivateKey }); // Step 2: Check if MFA is required try { await client.identity.resolve('did:atp:testnet:user'); } catch (error) { if (error instanceof ATPAuthenticationError && error.code === 'MFA_REQUIRED') { // MFA verification needed const mfaCode = await promptUserForMFACode(); // Verify MFA const mfaResult = await client.identity.verifyMFA({ method: 'totp', code: mfaCode }); if (mfaResult.data.verified) { console.log('MFA verification successful'); // Client is now fully authenticated } } } ``` ### MFA Recovery Handle MFA recovery scenarios: ```javascript // Generate recovery codes during MFA setup const recoverySetup = await client.identity.setupMFARecovery({ codeCount: 10 // Generate 10 recovery codes }); console.log('Recovery codes:', recoverySetup.data.recoveryCodes); // Use recovery code when primary MFA is unavailable const recoveryResult = await client.identity.verifyMFARecovery({ recoveryCode: 'RECOVERY-CODE-123' }); if (recoveryResult.data.verified) { // Disable compromised recovery code await client.identity.revokeRecoveryCode('RECOVERY-CODE-123'); } ``` ## Service-to-Service Authentication ### Machine-to-Machine Authentication For automated services: ```javascript // Service identity with long-lived credentials const serviceConfig = { baseUrl: 'https://api.atp.example.com', auth: { did: 'did:atp:mainnet:service-abc123', privateKey: await loadServiceKey(), // Service-specific options serviceType: 'automated', scope: ['audit:write', 'events:publish'] } }; const serviceClient = new ATPClient(serviceConfig); ``` ### Cross-Service Communication Authenticate between ATP services: ```javascript // Create service-to-service token const serviceToken = await JWTUtils.createDIDJWT( { service: 'audit-service', operation: 'log_event', requestId: generateRequestId() }, servicePrivateKey, 'did:atp:mainnet:audit-service', { audience: 'did:atp:mainnet:identity-service', expiresIn: '5m' // Short-lived for service calls } ); // Use for inter-service communication const response = await fetch('https://identity-service/api/validate', { headers: { 'Authorization': `Bearer ${serviceToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify(requestData) }); ``` ### Service Mesh Authentication For microservices architectures: ```javascript // Load service mesh certificates const serviceConfig = { baseUrl: 'https://atp-mesh.internal', tls: { cert: fs.readFileSync('/etc/certs/service.crt'), key: fs.readFileSync('/etc/certs/service.key'), ca: fs.readFileSync('/etc/certs/ca.crt') }, auth: { method: 'mutual-tls', serviceId: process.env.SERVICE_ID } }; ``` ## Token Management ### Token Lifecycle Manage token lifecycle automatically: ```javascript class TokenManager { constructor(client, did, privateKey) { this.client = client; this.did = did; this.privateKey = privateKey; this.currentToken = null; this.refreshToken = null; } async ensureValidToken() { // Check if current token is expired if (!this.currentToken || JWTUtils.isExpired(this.currentToken)) { await this.refreshAuthToken(); } return this.currentToken; } async refreshAuthToken() { if (this.refreshToken && !JWTUtils.isExpired(this.refreshToken)) { // Use refresh token this.currentToken = await this.exchangeRefreshToken(); } else { // Create new token with DID this.currentToken = await JWTUtils.createAuthToken( this.did, this.privateKey, { expiresIn: '1h' } ); this.refreshToken = await JWTUtils.createRefreshToken( this.did, this.privateKey, generateTokenId(), { expiresIn: '30d' } ); } // Update client authentication this.client.setAuthentication({ token: this.currentToken }); } async exchangeRefreshToken() { const response = await fetch('/auth/refresh', { method: 'POST', headers: { 'Authorization': `Bearer ${this.refreshToken}`, 'Content-Type': 'application/json' } }); const data = await response.json(); return data.accessToken; } } // Usage const tokenManager = new TokenManager(client, did, privateKey); await tokenManager.ensureValidToken(); ``` ### Token Caching Cache tokens to improve performance: ```javascript class CachedAuthClient { constructor(config) { this.client = new ATPClient(config); this.tokenCache = new Map(); this.did = config.auth.did; this.privateKey = config.auth.privateKey; } async authenticatedRequest(operation, ...args) { const cacheKey = `token:${this.did}`; let token = this.tokenCache.get(cacheKey); // Check if token is expired or near expiry if (!token || this.isTokenNearExpiry(token)) { token = await this.generateFreshToken(); this.tokenCache.set(cacheKey, token); this.client.setAuthentication({ token }); } return await operation.apply(this.client, args); } isTokenNearExpiry(token) { const timeToExpiry = JWTUtils.getTimeToExpiration(token); return timeToExpiry < 300; // Refresh if less than 5 minutes remaining } async generateFreshToken() { return await JWTUtils.createAuthToken( this.did, this.privateKey, { expiresIn: '1h' } ); } } ``` ### Token Revocation Revoke compromised tokens: ```javascript // Revoke specific token await client.identity.revokeToken({ tokenId: 'token-abc123', reason: 'Security incident' }); // Revoke all tokens for a DID await client.identity.revokeAllTokens({ did: 'did:atp:testnet:user', reason: 'Account compromise' }); // Check token revocation status const tokenStatus = await client.identity.checkTokenStatus('token-abc123'); if (tokenStatus.data.revoked) { console.log('Token has been revoked'); } ``` ## Security Best Practices ### Key Management 1. **Secure Storage**: Store private keys in secure vaults (HSMs, AWS KMS, etc.) 2. **Key Rotation**: Rotate keys regularly 3. **Separation**: Use different keys for different environments 4. **Backup**: Securely backup recovery keys ```javascript // Example: AWS KMS integration import { KMSClient, DecryptCommand } from '@aws-sdk/client-kms'; async function loadPrivateKeyFromKMS(keyId) { const kmsClient = new KMSClient({ region: 'us-east-1' }); const command = new DecryptCommand({ CiphertextBlob: Buffer.from(process.env.ENCRYPTED_PRIVATE_KEY, 'base64') }); const response = await kmsClient.send(command); return Buffer.from(response.Plaintext).toString('hex'); } // Use with client const privateKey = await loadPrivateKeyFromKMS(process.env.KMS_KEY_ID); client.setAuthentication({ did: process.env.ATP_DID, privateKey: privateKey }); ``` ### Token Security 1. **Short Expiry**: Use short token lifetimes 2. **Refresh Tokens**: Implement token refresh 3. **Secure Transmission**: Always use HTTPS 4. **Token Validation**: Validate tokens on every request ```javascript // Secure token configuration const tokenConfig = { accessTokenExpiry: '15m', // Short-lived access tokens refreshTokenExpiry: '7d', // Longer-lived refresh tokens audience: 'specific-service', // Limit token scope issuer: 'trusted-issuer', // Security headers headers: { 'Strict-Transport-Security': 'max-age=31536000', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY' } }; ``` ### Environment Separation Separate credentials by environment: ```javascript // Environment-specific authentication const authConfig = { development: { did: 'did:atp:testnet:dev-service', privateKey: process.env.DEV_PRIVATE_KEY, trustedIssuers: ['did:atp:testnet:dev-issuer'] }, staging: { did: 'did:atp:testnet:staging-service', privateKey: process.env.STAGING_PRIVATE_KEY, trustedIssuers: ['did:atp:testnet:staging-issuer'] }, production: { did: 'did:atp:mainnet:prod-service', privateKey: await loadFromSecureVault(), trustedIssuers: ['did:atp:mainnet:prod-issuer'], mfaRequired: true } }; const config = authConfig[process.env.NODE_ENV || 'development']; ``` ### Monitoring and Auditing Monitor authentication events: ```javascript // Authentication event logging client.on('auth:success', (event) => { console.log(`Authentication successful: ${event.did}`); // Log to audit service client.audit.logEvent({ source: 'sdk-client', action: 'authentication_success', resource: 'auth:login', actor: event.did, details: { method: event.method, timestamp: new Date().toISOString(), userAgent: process.env.USER_AGENT } }); }); client.on('auth:failure', (event) => { console.error(`Authentication failed: ${event.error}`); // Log security event client.audit.logEvent({ source: 'sdk-client', action: 'authentication_failure', resource: 'auth:login', details: { error: event.error, did: event.did, timestamp: new Date().toISOString(), severity: 'high' } }); }); ``` ### Error Handling Handle authentication errors gracefully: ```javascript async function safeAuthentication(client, credentials) { try { client.setAuthentication(credentials); // Test authentication await client.identity.resolve(credentials.did); return { success: true }; } catch (error) { if (error instanceof ATPAuthenticationError) { switch (error.code) { case 'INVALID_DID': return { success: false, error: 'Invalid DID format' }; case 'INVALID_SIGNATURE': return { success: false, error: 'Invalid private key' }; case 'MFA_REQUIRED': return { success: false, error: 'MFA verification required', requiresMFA: true }; case 'TOKEN_EXPIRED': return { success: false, error: 'Token has expired', requiresRefresh: true }; default: return { success: false, error: 'Authentication failed' }; } } throw error; // Re-throw non-auth errors } } ``` This authentication guide covers all aspects of securing your ATP™ SDK integration. Always follow security best practices and regularly review your authentication implementation.