Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.hawcx.com/llms.txt

Use this file to discover all available pages before exploring further.

Hawcx Web SDK Quickstart

Add passwordless authentication to your web app. The SDK handles device registration, login, and MFA - then sends the authorization code to your backend for validation.

Installation

Add the Hawcx SDK script to your HTML page:
<script src="https://drprpwzor5vmy.cloudfront.net/v1.1.58/hawcx-auth.window.min.js" defer></script>
Then in your JavaScript, initialize it:
const hawcx = await window.hawcx.init('YOUR_API_KEY', "your-base-url");
if (hawcx.status !== 'INITIALIZED') {
  console.error('Failed to initialize');
}

Setup

Step 1: Initialize

See Installation section above. The SDK will be available as window.hawcx after initialization.

Step 2: Start Authentication

async function handleLogin(email) {
  const response = await hawcx.authenticate(email);

  if (response.status === 'SUCCESS') {
    // Trusted device - send code to backend
    await sendToBackend(email, response.code);

  } else if (response.status === 'OTP_NEEDED') {
    // New device - show registration flow
    showRegistrationFlow(email);

  } else if (response.status === 'MFA_REQUIRED') {
    // Existing device with MFA - show MFA input
    showMfaScreen(email, response.sessionId);
  }
}

Step 3: Handle Registration (New Device)

async function showRegistrationFlow(email) {
  // Step 1: Verify email OTP
  const emailOtp = prompt('Enter OTP from email:');
  await hawcx.verifyEmailOtp({ 
    userid: email, 
    otp: emailOtp 
  });

  // Step 2: Complete registration with MFA
  const result = await hawcx.verifyDevice({
    userid: email,
    mfaMethod: 'sms'  // 'email', 'sms', or 'totp'
  });

  if (result.status === 'SUCCESS' || result.status === 'DEVICE_REGISTERED' || result.code) {
    // Send authorization code to backend
    await sendToBackend(email, result.code);
  }
}

Step 4: Handle MFA

async function showMfaScreen(email, sessionId) {
  // Start MFA challenge
  await hawcx.initiateMfa({ 
    userid: email, 
    sessionId 
  });

  // Get OTP from user
  const otp = prompt('Enter verification code:');

  // Verify MFA
  const result = await hawcx.verifyMfa({
    userid: email,
    otp: otp,
    remember_me: true  // Optional: skip MFA on this device next time
  });

  if (result.status === 'SUCCESS') {
    // Send authorization code to backend
    await sendToBackend(email, result.code);
  }
}

Step 5: Backend Validation

Your backend receives the authorization code and validates it:
import { exchangeCodeForClaims } from '@hawcx/oauth-client';

app.post('/api/login', async (req, res) => {
  try {
    const { code, email } = req.body;
    
    // Validate authorization code
    const claims = await exchangeCodeForClaims({
      code,
      oauthTokenUrl: process.env.OAUTH_TOKEN_ENDPOINT,
      clientId: process.env.OAUTH_CLIENT_ID,
      publicKey: process.env.OAUTH_PUBLIC_KEY,
      apiKey: process.env.HAWCX_API_KEY,
      code_verifier: req.body.code_verifier  // PKCE if used
    });
    
    // Create session for user
    const sessionToken = jwt.sign(
      { userId: claims.sub, email: claims.email },
      process.env.SESSION_SECRET,
      { expiresIn: '7d' }
    );
    
    res.json({ success: true, sessionToken });
  } catch (error) {
    res.status(401).json({ error: 'Authentication failed' });
  }
});

Step 6: Frontend Handles Response

async function sendToBackend(email, authCode) {
  const response = await fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ 
      code: authCode, 
      email: email,
      code_verifier: retrievePkceVerifier(email)  // Optional: PKCE
    })
  });

  if (response.ok) {
    const { sessionToken } = await response.json();
    localStorage.setItem('sessionToken', sessionToken);
    
    // Redirect to app or update UI
    window.location.href = '/app';
  } else {
    alert('Authentication failed');
  }
}

Authentication Flows

New Device (First Time)

authenticate() → OTP_NEEDED → verifyEmailOtp() → verifyDevice() → 
SUCCESS → Send code to backend → Logged in ✓

Trusted Device (No MFA)

authenticate() → SUCCESS → Send code to backend → Logged in ✓

Device with MFA Enabled

authenticate() → MFA_REQUIRED → initiateMfa() → verifyMfa() → 
SUCCESS → Send code to backend → Logged in ✓

Full Example

<!DOCTYPE html>
<html>
<head>
  <title>Hawcx Login</title>
  <style>
    body { font-family: sans-serif; max-width: 400px; margin: 50px auto; }
    input { width: 100%; padding: 8px; margin: 10px 0; box-sizing: border-box; }
    button { width: 100%; padding: 10px; background: #0076e0; color: white; border: none; cursor: pointer; }
  </style>
</head>
<body>
  <h1>Login</h1>
  <input type="email" id="email" placeholder="Email">
  <button onclick="handleLogin()">Sign In</button>
  <div id="status"></div>

  <script type="module">
    import { HawcxInitializer } from 'https://drprpwzor5vmy.cloudfront.net/v1.1.55/hawcx-auth.esm.min.js';
    
    let hawcx;
    let currentEmail;

    // Initialize
    async function init() {
      hawcx = await HawcxInitializer.init('YOUR_API_KEY', "your-base-url");
      document.getElementById('status').textContent = 'Ready';
    }

    // Handle login
    window.handleLogin = async function() {
      currentEmail = document.getElementById('email').value;
      if (!currentEmail) return alert('Enter email');

      try {
        const response = await hawcx.authenticate(currentEmail);

        if (response.status === 'SUCCESS') {
          // Trusted device
          await exchangeCode(currentEmail, response.code);

        } else if (response.status === 'OTP_NEEDED') {
          // New device - get email OTP
          document.getElementById('status').textContent = 'Check email for OTP';
          const emailOtp = prompt('Email OTP:');
          await hawcx.verifyEmailOtp({ userid: currentEmail, otp: emailOtp });

          // Complete registration
          const result = await hawcx.verifyDevice({
            userid: currentEmail,
            mfaMethod: 'sms'  // 'email', 'sms', or 'totp'
          });

          if (result.code || result.status === 'SUCCESS' || result.status === 'DEVICE_REGISTERED') {
            await exchangeCode(currentEmail, result.code);
          }

        } else if (response.status === 'MFA_REQUIRED') {
          // MFA required
          await hawcx.initiateMfa({ userid: currentEmail, sessionId: response.sessionId });
          const mfaOtp = prompt('MFA OTP:');
          const mfaResult = await hawcx.verifyMfa({
            userid: currentEmail,
            otp: mfaOtp,
            remember_me: true
          });

          if (mfaResult.code) {
            await exchangeCode(currentEmail, mfaResult.code);
          }
        }
      } catch (error) {
        document.getElementById('status').textContent = 'Error: ' + error.message;
      }
    };

    // Exchange code with backend
    async function exchangeCode(email, code) {
      const res = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code, email })
      });
      
      if (res.ok) {
        const { sessionToken } = await res.json();
        localStorage.setItem('sessionToken', sessionToken);
        window.location.href = '/app';
      } else {
        document.getElementById('status').textContent = 'Backend auth failed';
      }
    }

    init();
  </script>
</body>
</html>

Environment Setup

Your backend needs these from the Hawcx dashboard:
OAUTH_TOKEN_ENDPOINT=https://example.hawcx.com/token
OAUTH_CLIENT_ID=your_client_id
OAUTH_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----\n...

# Your app secret
SESSION_SECRET=your_secret_key

Key Points

  • Always validate on backend - The authorization code must be exchanged by your backend
  • Handle all response statuses - Different statuses require different UI flows
  • Store tokens securely - Use httpOnly cookies or secure storage
  • Use HTTPS - Never send authentication over HTTP
  • PKCE optional - For extra security, use PKCE for code verification

Next Steps