auth/auth.ts

import { credentials } from "@grpc/grpc-js";
import { sendlix } from "../proto/auth";
import { IAuth } from "./IAuth";

const { AuthRequest, ApiKey, AuthClient: gRPCAuthClient } = sendlix.api.v1;

/**
 * Auth class implements IAuth for handling authentication.
 * It retrieves and manages JWT tokens using the provided API key.
 * */
export class Auth implements IAuth {
  private apiKey: InstanceType<typeof ApiKey>;
  private client: InstanceType<typeof gRPCAuthClient>;

  private token: {
    token: string;
    expiresAt: number;
  } | null = null;

  /**
   * Constructs a new Auth instance.
   * @param apiKey - The API key in the format 'key.value'.
   */
  constructor(apiKey: string) {
    const g = apiKey.split(".");
    if (g.length !== 2) {
      throw new Error("Invalid API key format. Expected format: 'key.value'.");
    }
    this.apiKey = new ApiKey({
      secret: g[0],
      keyID: parseInt(g[1]),
    });
    this.client = new gRPCAuthClient(
      "api.sendlix.com",
      credentials.createSsl(),
      { "grpc.primary_user_agent": "sendlix-nodejs-sdk/1.0.0" }
    );
  }

  /**
   * Returns the authorization header for the client
   * @returns {Promise<Array<string>>} Promise resolving to [headerName, headerValue]
   */
  getAuthHeader(): Promise<[string, string]> {
    if (this.token && this.token.expiresAt > Date.now()) {
      return Promise.resolve(["Authorization", `Bearer ${this.token.token}`]);
    }

    return new Promise((resolve, reject) => {
      const request = new AuthRequest();
      request.apiKey = this.apiKey;
      this.client.GetJwtToken(request, (err, response) => {
        if (err) {
          return reject(err);
        }
        if (!response) {
          return reject(new Error("No response from server"));
        }

        this.token = {
          token: response.token,
          expiresAt: Date.now() + response.expires.seconds * 1000,
        };

        resolve(["Authorization", `Bearer ${this.token.token}`]);
      });
    });
  }
}