Authentication — SDK
The Aerostack SDK provides a complete authentication system: email/password registration, passwordless OTP, magic link login, email verification, password reset, token refresh, and profile management. All methods are available through the useAuth() React hook and the server-side sdk.auth module.
What you can build
Section titled “What you can build”- SaaS login pages — Email/password registration with email verification
- Mobile OTP flows — Send a 6-digit code to email or phone, verify and sign in
- Magic link onboarding — Frictionless sign-in via email link
- Password reset — Self-service reset with secure token-based flow
- Profile management — Update name, avatar, and custom fields
- Bot protection — Cloudflare Turnstile integration on sign-in and sign-up
React: useAuth() hook
Section titled “React: useAuth() hook”The useAuth() hook returns authentication state and all auth methods.
import { useAuth } from '@aerostack/react'
const { user, // User | null -- current signed-in user tokens, // { accessToken, refreshToken?, expiresAt? } | null loading, // boolean -- true during any auth operation error, // string | null -- last error message isAuthenticated, // boolean -- shorthand for !!tokens?.accessToken} = useAuth()User type
Section titled “User type”interface User { id: string email: string name?: string avatar_url?: string emailVerified: boolean createdAt?: string customFields?: Record<string, any>}Registration
Section titled “Registration”const { signUp } = useAuth()
const result = await signUp('jane@example.com', 'securePassword123', { name: 'Jane Doe', customFields: { plan: 'free', referral: 'twitter' }, turnstileToken: token, // optional Cloudflare Turnstile})
if (result.requiresVerification) { // Show "check your email" UI}// Server-side: register a user programmaticallyconst result = await sdk.auth.register({ email: 'jane@example.com', password: 'securePassword123', name: 'Jane Doe',})Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | New user’s email address |
password | string | Yes | Password (minimum length set in project settings) |
opts.name | string | No | Display name |
opts.customFields | Record<string, any> | No | Arbitrary profile data |
opts.turnstileToken | string | No | Cloudflare Turnstile bot protection token |
Sign in (email/password)
Section titled “Sign in (email/password)”const { signIn, error } = useAuth()
try { await signIn('jane@example.com', 'securePassword123') // user and tokens are now set} catch (err) { // error state is also updated console.error(err.message)}const result = await sdk.auth.login('jane@example.com', 'securePassword123')const { accessToken, refreshToken, user } = resultSupports optional turnstileToken as a third parameter for bot protection.
Passwordless OTP
Section titled “Passwordless OTP”Send a one-time code to an email or phone number, then verify it to sign in. No password required.
const { sendOTP, verifyOTP } = useAuth()
// Step 1: Send the codeawait sendOTP('jane@example.com', 'email')// or: await sendOTP('+1234567890', 'phone')
// Step 2: User enters the codeawait verifyOTP('jane@example.com', '482916', 'email')// user is now signed inawait sdk.auth.sendOTP({ email: 'jane@example.com' })const result = await sdk.auth.verifyOTP({ email: 'jane@example.com', code: '482916' })Parameters:
| Method | Parameter | Type | Default |
|---|---|---|---|
sendOTP | identifier | string | — |
sendOTP | type | 'email' | 'phone' | 'email' |
verifyOTP | identifier | string | — |
verifyOTP | code | string | — |
verifyOTP | type | 'email' | 'phone' | 'email' |
Email verification
Section titled “Email verification”When email verification is enabled in your project settings, new users receive a verification email after registration.
// In your /verify-email page componentconst { verifyEmail } = useAuth()
useEffect(() => { const token = new URLSearchParams(window.location.search).get('token') if (token) { verifyEmail(token) }}, [])Resend verification email
Section titled “Resend verification email”const { resendVerificationEmail } = useAuth()
await resendVerificationEmail('jane@example.com')Password reset
Section titled “Password reset”-
Request a reset link
const { requestPasswordReset } = useAuth()await requestPasswordReset('jane@example.com')// User receives an email with a reset link -
Set the new password
// In your /reset-password page componentconst { resetPassword } = useAuth()const token = new URLSearchParams(window.location.search).get('token')await resetPassword(token, 'newSecurePassword456')
Token refresh
Section titled “Token refresh”Access tokens expire after a configurable period (default: 15 minutes). The SDK automatically refreshes tokens on 401 responses, but you can also refresh manually.
const { refreshAccessToken, tokens } = useAuth()
const newTokens = await refreshAccessToken(tokens.refreshToken)Profile management
Section titled “Profile management”Update profile
Section titled “Update profile”const { updateProfile } = useAuth()
await updateProfile({ name: 'Jane Smith', avatar_url: 'https://cdn.example.com/avatar.jpg', customFields: { plan: 'pro', onboarded: true },})// user state is automatically refreshed| Field | Type | Description |
|---|---|---|
name | string | Display name |
avatar_url | string | Direct URL to avatar image |
avatar_image_id | string | Storage image ID (auto-resolves to URL) |
customFields | Record<string, any> | Merged into existing custom fields |
Delete avatar
Section titled “Delete avatar”const { deleteAvatar } = useAuth()
await deleteAvatar()Refresh user data
Section titled “Refresh user data”const { refreshUser } = useAuth()
await refreshUser()// user state is updated from the serverSign out
Section titled “Sign out”const { signOut } = useAuth()
await signOut()// Invalidates the refresh token server-side// Clears user and tokens from local stateServer-side token verification
Section titled “Server-side token verification”In your backend (Node.js, Cloudflare Worker, or edge function), verify access tokens sent by the client:
import { AerostackClient } from '@aerostack/sdk'
const { sdk } = new AerostackClient({ projectId, apiKey })
// In your request handlerconst token = request.headers.get('Authorization')?.replace('Bearer ', '')const user = await sdk.auth.verifyToken(token)
if (!user) { return new Response('Unauthorized', { status: 401 })}Hono middleware pattern
Section titled “Hono middleware pattern”import { Hono } from 'hono'import { createMiddleware } from 'hono/factory'
const requireAuth = createMiddleware(async (c, next) => { const token = c.req.header('Authorization')?.replace('Bearer ', '') if (!token) return c.json({ error: 'Missing token' }, 401)
const user = await sdk.auth.verifyToken(token) if (!user) return c.json({ error: 'Invalid token' }, 401)
c.set('user', user) await next()})
app.get('/api/me', requireAuth, (c) => c.json(c.get('user')))Complete example: login page
Section titled “Complete example: login page”import { useAuth } from '@aerostack/react'
export function LoginPage() { const { signIn, signUp, sendOTP, verifyOTP, error, loading } = useAuth() const [mode, setMode] = useState<'password' | 'otp'>('password') const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [otpCode, setOtpCode] = useState('') const [otpSent, setOtpSent] = useState(false)
const handlePasswordLogin = async (e: React.FormEvent) => { e.preventDefault() await signIn(email, password) }
const handleSendOTP = async () => { await sendOTP(email, 'email') setOtpSent(true) }
const handleVerifyOTP = async (e: React.FormEvent) => { e.preventDefault() await verifyOTP(email, otpCode, 'email') }
return ( <div> <div> <button onClick={() => setMode('password')}>Password</button> <button onClick={() => setMode('otp')}>OTP</button> </div>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email address" />
{mode === 'password' ? ( <form onSubmit={handlePasswordLogin}> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" /> <button type="submit" disabled={loading}>Sign in</button> </form> ) : otpSent ? ( <form onSubmit={handleVerifyOTP}> <input value={otpCode} onChange={(e) => setOtpCode(e.target.value)} placeholder="Enter 6-digit code" maxLength={6} /> <button type="submit" disabled={loading}>Verify</button> </form> ) : ( <button onClick={handleSendOTP} disabled={loading}> Send code to {email || '...'} </button> )}
{error && <p style={{ color: 'red' }}>{error}</p>} </div> )}All useAuth methods
Section titled “All useAuth methods”| Method | Signature | Description |
|---|---|---|
signIn | (email, password, turnstileToken?) => Promise | Sign in with credentials |
signUp | (email, password, opts?) => Promise | Register a new user |
signOut | () => Promise | Sign out and invalidate tokens |
sendOTP | (identifier, type?) => Promise | Send a one-time code |
verifyOTP | (identifier, code, type?) => Promise | Verify OTP and sign in |
verifyEmail | (token) => Promise | Verify email address |
resendVerificationEmail | (email) => Promise | Resend verification link |
requestPasswordReset | (email, turnstileToken?) => Promise | Send password reset email |
resetPassword | (token, newPassword) => Promise | Set new password |
refreshAccessToken | (refreshToken) => Promise | Refresh the access token |
refreshUser | () => Promise | Re-fetch user profile |
updateProfile | (updates) => Promise | Update name, avatar, custom fields |
deleteAvatar | () => Promise | Remove the user’s avatar |