Skip to main content
Glama
config.ts3.93 kB
/** * FreshBooks OAuth Configuration Helper * * Loads OAuth configuration from environment variables with validation. */ import { OAuthConfig, TokenStore } from './types.js'; import { EncryptedFileTokenStore, EnvTokenStore } from './token-store.js'; /** * Load OAuth configuration from environment variables * * Required environment variables: * - FRESHBOOKS_CLIENT_ID * - FRESHBOOKS_CLIENT_SECRET * - FRESHBOOKS_REDIRECT_URI * * Optional: * - FRESHBOOKS_SCOPES (comma-separated) * * @throws Error if required environment variables are missing */ export function loadOAuthConfig(): OAuthConfig { const clientId = process.env.FRESHBOOKS_CLIENT_ID; const clientSecret = process.env.FRESHBOOKS_CLIENT_SECRET; const redirectUri = process.env.FRESHBOOKS_REDIRECT_URI; if (!clientId) { throw new Error( 'FRESHBOOKS_CLIENT_ID environment variable is required. ' + 'Get your client ID from FreshBooks Developer Portal.' ); } if (!clientSecret) { throw new Error( 'FRESHBOOKS_CLIENT_SECRET environment variable is required. ' + 'Get your client secret from FreshBooks Developer Portal.' ); } if (!redirectUri) { throw new Error( 'FRESHBOOKS_REDIRECT_URI environment variable is required. ' + 'Set to your OAuth redirect URI (e.g., http://localhost:3000/callback)' ); } const scopesEnv = process.env.FRESHBOOKS_SCOPES; const scopes = scopesEnv ? scopesEnv.split(',').map(s => s.trim()) : undefined; const config: OAuthConfig = { clientId, clientSecret, redirectUri, }; // Only add scopes if defined if (scopes) { config.scopes = scopes; } return config; } /** * Create appropriate token store based on environment * * Strategy: * 1. If FRESHBOOKS_ACCESS_TOKEN is set, use EnvTokenStore (CI/testing) * 2. Otherwise, use EncryptedFileTokenStore (production/development) * * Token file location: * - Uses FRESHBOOKS_TOKEN_FILE env var if set * - Otherwise defaults to './freshbooks-tokens.enc' * * Encryption password: * - Uses FRESHBOOKS_TOKEN_PASSWORD env var if set * - Otherwise uses machine-specific default */ export function createTokenStore(): TokenStore { // If access token is in environment, use EnvTokenStore if (process.env.FRESHBOOKS_ACCESS_TOKEN) { return new EnvTokenStore(); } // Use encrypted file store const tokenFile = process.env.FRESHBOOKS_TOKEN_FILE || './freshbooks-tokens.enc'; return new EncryptedFileTokenStore(tokenFile); } /** * Validate OAuth configuration * * Checks that configuration values are properly formatted. * * @param config OAuth configuration to validate * @throws Error if configuration is invalid */ export function validateOAuthConfig(config: OAuthConfig): void { // Validate client ID format (basic check) if (config.clientId.length < 10) { throw new Error('Client ID appears to be invalid (too short)'); } // Validate client secret format (basic check) if (config.clientSecret.length < 10) { throw new Error('Client secret appears to be invalid (too short)'); } // Validate redirect URI format try { const url = new URL(config.redirectUri); // Warn about insecure redirect URIs in production if (process.env.NODE_ENV === 'production' && url.protocol === 'http:') { console.warn( 'Warning: Using insecure HTTP redirect URI in production. ' + 'Consider using HTTPS for enhanced security.' ); } } catch { throw new Error('Redirect URI must be a valid URL'); } } /** * Initialize OAuth configuration with validation * * Convenience function that: * 1. Loads configuration from environment * 2. Validates configuration * 3. Returns validated config * * @returns Validated OAuth configuration */ export function initializeOAuthConfig(): OAuthConfig { const config = loadOAuthConfig(); validateOAuthConfig(config); return config; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Good-Samaritan-Software-LLC/freshbooks-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server