Integrate in Existing Node.js Project

Add Hawcx authentication to an existing Node.js application

This guide walks through adding Hawcx OAuth authentication to an existing Node.js backend.

Step 1: Install the SDK

npm install @hawcx/oauth-client

Step 2: Set Up Environment Variables

Create a .env file or configure your environment with:

# Base URL — from Admin Console → Project Settings (environment-specific)
HAWCX_BASE_URL="<Base URL from Admin Console>"
# Config ID — from Admin Console → Project Settings (tenant routing key)
HAWCX_CONFIG_ID="<Config ID from Admin Console>"
# Client ID — from Admin Console → Project Settings (expected `aud` on id_tokens)
HAWCX_CLIENT_ID="<Client ID from Admin Console>"

# Optional (for delegation / MFA management)
HAWCX_SECRET_KEY=hwx_sk_v1_...

Where to find these values

Open the Hawcx Admin Console, go to Project Settings, and copy the Base URL, Config ID, and Client ID. All three are environment-specific.

configId vs clientId

These look similar but are distinct values. configId is the tenant routing key sent as the X-Config-Id header on token exchange. clientId is the value the tenant has configured as the aud claim on issued id_tokens. The SDK passes each to the right place; you just need both set correctly. See the Quickstart for details.

Load environment variables in your application:

import dotenv from 'dotenv';
dotenv.config();

Step 3: Construct the SDK Once at Startup

Use the discovery-mode factory so verification enforces iss / aud / exp / nbf (not just signature). The discovery fetch happens once at boot — don't run it per request.

// src/hawcx.ts
import { HawcxOAuth } from '@hawcx/oauth-client';

export const oauth = await HawcxOAuth.fromIssuer({
  issuer: process.env.HAWCX_BASE_URL!,
  configId: process.env.HAWCX_CONFIG_ID!,
  clientId: process.env.HAWCX_CLIENT_ID!,
});

The await rejects with DiscoveryError if the issuer is unreachable or the discovery document is malformed — your bootstrap fails fast rather than starting with broken verification.

Step 4: Create an Exchange Endpoint

import express from 'express';
import { oauth } from './hawcx';   // singleton from Step 3

const router = express.Router();

router.post('/exchange', async (req, res) => {
  try {
    const { authCode, codeVerifier } = req.body;

    if (!authCode || !codeVerifier) {
      return res.status(400).json({ error: 'Missing authCode or codeVerifier' });
    }

    // Verification enforces signature + iss + aud + exp + nbf
    const { claims } = await oauth.exchangeCode(authCode, codeVerifier);

    // Find or create user in your database
    const user = await findOrCreateUser({
      id: claims.sub,
      email: claims.email,
    });

    // Create your application's session/JWT
    const sessionToken = generateSessionToken(user);

    res.json({
      success: true,
      sessionToken,
      user: { id: user.id, email: user.email },
    });
  } catch (error) {
    console.error('Hawcx exchange error:', error);
    res.status(401).json({ error: 'Authentication failed' });
  }
});

export default router;

Step 5: Integrate with Your User Management

Update your user service to handle Hawcx identities:

// services/userService.ts
import db from '../db';

interface HawcxUser {
  id: string;  // Hawcx user ID (sub claim)
  email?: string;
}

export async function findOrCreateUser(hawcxUser: HawcxUser) {
  let user = await db.users.findOne({ hawcx_id: hawcxUser.id });

  if (!user) {
    user = await db.users.create({
      hawcx_id: hawcxUser.id,
      email: hawcxUser.email
    });
  }

  return user;
}

Optional: Backend-Driven MFA Management

If you need to manage MFA from your backend, use the delegation client with your secret key blob:

import { DelegationClient, MfaMethod } from '@hawcx/oauth-client';

const client = DelegationClient.fromSecretKey({
  secretKey: process.env.HAWCX_SECRET_KEY!,
  baseUrl: process.env.HAWCX_BASE_URL!,
  apiKey: process.env.HAWCX_CONFIG_ID
});

const result = await client.mfa.initiate({
  userId: 'user@example.com',
  mfaMethod: MfaMethod.SMS,
  phoneNumber: '+15551234567'
});

await client.mfa.verify({
  userId: 'user@example.com',
  sessionId: result.session_id,
  otp: '123456'
});

Last updated on