Node.js Backend SDK Quickstart
Integrate Hawcx OAuth authentication in your Node.js backend
Hawcx OAuth Client SDK for Node.js
Add passwordless authentication to your Node.js backend. Exchange OAuth codes for verified user claims and manage MFA.
Installation
npm install @hawcx/oauth-clientQuick Start
OAuth Code Exchange
The most common flow: exchange a Hawcx authorization code for verified user claims.
import { exchangeCodeForClaims } from '@hawcx/oauth-client';
app.post('/api/auth/callback', async (req, res) => {
try {
// Exchange the authorization code for verified claims
const claims = await exchangeCodeForClaims({
code: req.body.code,
oauthTokenUrl: process.env.OAUTH_TOKEN_ENDPOINT!,
clientId: process.env.OAUTH_CLIENT_ID!,
apiKey: process.env.HAWCX_API_KEY!,
jwksUrl: process.env.JWKS_URL!,
// Optional (recommended for production):
codeVerifier: req.session?.pkceVerifier, // PKCE support
audience: 'my-app', // Validate 'aud' claim
issuer: 'https://oauth.example.com', // Validate 'iss' claim
leeway: 10 // Clock skew tolerance
});
// Use the verified claims
const userId = claims.sub;
const email = claims.email;
// Mint your own access token or session
const sessionToken = jwt.sign(
{ sub: claims.sub, email: claims.email },
process.env.APP_SESSION_SECRET!,
{ expiresIn: '1h' }
);
res.json({ sessionToken, userId });
} catch (error) {
console.error('OAuth exchange failed:', error);
res.status(401).json({ error: 'Authentication failed' });
}
});Hawcx Delegation Client (MFA Setup)
For advanced use cases like setting up MFA for users programmatically:
import { HawcxDelegationClient, MfaMethod } from '@hawcx/oauth-client';
// Initialize the delegation client with your signing/encryption keys
const client = HawcxDelegationClient.fromKeys({
spSigningKey: process.env.SP_ED25519_PRIVATE_KEY_PEM!,
spEncryptionKey: process.env.SP_X25519_PRIVATE_KEY_PEM!,
idpVerifyKey: process.env.IDP_ED25519_PUBLIC_KEY_PEM!,
idpEncryptionKey: process.env.IDP_X25519_PUBLIC_KEY_PEM!,
baseUrl: 'https://example.hawcx.com',
spId: process.env.OAUTH_CLIENT_ID!,
apiKey: process.env.HAWCX_API_KEY
})
// Initiate MFA setup (Email, SMS, or TOTP)
const result = await client.initiateMfaChange({
userid: "[email protected]",
mfaMethod: MfaMethod.SMS,
phoneNumber: "+15551234567"
});
// Verify OTP and complete MFA setup
await client.verifyMfaChange({
userid: "[email protected]",
sessionId: result.session_id,
otp: "123456"
});
// Get user credentials
const creds = await client.getUserCredentials("[email protected]");
console.log(`MFA method: ${creds.mfa_method}`);Configuration
Environment Variables
For OAuth Code Exchange:
HAWCX_API_KEY="your-api-key"
OAUTH_TOKEN_ENDPOINT="https://example.hawcx.com/oauth2/token"
JWKS_URL="https://example.hawcx.com/.well-known/jwks.json"
OAUTH_CLIENT_ID="your-client-id"
OAUTH_ISSUER="https://example.hawcx.com" # Optional but recommended
OAUTH_AUDIENCE="your-client-id" # Optional but recommendedFor Hawcx Delegation:
SP_ED25519_PRIVATE_KEY_PEM="-----BEGIN PRIVATE KEY-----..."
SP_X25519_PRIVATE_KEY_PEM="-----BEGIN PRIVATE KEY-----..."
IDP_ED25519_PUBLIC_KEY_PEM="-----BEGIN PUBLIC KEY-----..."
IDP_X25519_PUBLIC_KEY_PEM="-----BEGIN PUBLIC KEY-----..."
OAUTH_CLIENT_ID="your-client-id"Public Key Formats
The SDK accepts public keys in multiple formats or via JWKS endpoint (recommended):
Using JWKS endpoint (recommended for production):
const claims = await exchangeCodeForClaims({
code: authCode,
oauthTokenUrl: process.env.OAUTH_TOKEN_ENDPOINT!,
clientId: process.env.OAUTH_CLIENT_ID!,
apiKey: process.env.HAWCX_API_KEY!,
jwksUrl: 'https://example.hawcx.com/.well-known/jwks.json' // Dynamic key fetching
});From environment variable (static key):
const claims = await exchangeCodeForClaims({
// ...
apiKey: process.env.HAWCX_API_KEY!,
publicKey: process.env.OAUTH_PUBLIC_KEY! // PEM string
});From file path:
const claims = await exchangeCodeForClaims({
// ...
apiKey: process.env.HAWCX_API_KEY!,
publicKey: '/path/to/public.pem' // Absolute path
});Expected PEM format:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----Error Handling
The SDK provides specific exceptions for different failure scenarios:
import {
exchangeCodeForClaims,
OAuthExchangeError,
JWTVerificationError,
InvalidPublicKeyError
} from '@hawcx/oauth-client';
try {
const claims = await exchangeCodeForClaims({
code: authCode,
oauthTokenUrl: tokenEndpoint,
clientId: clientId,
apiKey: process.env.HAWCX_API_KEY!,
publicKey: publicKey
});
} catch (error) {
if (error instanceof OAuthExchangeError) {
console.error('OAuth exchange failed:', error.message);
// The authorization code may be invalid or expired
} else if (error instanceof JWTVerificationError) {
console.error('JWT verification failed:', error.message);
// The JWT signature or claims validation failed
} else if (error instanceof InvalidPublicKeyError) {
console.error('Invalid public key:', error.message);
// The provided public key is malformed
} else {
throw error;
}
}Integration Examples
Express.js
import express, { Request, Response } from 'express';
import { exchangeCodeForClaims } from '@hawcx/oauth-client';
const app = express();
app.use(express.json());
// Callback endpoint after user completes Hawcx authentication
app.post('/auth/callback', async (req: Request, res: Response) => {
const { code } = req.body;
if (!code) {
return res.status(400).json({ error: 'Missing authorization code' });
}
try {
const claims = await exchangeCodeForClaims({
code,
oauthTokenUrl: process.env.OAUTH_TOKEN_ENDPOINT!,
clientId: process.env.OAUTH_CLIENT_ID!,
apiKey: process.env.HAWCX_API_KEY!,
jwksUrl: process.env.JWKS_URL!,
audience: process.env.OAUTH_CLIENT_ID
});
// Create user session
req.session.userId = claims.sub;
req.session.email = claims.email;
res.json({ success: true, userId: claims.sub });
} catch (error) {
console.error('Authentication failed:', error);
res.status(401).json({ error: 'Authentication failed' });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));Fastify
import Fastify, { FastifyRequest, FastifyReply } from 'fastify';
import { exchangeCodeForClaims } from '@hawcx/oauth-client';
const app = Fastify();
app.post<{ Body: { code: string } }>('/auth/callback', async (request: FastifyRequest, reply: FastifyReply) => {
const { code } = request.body;
try {
const claims = await exchangeCodeForClaims({
code,
oauthTokenUrl: process.env.OAUTH_TOKEN_ENDPOINT!,
clientId: process.env.OAUTH_CLIENT_ID!,
apiKey: process.env.HAWCX_API_KEY!,
jwksUrl: process.env.JWKS_URL!
});
reply.send({ success: true, userId: claims.sub });
} catch (error) {
reply.status(401).send({ error: 'Authentication failed' });
}
});
app.listen({ port: 3000 }, (err) => {
if (err) throw err;
console.log('Server running on port 3000');
});PKCE Support
For enhanced security, especially in native or SPA applications, use PKCE:
// On client: Generate code verifier
const codeVerifier = generateRandomString(128);
const codeChallenge = base64url(sha256(codeVerifier));
// On backend: Exchange with verifier
const claims = await exchangeCodeForClaims({
code: authCode,
codeVerifier, // Include the verifier from client session
oauthTokenUrl: process.env.OAUTH_TOKEN_ENDPOINT!,
clientId: process.env.OAUTH_CLIENT_ID!,
apiKey: process.env.HAWCX_API_KEY!,
jwksUrl: process.env.JWKS_URL!
});Next Steps
- View the complete API reference for advanced features and detailed method signatures
- Set up MFA management for your users
- Join our developer community on Slack for support and updates