@fairmint/canton-node-sdk
Version:
Canton Node SDK
133 lines • 5.85 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthenticationManager = void 0;
const axios_1 = __importDefault(require("axios"));
const url_1 = require("url");
const errors_1 = require("../errors");
/** Manages OAuth2 authentication and token lifecycle */
class AuthenticationManager {
constructor(authUrl, authConfig, logger) {
this.authUrl = authUrl;
this.authConfig = authConfig;
this.logger = logger;
this.bearerToken = null;
this.tokenExpiry = null;
}
async authenticate() {
// Check if we have a valid token
if (this.isTokenValid()) {
return this.bearerToken;
}
// Check if authentication credentials are provided
if (!this.authConfig.clientId || this.authConfig.clientId.trim() === '') {
// No authentication credentials provided, skip authentication
this.bearerToken = null;
return '';
}
// Validate required auth configuration
this.validateAuthConfig();
const formData = new url_1.URLSearchParams();
formData.append('grant_type', this.authConfig.grantType);
formData.append('client_id', this.authConfig.clientId);
if (this.authConfig.clientSecret) {
formData.append('client_secret', this.authConfig.clientSecret);
}
if (this.authConfig.audience) {
formData.append('audience', this.authConfig.audience);
}
if (this.authConfig.scope) {
formData.append('scope', this.authConfig.scope);
}
if (this.authConfig.username) {
formData.append('username', this.authConfig.username);
}
if (this.authConfig.password) {
formData.append('password', this.authConfig.password);
}
const url = this.authUrl + '/';
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// Build a log-friendly representation of the request body
const requestBody = {};
for (const [key, value] of formData.entries()) {
requestBody[key] = value;
}
try {
const response = await axios_1.default.post(url, formData.toString(), {
headers,
});
if (!response.data.access_token) {
throw new errors_1.AuthenticationError(`Authentication response missing access_token. Response: ${JSON.stringify(response.data, null, 2)}`);
}
this.bearerToken = response.data.access_token;
// Set token expiry if provided
if (response.data.expires_in) {
this.tokenExpiry = Date.now() + (response.data.expires_in * 1000);
}
// Log success
if (this.logger) {
await this.logger.logRequestResponse(url, { method: 'POST', headers, data: requestBody }, response.data);
}
return this.bearerToken;
}
catch (error) {
// Log failure with context
if (this.logger) {
const errorPayload = axios_1.default.isAxiosError(error) ? (error.response?.data || error.message) : (error instanceof Error ? error.message : String(error));
await this.logger.logRequestResponse(url, { method: 'POST', headers, data: requestBody }, errorPayload);
}
if (axios_1.default.isAxiosError(error)) {
const status = error.response?.status;
const statusText = error.response?.statusText;
const errorData = error.response?.data
? JSON.stringify(error.response.data, null, 2)
: error.message;
throw new errors_1.ApiError(`Authentication failed for URL ${url} with status ${status} ${statusText}: ${errorData}`, status, statusText);
}
throw new errors_1.AuthenticationError(`Authentication failed for URL ${url}: ${error instanceof Error ? error.message : String(error)}`);
}
}
async getBearerToken() {
return this.authenticate();
}
clearToken() {
this.bearerToken = null;
this.tokenExpiry = null;
}
validateAuthConfig() {
const missingConfig = [];
if (!this.authConfig.clientId)
missingConfig.push('clientId');
if (!this.authConfig.grantType)
missingConfig.push('grantType');
// Check for grant type specific requirements
if (this.authConfig.grantType === 'password') {
if (!this.authConfig.username)
missingConfig.push('username');
if (!this.authConfig.password)
missingConfig.push('password');
}
// Note: client_credentials grant type may not always require client_secret
// Some providers allow client_credentials without secret for public clients
if (missingConfig.length > 0) {
throw new errors_1.AuthenticationError(`Authentication configuration incomplete. Missing required fields: ${missingConfig.join(', ')}. ` +
`Grant Type: ${this.authConfig.grantType}`);
}
}
isTokenValid() {
if (!this.bearerToken) {
return false;
}
// If no expiry is set, assume token is valid
if (!this.tokenExpiry) {
return true;
}
// Check if token has expired (with 5 minute buffer)
const bufferTime = 5 * 60 * 1000; // 5 minutes
return Date.now() < (this.tokenExpiry - bufferTime);
}
}
exports.AuthenticationManager = AuthenticationManager;
//# sourceMappingURL=AuthenticationManager.js.map