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-clientStep 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