SDK (Beta)Authentication

Authentication

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.

⚠️

Beta — Auth APIs are stable but may receive non-breaking additions in minor versions.

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

The useAuth() hook returns authentication state and all auth methods.

State

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

interface User {
  id: string
  email: string
  name?: string
  avatar_url?: string
  emailVerified: boolean
  createdAt?: string
  customFields?: Record<string, any>
}

Registration

const { signUp } = useAuth()
 
const result = await signUp('[email protected]', 'securePassword123', {
  name: 'Jane Doe',
  customFields: { plan: 'free', referral: 'twitter' },
  turnstileToken: token, // optional Cloudflare Turnstile
})
 
if (result.requiresVerification) {
  // Show "check your email" UI
}

Parameters:

ParameterTypeRequiredDescription
emailstringYesNew user’s email address
passwordstringYesPassword (minimum length set in project settings)
opts.namestringNoDisplay name
opts.customFieldsRecord<string, any>NoArbitrary profile data
opts.turnstileTokenstringNoCloudflare Turnstile bot protection token

Sign in (email/password)

const { signIn, error } = useAuth()
 
try {
  await signIn('[email protected]', 'securePassword123')
  // user and tokens are now set
} catch (err) {
  // error state is also updated
  console.error(err.message)
}

Supports optional turnstileToken as a third parameter for bot protection.


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 code
await sendOTP('[email protected]', 'email')
// or: await sendOTP('+1234567890', 'phone')
 
// Step 2: User enters the code
await verifyOTP('[email protected]', '482916', 'email')
// user is now signed in

Parameters:

MethodParameterTypeDefault
sendOTPidentifierstring
sendOTPtype'email' | 'phone''email'
verifyOTPidentifierstring
verifyOTPcodestring
verifyOTPtype'email' | 'phone''email'

OTP codes expire after 10 minutes. After 3 failed verification attempts, the user is locked out for 60 minutes.


Email verification

When email verification is enabled in your project settings, new users receive a verification email after registration.

// In your /verify-email page component
const { verifyEmail } = useAuth()
 
useEffect(() => {
  const token = new URLSearchParams(window.location.search).get('token')
  if (token) {
    verifyEmail(token)
  }
}, [])

Resend verification email

const { resendVerificationEmail } = useAuth()
 
await resendVerificationEmail('[email protected]')

Password reset

const { requestPasswordReset } = useAuth()
 
await requestPasswordReset('[email protected]')
// User receives an email with a reset link

Set the new password

// In your /reset-password page component
const { resetPassword } = useAuth()
 
const token = new URLSearchParams(window.location.search).get('token')
await resetPassword(token, 'newSecurePassword456')

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

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
FieldTypeDescription
namestringDisplay name
avatar_urlstringDirect URL to avatar image
avatar_image_idstringStorage image ID (auto-resolves to URL)
customFieldsRecord<string, any>Merged into existing custom fields

Delete avatar

const { deleteAvatar } = useAuth()
 
await deleteAvatar()

Refresh user data

const { refreshUser } = useAuth()
 
await refreshUser()
// user state is updated from the server

Sign out

const { signOut } = useAuth()
 
await signOut()
// Invalidates the refresh token server-side
// Clears user and tokens from local state

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 handler
const 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

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

src/pages/Login.tsx
import { useAuth } from '@aerostack/react'
import { useState } from '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

MethodSignatureDescription
signIn(email, password, turnstileToken?) => PromiseSign in with credentials
signUp(email, password, opts?) => PromiseRegister a new user
signOut() => PromiseSign out and invalidate tokens
sendOTP(identifier, type?) => PromiseSend a one-time code
verifyOTP(identifier, code, type?) => PromiseVerify OTP and sign in
verifyEmail(token) => PromiseVerify email address
resendVerificationEmail(email) => PromiseResend verification link
requestPasswordReset(email, turnstileToken?) => PromiseSend password reset email
resetPassword(token, newPassword) => PromiseSet new password
refreshAccessToken(refreshToken) => PromiseRefresh the access token
refreshUser() => PromiseRe-fetch user profile
updateProfile(updates) => PromiseUpdate name, avatar, custom fields
deleteAvatar() => PromiseRemove the user’s avatar