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
JavaScript
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