Skip to content

Basic Auth based Connection

This guide explains how to connect to a WebSocket server (wss://itsochvts.com/api/socket) that requires Basic Authentication and a session token. It includes handling reconnection, parsing messages, and cleanly closing the connection.

Steps to Follow

  1. Generate Session Token using basic auth
  2. Use Session token to initialize socket
  3. Handle socket connection

1. Generate a Session Token

  • Uses Basic Auth to request a session token.
  • Sends a POST request with an expiration query param.
  • Returns the raw token string.
const generateToken = async () => {
  const response = await fetch(`https://itsochvts.com/api/session/token`, {
    method: "POST",
    headers: {
      Authorization: `Basic ${Buffer.from(
        `${authorization.user}:${authorization.password}`
      ).toString("base64")}`,
    },
    body: new URLSearchParams({ expiration: "2026-10-10T09:04:51Z" }),
  });
  const responseText = await response.text();
  return responseText;
};

2. Initialize Connection

  • Generates token and session token.
  • Starts WebSocket connection using the session token.
async function initSocket() {
  console.log("[WS] Connecting to WebSocket...");

  const token = await generateToken(); // Step 1

  if (!token) {
    process.exit(1);
  }

  console.log("[WS] Start Connection...");
  connectWebSocket(token); // Step 3
}

3. Handling Socket connection

  • Connects to the WebSocket using the session token.
  • Handles open, message, close, and error events.
  • Parses device, position, and event data from incoming messages.
function connectWebSocket(token: string): void {
  socket = new WebSocket(`wss://itsochvts.com/api/socket?token=${token}`);

  socket.on("open", () => {
    console.log("[WS] Connected");
    reconnectAttempts = 0;
    socket?.send(JSON.stringify({ logs: includeLogs }));
  });

  socket.on("message", (data: WebSocket.Data) => {
    try {
      const parsed: ParsedMessage = JSON.parse(data.toString());

      if (parsed.type === "pong") return;

      if (parsed.devices) console.log("[WS] Devices:", parsed.devices);
      if (parsed.positions) console.log("[WS] Positions:", parsed.positions);
      if (parsed.events && !features.disableEvents) {
        console.log("[WS] Events:", parsed.events);
      }
    } catch (err) {
      console.error("[WS] Parse Error:", err);
    }
  });

  socket.on("close", (code: number) => {
    console.warn("[WS] Closed:", code);
    socket = null;
    if (code !== 4000) {
      scheduleReconnect();
    }
  });

  socket.on("error", (err: Error) => {
    console.error("[WS] Error:", err);
    if (socket?.readyState !== WebSocket.CLOSED) {
      socket?.close();
    }
  });
}

Reconnecting

  • Implements exponential backoff for reconnection attempts.
  • Prevents infinite retries beyond the maximum allowed attempts.
function scheduleReconnect(): void {
  if (reconnectAttempts >= maxReconnectAttempts) return;
  const delay: number = Math.min(30000, 1000 * 2 ** reconnectAttempts);
  console.log(`[WS] Reconnecting in ${delay / 1000}s...`);
  setTimeout(() => {
    reconnectAttempts++;
    if (isSocketDisconnected()) {
      initSocket();
    }
  }, delay);
}

function isSocketDisconnected(): boolean {
  return !socket || socket.readyState === WebSocket.CLOSED;
}

Stop Connection

  • Closes the connection after 15 seconds using custom code 4000.
  • Simulates a logout for demo/testing purposes.
function logout(): void {
  if (
    socket?.readyState === WebSocket.OPEN ||
    socket?.readyState === WebSocket.CONNECTING
  ) {
    socket.close(4000);
  }
}

//stop connection after 15 sec
setTimeout(() => {
  logout();
  console.log("[WS] Logged out");
}, 15000);

Full code

Using Nodejs and Typescript

import dotenv from "dotenv";
import WebSocket from "ws";

dotenv.config();

interface Features {
  disableEvents: boolean;
}

interface ParsedMessage {
  type?: string;
  devices?: any[];
  positions?: any[];
  events?: any[];
}

const authorization = {
  user: process.env.USER || "demo@trackon-gps.com",
  password: process.env.PASSWORD || "demo123",
};

let socket: WebSocket | null = null;
let includeLogs: boolean = true;
let features: Features = { disableEvents: false };
let reconnectAttempts: number = 0;
const maxReconnectAttempts: number = 10;

//Step 1: Generate a session token using the API
const generateToken = async () => {
  const response = await fetch(`https://itsochvts.com/api/session/token`, {
    method: "POST",
    headers: {
      Authorization: `Basic ${Buffer.from(
        `${authorization.user}:${authorization.password}`
      ).toString("base64")}`,
    },
    body: new URLSearchParams({ expiration: "2026-10-10T09:04:51Z" }),
  });
  const responseText = await response.text();
  return responseText;
};

//Step 2: Initialize the WebSocket connection using the token
async function initSocket() {
  console.log("[WS] Connecting to WebSocket...");

  const token = await generateToken(); //get token
  if (!token) {
    process.exit(1);
  }
  console.log("[WS] Session token Generated");

  connectWebSocket(token as string);
}

//Step 3: Connect to WebSocket using the session token
function connectWebSocket(token: string): void {
  socket = new WebSocket(`wss://itsochvts.com/api/socket?token=${token}`);

  socket.on("open", () => {
    console.log("[WS] Connected");
    reconnectAttempts = 0;

    if (includeLogs) {
      socket?.send(JSON.stringify({ logs: includeLogs }));
    }
  });

  socket.on("message", (data: WebSocket.Data) => {
    try {
      const parsed: ParsedMessage = JSON.parse(data.toString());

      if (parsed.type === "pong") return;

      if (parsed.devices) {
        //handle devices
        console.log("[WS] Devices:", parsed.devices);
      }

      if (parsed.positions) {
        //handle positions
        console.log("[WS] Positions:", parsed.positions);
      }

      if (parsed.events && !features.disableEvents) {
        //handle events
        console.log("[WS] Events:", parsed.events);
      }
    } catch (err) {
      console.error("[WS] Parse Error:", err);
    }
  });

  socket.on("close", (code: number) => {
    console.warn("[WS] Closed:", code);
    socket = null;

    if (code !== 4000) {
      scheduleReconnect();
    }
  });

  socket.on("error", (err: Error) => {
    console.error("[WS] Error:", err);
    if (socket?.readyState !== WebSocket.CLOSED) {
      socket?.close();
    }
  });
}

function isSocketDisconnected(): boolean {
  return !socket || socket.readyState === WebSocket.CLOSED;
}

// Reconnecting to socket on failure with exponential backoff
function scheduleReconnect(): void {
  if (reconnectAttempts >= maxReconnectAttempts) return;
  const delay: number = Math.min(30000, 1000 * 2 ** reconnectAttempts);
  console.log(`[WS] Reconnecting in ${delay / 1000}s...`);
  setTimeout(() => {
    reconnectAttempts++;
    if (isSocketDisconnected()) {
      initSocket();
    }
  }, delay);
}

// Simulate logout after 15 seconds
function logout(): void {
  if (
    socket?.readyState === WebSocket.OPEN ||
    socket?.readyState === WebSocket.CONNECTING
  ) {
    socket.close(4000);
  }
}

setTimeout(() => {
  logout();
  console.log("[WS] Logged out");
}, 15000);

//run the socket connection
initSocket();