# Authentication — SDK

> Complete guide to Aerostack authentication -- registration, login, OTP, magic links, password reset, email verification, token refresh, and profile management.

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

```ts

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

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

---

## Registration

  
```tsx
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
}
```
  
  
```ts
// Server-side: register a user programmatically
const 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)

  
```tsx
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)
}
```
  
  
```ts
const result = await sdk.auth.login('jane@example.com', 'securePassword123')
const { accessToken, refreshToken, user } = result
```
  

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.

  
```tsx
const { sendOTP, verifyOTP } = useAuth()

// Step 1: Send the code
await sendOTP('jane@example.com', 'email')
// or: await sendOTP('+1234567890', 'phone')

// Step 2: User enters the code
await verifyOTP('jane@example.com', '482916', 'email')
// user is now signed in
```
  
  
```ts
await 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'` |

  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.

```tsx
// 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

```tsx
const { resendVerificationEmail } = useAuth()

await resendVerificationEmail('jane@example.com')
```

---

## Password reset

1. **Request a reset link**

   ```tsx
   const { requestPasswordReset } = useAuth()

   await requestPasswordReset('jane@example.com')
   // User receives an email with a reset link
   ```

1. **Set the new password**

   ```tsx
   // 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.

```tsx
const { refreshAccessToken, tokens } = useAuth()

const newTokens = await refreshAccessToken(tokens.refreshToken)
```

---

## Profile management

### Update profile

```tsx
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

```tsx
const { deleteAvatar } = useAuth()

await deleteAvatar()
```

### Refresh user data

```tsx
const { refreshUser } = useAuth()

await refreshUser()
// user state is updated from the server
```

---

## Sign out

```tsx
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:

```ts

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

```ts

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

```tsx title="src/pages/Login.tsx"

  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

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