Authentication
Customer authentication lets readers register and log in on your site with email and password. The SDK manages JWT tokens in localStorage and automatically attaches them to access checks for verified identity.
Initialize the SDK as usual. Auth methods use the same apiKey and apiUrl:
import { init } from '@latch/sdk';
init({ apiKey: 'pk_...', apiUrl: 'https://your-latch-api',});Registration
Section titled “Registration”import { register } from '@latch/sdk';
try { const { customer, accessToken } = await register( 'securepass123', 'Jane Doe', // optional name ); console.log('Registered:', customer.email);} catch (err) { // err.message contains the API error (e.g. "A customer with this email already exists") console.error(err.message);}After registration, the SDK stores the access token and refresh token in localStorage. Subsequent checkAccess() calls automatically include the verified JWT.
import { login } from '@latch/sdk';
try { console.log('Logged in as:', customer.email);} catch (err) { console.error(err.message); // "Invalid email or password"}Logout
Section titled “Logout”import { logout } from '@latch/sdk';
await logout();// Refresh token is revoked on the server// All stored tokens are clearedChecking auth state
Section titled “Checking auth state”import { isAuthenticated, getStoredCustomer, getSession } from '@latch/sdk';
// Synchronous check (no network request)if (isAuthenticated()) { const customer = getStoredCustomer(); console.log('Welcome back,', customer?.name);}
// Server-validated profile (refreshes token if needed)const profile = await getSession();if (profile) { console.log(profile.email, profile.customAttributes);}Auth state changes
Section titled “Auth state changes”Listen for login, logout, and token refresh events:
import { onAuthChange } from '@latch/sdk';
const unsubscribe = onAuthChange((customer) => { if (customer) { showUserMenu(customer.name || customer.email); } else { showLoginButton(); }});
// Later: stop listeningunsubscribe();How access checks work with auth
Section titled “How access checks work with auth”When the customer is authenticated, checkAccess() automatically attaches the JWT:
import { checkAccess } from '@latch/sdk';
const result = await checkAccess();// The API verifies the JWT and uses the customer's real ID// for subscription lookups and paywall evaluationIf the access token is expired, the SDK silently refreshes it before the access check. If the refresh token is also expired, the customer is treated as anonymous.
Token lifecycle
Section titled “Token lifecycle”| Token | Storage | Expiry | Renewal |
|---|---|---|---|
| Access token (JWT) | localStorage | 15 minutes | Automatic via refresh token |
| Refresh token | localStorage | 30 days | Rotated on each use (old token invalidated) |
On page load, the SDK restores auth state from localStorage. If the access token is expired, it is silently refreshed on the next checkAccess() or getSession() call.
Full example
Section titled “Full example”<script type="module"> import { init, login, register, logout, onAuthChange, checkAccess, isAuthenticated, } from '@latch/sdk';
init({ apiKey: 'pk_...', apiUrl: 'https://your-latch-api' });
// Update UI on auth changes onAuthChange((customer) => { document.getElementById('auth-status').textContent = customer ? `Hello, ${customer.name || customer.email}` : 'Not logged in'; });
// Registration form document.getElementById('register-form')?.addEventListener('submit', async (e) => { e.preventDefault(); const form = new FormData(e.target); try { await register(form.get('email'), form.get('password'), form.get('name')); } catch (err) { alert(err.message); } });
// Login form document.getElementById('login-form')?.addEventListener('submit', async (e) => { e.preventDefault(); const form = new FormData(e.target); try { await login(form.get('email'), form.get('password')); // Access checks now use verified identity const access = await checkAccess(); console.log('Access:', access.granted); } catch (err) { alert(err.message); } });
// Logout button document.getElementById('logout-btn')?.addEventListener('click', () => logout());</script>Security notes
Section titled “Security notes”- Auth endpoints accept publishable keys (
pk_), so the SDK can call them directly from the browser. The customer’s password is the real credential. - Passwords are hashed with Argon2id before storage.
- Refresh tokens are stored as SHA-256 hashes in the database.
- Refresh tokens are rotated on use — each refresh invalidates the old token.
- JWTs are signed with HMAC-SHA256 and verified on every access check.
- Login returns the same error for wrong password and non-existent email to prevent email enumeration.
- Customers are scoped to a publication — a customer on site A cannot authenticate on site B.