Javascript Client
This example demonstrates how to interact with ezauth's JSON API using a Javascript client (SPA style).
The full source code is available in _example/javascript-client.
Overview
In this scenario, ezauth acts as a backend API. The Go server (main.go) serves static HTML/JS files, and the browser communicates directly with the ezauth endpoints mounted at /auth.
- API Registration:
POST /auth/api/register - API Login:
POST /auth/api/login - User Info:
GET /auth/api/userinfo - Logout:
POST /auth/api/logout
Backend Setup
The Go server simply mounts ezauth and serves static files.
// Initialize ezauth with "auth" path prefix
authApp, _ := ezauth.New(&cfg, "auth")
// Mount handler
r.Handle("/auth/*", authApp.Handler)
// Serve static files
r.Handle("/*", http.FileServer(http.FS(staticFS)))
Frontend Logic
Login
Send a JSON request to /auth/api/login. The server verifies credentials and returns Access and Refresh tokens.
async function login(email, password) {
const response = await fetch('/auth/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (response.ok) {
const data = await response.json();
const { access_token, refresh_token } = data.data; // ezauth returns { data: ... } wrapped response
// Store tokens (localStorage for simplicity, but treat carefully)
localStorage.setItem('access_token', access_token);
localStorage.setItem('refresh_token', refresh_token);
return true;
}
return false;
}
Accessing Protected Resources
To access protected endpoints (like /auth/api/userinfo), include the Access Token in the Authorization header.
async function getUserInfo() {
const token = localStorage.getItem('access_token');
const response = await fetch('/auth/api/userinfo', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
const data = await response.json();
return data.data; // The user object
}
return null;
}
Step-up Login (Multi-Factor Authentication)
If the account has TOTP MFA enabled, POST /auth/api/login doesn't return tokens directly — it returns { "mfa_required": true, "mfa_token": "..." } instead. Prompt the user for their authenticator code and exchange it (plus the mfa_token) at POST /auth/api/mfa/login/verify to get real session tokens. Passing remember_device: true returns a device_token; send it back via the X-Device-Token header on future logins to skip the step-up prompt for EZAUTH_TRUSTED_DEVICE_TTL.
async function login(email, password) {
const response = await fetch('/auth/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Send a previously-issued device token, if any, to skip MFA on trusted devices.
'X-Device-Token': localStorage.getItem('device_token') || '',
},
body: JSON.stringify({ email, password })
});
if (!response.ok) return { ok: false };
const { data } = await response.json();
if (data.mfa_required) {
// Stash the mfa_token; the UI should now prompt for a TOTP/recovery code.
sessionStorage.setItem('mfa_token', data.mfa_token);
return { ok: true, mfaRequired: true };
}
storeTokens(data);
return { ok: true, mfaRequired: false };
}
async function verifyMFA(code, rememberDevice = false) {
const mfaToken = sessionStorage.getItem('mfa_token');
const response = await fetch('/auth/api/mfa/login/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mfa_token: mfaToken, code, remember_device: rememberDevice })
});
if (!response.ok) return false; // wrong code — let the user retry
const { data } = await response.json();
sessionStorage.removeItem('mfa_token');
if (data.device_token) {
// Only present when remember_device was true; persist for the X-Device-Token header above.
localStorage.setItem('device_token', data.device_token);
}
storeTokens(data);
return true;
}
function storeTokens({ access_token, refresh_token }) {
localStorage.setItem('access_token', access_token);
localStorage.setItem('refresh_token', refresh_token);
}
Token Refresh
ezauth provides a refresh endpoint. When the access token expires (401 Unauthorized), use the refresh token to get a new one.
async function refreshAccessToken() {
const refreshToken = localStorage.getItem('refresh_token');
const response = await fetch('/auth/api/token/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refreshToken })
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('access_token', data.data.access_token);
localStorage.setItem('refresh_token', data.data.refresh_token); // Update refresh token if rotated
return true;
}
return false;
}