TILLAUTH · QUICKSTART

React in five lines.

Wrap your app in TillAuthProvider, call useSignIn, ship. The provider auto-refreshes tokens, catches OAuth fragments, and exposes a consistent session object.

1 · Get an app ID

Sign in to the dashboard, open TillDev → TillAuth → All apps, click + New app. Pick a name and a slug (your hosted-login URL becomes <slug>.tilldev.app). Copy the public app ID — looks like tau_pub_….

2 · Install

bash
pnpm add @tilldev/auth-react

3 · Wrap your root

tsx
// app/layout.tsx (or wherever your root lives)
import { TillAuthProvider } from '@tilldev/auth-react'

export default function RootLayout({ children }) {
  return (
    <TillAuthProvider appId={process.env.NEXT_PUBLIC_TILLAUTH_APP_ID!}>
      {children}
    </TillAuthProvider>
  )
}

Set NEXT_PUBLIC_TILLAUTH_APP_ID in your env to your app's public ID. Build-time inline; client code reads it from the bundle.

4 · Sign in

tsx
// app/login/page.tsx
'use client'
import { useState } from 'react'
import { useSignIn } from '@tilldev/auth-react'

export default function Login() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const { signIn, pending, error } = useSignIn()

  async function submit(e: React.FormEvent) {
    e.preventDefault()
    const r = await signIn(email, password)
    if (r.ok) window.location.href = '/'
    // MFA challenge?  r.mfa_required + r.challenge_token are set.
  }

  return (
    <form onSubmit={submit}>
      <input type="email" value={email} onChange={e => setEmail(e.target.value)} />
      <input type="password" value={password} onChange={e => setPassword(e.target.value)} />
      <button disabled={pending}>{pending ? 'Signing in…' : 'Sign in'}</button>
      {error && <p>{error}</p>}
    </form>
  )
}

That's it for password auth. If the user has MFA enrolled, signIn() returns { ok: false, mfa_required: true, challenge_token } — pass the challenge to useMfa().verify() with the user's TOTP code.

5 · Read the session anywhere

tsx
// app/profile/page.tsx
'use client'
import { useSession } from '@tilldev/auth-react'

export default function Profile() {
  const session = useSession()
  if (!session) return <p>Not signed in.</p>
  return <p>Hello, {session.user.email}.</p>
}

useSession() returns { user, access_token } | null with full type safety. The provider auto-refreshes the access token 60s before expiry, so this stays current as long as the tab is open.

What's next