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' });
}
});
from hawcx_oauth_client import exchange_code_for_claims
import os, jwt
from datetime import datetime, timedelta
@app.route('/api/login', methods=['POST'])
def login():
try:
data = request.get_json()
code = data.get('code')
email = data.get('email')
# Validate authorization code
claims = exchange_code_for_claims(
code=code,
oauth_token_url=os.getenv('OAUTH_TOKEN_ENDPOINT'),
client_id=os.getenv('OAUTH_CLIENT_ID'),
public_key=os.getenv('OAUTH_PUBLIC_KEY'),
api_key=os.getenv('HAWCX_API_KEY'),
code_verifier=data.get('code_verifier') # PKCE if used
)
# Create session for user
session_token = jwt.encode(
{
'userId': claims['sub'],
'email': claims['email'],
'exp': datetime.utcnow() + timedelta(days=7)
},
os.getenv('SESSION_SECRET'),
algorithm='HS256'
)
return jsonify({'success': True, 'sessionToken': session_token})
except Exception as error:
return jsonify({'error': 'Authentication failed'}), 401
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