@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
135 lines (134 loc) • 4.14 kB
JavaScript
/**
* Token Storage for OAuth 2.1 authentication
* Provides implementations for storing OAuth tokens
*/
import { logger } from "../../utils/logger.js";
/**
* In-memory token storage implementation
* Suitable for development and single-session use
* Tokens are lost when the process terminates
*/
export class InMemoryTokenStorage {
tokens = new Map();
async getTokens(serverId) {
return this.tokens.get(serverId) ?? null;
}
async saveTokens(serverId, tokens) {
this.tokens.set(serverId, tokens);
}
async deleteTokens(serverId) {
this.tokens.delete(serverId);
}
async hasTokens(serverId) {
return this.tokens.has(serverId);
}
async clearAll() {
this.tokens.clear();
}
/**
* Get the number of stored token sets
*/
get size() {
return this.tokens.size;
}
/**
* Get all server IDs with stored tokens
*/
getServerIds() {
return Array.from(this.tokens.keys());
}
}
/**
* File-based token storage implementation
* Persists tokens to disk for cross-session use
*/
export class FileTokenStorage {
filePath;
tokens = new Map();
loaded = false;
constructor(filePath) {
this.filePath = filePath;
}
async loadTokens() {
if (this.loaded) {
return;
}
try {
const fs = await import("fs/promises");
const data = await fs.readFile(this.filePath, "utf-8");
const parsed = JSON.parse(data);
this.tokens = new Map(Object.entries(parsed));
this.loaded = true;
}
catch (error) {
// File doesn't exist or is invalid, start with empty tokens
if (error instanceof Error &&
"code" in error &&
error.code !== "ENOENT") {
logger.warn(`[FileTokenStorage] Error loading tokens: ${error.message}`);
}
this.tokens = new Map();
this.loaded = true;
}
}
async saveToFile() {
try {
const fs = await import("fs/promises");
const path = await import("path");
// Ensure directory exists
const dir = path.dirname(this.filePath);
await fs.mkdir(dir, { recursive: true });
// Write tokens to file
const data = Object.fromEntries(this.tokens.entries());
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2), "utf-8");
}
catch (error) {
logger.error(`[FileTokenStorage] Error saving tokens: ${error instanceof Error ? error.message : String(error)}`);
throw error;
}
}
async getTokens(serverId) {
await this.loadTokens();
return this.tokens.get(serverId) ?? null;
}
async saveTokens(serverId, tokens) {
await this.loadTokens();
this.tokens.set(serverId, tokens);
await this.saveToFile();
}
async deleteTokens(serverId) {
await this.loadTokens();
this.tokens.delete(serverId);
await this.saveToFile();
}
async hasTokens(serverId) {
await this.loadTokens();
return this.tokens.has(serverId);
}
async clearAll() {
this.tokens.clear();
await this.saveToFile();
}
}
/**
* Check if tokens are expired or about to expire
* @param tokens - OAuth tokens to check
* @param bufferSeconds - Buffer time in seconds before expiration (default: 60)
* @returns True if tokens are expired or will expire within buffer time
*/
export function isTokenExpired(tokens, bufferSeconds = 60) {
if (!tokens.expiresAt) {
return false; // No expiration set, assume valid
}
const bufferMs = bufferSeconds * 1000;
const now = Date.now();
return tokens.expiresAt - bufferMs <= now;
}
/**
* Calculate token expiration timestamp from expires_in value
* @param expiresIn - Token lifetime in seconds
* @returns Expiration timestamp (Unix epoch in milliseconds)
*/
export function calculateExpiresAt(expiresIn) {
return Date.now() + expiresIn * 1000;
}