Skip to main content
Glama
authentication.ts17.5 kB
import axios from 'axios'; import { dhanConfig } from './config.js'; import { AuthStep1Response, AuthStep3Response, DhanAuthToken, AuthState, FundLimit, PlaceOrderRequest, OrderResponse, OrderBook, TradeBook, ModifyOrderRequest, PlaceSuperOrderRequest, SuperOrderBook, SuperOrderLeg, } from './types.js'; const AUTH_BASE_URL = 'https://auth.dhan.co'; // Helper to get standard API headers with access token const getApiHeaders = () => { const accessToken = getAccessToken(); if (!accessToken) { throw new Error('No valid access token. Please authenticate first.'); } return { 'Content-Type': 'application/json', 'access-token': accessToken, 'User-Agent': 'DhanMCPClient/1.0', }; }; // Helper to log to stderr (doesn't interfere with stdio protocol) const log = (message: string) => { console.error(`[DhanAuth] ${message}`); }; // In-memory storage for authentication state (in production, use a database) let authState: AuthState = {}; /** * Step 1: Generate Consent * Validates the App ID and secret and initiates a login session */ export async function generateConsent(): Promise<{ consentAppId: string; loginUrl: string; message: string; }> { try { log('Generating consent...'); const response = await axios.post<AuthStep1Response>( `${AUTH_BASE_URL}/app/generate-consent?client_id=${dhanConfig.clientId}`, {}, { headers: { app_id: dhanConfig.apiKey, app_secret: dhanConfig.apiSecret, }, } ); if (response.data.status !== 'success') { throw new Error(`Failed to generate consent: ${response.data.status}`); } authState.consentAppId = response.data.consentAppId; authState.consentAppStatus = response.data.consentAppStatus; const loginUrl = `${AUTH_BASE_URL}/login/consentApp-login?consentAppId=${response.data.consentAppId}`; log('✓ Consent generated successfully'); log(`Consent App ID: ${response.data.consentAppId}`); return { consentAppId: response.data.consentAppId, loginUrl, message: `Please open this URL in your browser to authenticate:\n${loginUrl}\n\nAfter login, you will be redirected and the tokenId will be passed as a query parameter.`, }; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${error.response?.data}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed: ${errorMessage}`); throw new Error(`Step 1 Failed: ${errorMessage}`); } } /** * Step 2: Browser-Based Login * This is handled by the user opening the login URL in their browser * The redirect will provide the tokenId which must be passed to Step 3 */ export function getStep2Instructions( consentAppId: string ): { loginUrl: string; instruction: string } { const loginUrl = `${AUTH_BASE_URL}/login/consentApp-login?consentAppId=${consentAppId}`; return { loginUrl, instruction: `1. Open this URL in your browser: ${loginUrl} 2. Log in with your Dhan credentials 3. Complete 2FA verification (OTP/PIN/Password) 4. You will be redirected to: ${dhanConfig.redirectUrl}?tokenId=<TOKEN_ID> 5. Copy the tokenId from the URL and use it in the 'consumeConsent' tool`, }; } /** * Step 3: Consume Consent * Uses the tokenId from Step 2 to generate the final access token */ export async function consumeConsent( tokenId: string ): Promise<DhanAuthToken> { try { log('Consuming consent with tokenId...'); const response = await axios.get<AuthStep3Response>( `${AUTH_BASE_URL}/app/consumeApp-consent?tokenId=${tokenId}`, { headers: { app_id: dhanConfig.apiKey, app_secret: dhanConfig.apiSecret, }, } ); const authToken: DhanAuthToken = { accessToken: response.data.accessToken, dhanClientId: response.data.dhanClientId, dhanClientName: response.data.dhanClientName, dhanClientUcc: response.data.dhanClientUcc, expiryTime: response.data.expiryTime, givenPowerOfAttorney: response.data.givenPowerOfAttorney, generatedAt: new Date().toISOString(), }; authState.authToken = authToken; authState.tokenId = tokenId; log('✓ Access token generated successfully'); log(`Client Name: ${authToken.dhanClientName}`); log(`Client ID: ${authToken.dhanClientId}`); log(`Token Expiry: ${authToken.expiryTime}`); return authToken; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${error.response?.data}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed: ${errorMessage}`); throw new Error(`Step 3 Failed: ${errorMessage}`); } } /** * Get current authentication state */ export function getAuthState(): AuthState { return authState; } /** * Get the current valid access token if available */ export function getAccessToken(): string | null { if (!authState.authToken) { return null; } const expiryTime = new Date(authState.authToken.expiryTime); if (new Date() > expiryTime) { log('Access token has expired'); return null; } return authState.authToken.accessToken; } /** * Reset authentication state */ export function resetAuthState(): void { authState = {}; log('State reset'); } /** * Check if token is valid and not expired */ export function isTokenValid(): boolean { if (!authState.authToken) { return false; } const expiryTime = new Date(authState.authToken.expiryTime); return new Date() < expiryTime; } /** * Get account fund limit (balance, margins, etc.) */ export async function getFundLimit(): Promise<FundLimit> { try { const accessToken = getAccessToken(); if (!accessToken) { throw new Error('No valid access token. Please authenticate first.'); } log('Fetching fund limit...'); const response = await axios.get<FundLimit>( 'https://api.dhan.co/v2/fundlimit', { headers: { 'Content-Type': 'application/json', 'access-token': accessToken, }, } ); log(`✓ Fund limit retrieved for client ${response.data.dhanClientId}`); log(`Available Balance: ${response.data.availabelBalance}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to get fund limit: ${errorMessage}`); throw new Error(`Failed to get fund limit: ${errorMessage}`); } } // ============ ORDER MANAGEMENT APIS ============ /** * Place a new order */ export async function placeOrder( request: PlaceOrderRequest ): Promise<OrderResponse> { try { log(`Placing order: ${request.transactionType} ${request.quantity} shares`); const response = await axios.post<OrderResponse>( 'https://api.dhan.co/v2/orders', request, { headers: getApiHeaders(), } ); log(`✓ Order placed successfully. Order ID: ${response.data.orderId}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to place order: ${errorMessage}`); throw new Error(`Failed to place order: ${errorMessage}`); } } /** * Modify a pending order */ export async function modifyOrder( orderId: string, request: ModifyOrderRequest ): Promise<OrderResponse> { try { log(`Modifying order: ${orderId}`); const response = await axios.put<OrderResponse>( `https://api.dhan.co/v2/orders/${orderId}`, request, { headers: getApiHeaders(), } ); log(`✓ Order modified successfully. Order ID: ${response.data.orderId}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to modify order: ${errorMessage}`); throw new Error(`Failed to modify order: ${errorMessage}`); } } /** * Cancel a pending order */ export async function cancelOrder(orderId: string): Promise<OrderResponse> { try { log(`Cancelling order: ${orderId}`); const response = await axios.delete<OrderResponse>( `https://api.dhan.co/v2/orders/${orderId}`, { headers: getApiHeaders(), } ); log(`✓ Order cancelled successfully. Order ID: ${response.data.orderId}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to cancel order: ${errorMessage}`); throw new Error(`Failed to cancel order: ${errorMessage}`); } } /** * Get all orders (Order Book) */ export async function getOrderBook(): Promise<OrderBook[]> { try { log('Fetching order book...'); const response = await axios.get<OrderBook[]>( 'https://api.dhan.co/v2/orders', { headers: getApiHeaders(), } ); log(`✓ Order book retrieved. Total orders: ${response.data.length}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to get order book: ${errorMessage}`); throw new Error(`Failed to get order book: ${errorMessage}`); } } /** * Get a specific order by Order ID */ export async function getOrderByID(orderId: string): Promise<OrderBook> { try { log(`Fetching order: ${orderId}`); const response = await axios.get<OrderBook>( `https://api.dhan.co/v2/orders/${orderId}`, { headers: getApiHeaders(), } ); log(`✓ Order retrieved. Status: ${response.data.orderStatus}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to get order: ${errorMessage}`); throw new Error(`Failed to get order: ${errorMessage}`); } } /** * Get order by correlation ID */ export async function getOrderByCorrelationID( correlationId: string ): Promise<OrderBook> { try { log(`Fetching order by correlation ID: ${correlationId}`); const response = await axios.get<OrderBook>( `https://api.dhan.co/v2/orders/external/${correlationId}`, { headers: getApiHeaders(), } ); log(`✓ Order retrieved. Order ID: ${response.data.orderId}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to get order by correlation ID: ${errorMessage}`); throw new Error( `Failed to get order by correlation ID: ${errorMessage}` ); } } /** * Get all trades (Trade Book) */ export async function getTradeBook(): Promise<TradeBook[]> { try { log('Fetching trade book...'); const response = await axios.get<TradeBook[]>( 'https://api.dhan.co/v2/trades', { headers: getApiHeaders(), } ); log(`✓ Trade book retrieved. Total trades: ${response.data.length}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to get trade book: ${errorMessage}`); throw new Error(`Failed to get trade book: ${errorMessage}`); } } /** * Get trades for a specific order */ export async function getOrderTrades(orderId: string): Promise<TradeBook[]> { try { log(`Fetching trades for order: ${orderId}`); const response = await axios.get<TradeBook[]>( `https://api.dhan.co/v2/trades/${orderId}`, { headers: getApiHeaders(), } ); log(`✓ Trades retrieved. Total trades: ${response.data.length}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to get order trades: ${errorMessage}`); throw new Error(`Failed to get order trades: ${errorMessage}`); } } // ============ SUPER ORDER MANAGEMENT APIS ============ /** * Place a super order (Entry, Target, Stop Loss) */ export async function placeSuperOrder( request: PlaceSuperOrderRequest ): Promise<OrderResponse> { try { log( `Placing super order: ${request.transactionType} ${request.quantity} shares with target ${request.targetPrice} and SL ${request.stopLossPrice}` ); const response = await axios.post<OrderResponse>( 'https://api.dhan.co/v2/super/orders', request, { headers: getApiHeaders(), } ); log( `✓ Super order placed successfully. Order ID: ${response.data.orderId}` ); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to place super order: ${errorMessage}`); throw new Error(`Failed to place super order: ${errorMessage}`); } } /** * Modify a super order */ export async function modifySuperOrder( orderId: string, legName: string, request: Record<string, unknown> ): Promise<OrderResponse> { try { log(`Modifying super order: ${orderId}, leg: ${legName}`); const payload = { ...request, legName, }; const response = await axios.put<OrderResponse>( `https://api.dhan.co/v2/super/orders/${orderId}`, payload, { headers: getApiHeaders(), } ); log( `✓ Super order modified successfully. Order ID: ${response.data.orderId}` ); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to modify super order: ${errorMessage}`); throw new Error(`Failed to modify super order: ${errorMessage}`); } } /** * Cancel a super order leg */ export async function cancelSuperOrderLeg( orderId: string, orderLeg: string ): Promise<OrderResponse> { try { log( `Cancelling super order leg: ${orderId}, leg: ${orderLeg}` ); const response = await axios.delete<OrderResponse>( `https://api.dhan.co/v2/super/orders/${orderId}/${orderLeg}`, { headers: getApiHeaders(), } ); log( `✓ Super order leg cancelled successfully. Order ID: ${response.data.orderId}` ); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to cancel super order leg: ${errorMessage}`); throw new Error(`Failed to cancel super order leg: ${errorMessage}`); } } /** * Get all super orders */ export async function getSuperOrderBook(): Promise<SuperOrderBook[]> { try { log('Fetching super order book...'); const response = await axios.get<SuperOrderBook[]>( 'https://api.dhan.co/v2/super/orders', { headers: getApiHeaders(), } ); log(`✓ Super order book retrieved. Total super orders: ${response.data.length}`); return response.data; } catch (error) { const errorMessage = error instanceof axios.AxiosError ? `API Error: ${error.response?.status} - ${JSON.stringify(error.response?.data)}` : error instanceof Error ? error.message : 'Unknown error'; log(`✗ Failed to get super order book: ${errorMessage}`); throw new Error(`Failed to get super order book: ${errorMessage}`); } }

Implementation Reference

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/harshitdynamite/DhanMCP'

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