Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Aking16/timify/llms.txt

Use this file to discover all available pages before exploring further.

Timify delegates all authentication to better-auth, a framework-agnostic auth library with first-class Next.js support. The server-side auth instance lives in src/lib/auth.ts and handles session creation, validation, and cookie management. A lightweight client wrapper in src/lib/auth-client.ts exposes auth methods to React client components.

How Authentication Is Configured

The auth instance is initialized with a Drizzle adapter pointing at the SQLite database, email/password sign-in, a localization plugin, and several advanced cookie settings:
src/lib/auth.ts
import { db } from "@/db";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { localization } from "better-auth-localization";
import * as schema from "@/db/schema";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "sqlite",
    schema,
  }),
  emailAndPassword: {
    enabled: true,
  },
  plugins: [
    localization({
      defaultLocale: "fa-IR",
    }),
  ],
  trustedOrigins: [
    process.env.BETTER_AUTH_URL || "http://localhost:3000",
    "http://192.168.100.5:3000",
  ],
  advanced: {
    crossSubDomainCookies: {
      enabled: true,
    },
    useSecureCookies: false, // Set to false for local development over HTTP
  },
});

Environment Variables

Two environment variables drive the auth configuration. Both must be set before starting the app.
BETTER_AUTH_SECRET
string
required
A secret string used to sign session tokens. Any token signed with a different secret (or a rotated secret) will be rejected, invalidating all existing sessions.
BETTER_AUTH_SECRET=replace-with-openssl-rand-base64-32-output
Generate this value with openssl rand -base64 32 and treat it like a password. Rotating this secret invalidates all active user sessions.
BETTER_AUTH_URL
string
default:"http://localhost:3000"
required
The fully-qualified base URL of the application. better-auth uses this to populate the trustedOrigins list and to build absolute callback URLs. It must match the URL that browsers use to reach the app.
# Development
BETTER_AUTH_URL=http://localhost:3000

# Production
BETTER_AUTH_URL=https://timify.example.com

Auth API Route

The better-auth request handler is mounted at /api/auth/[...all] via Next.js route handlers. All sign-in, sign-out, and session API calls are routed through here:
src/app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { POST, GET } = toNextJsHandler(auth);
You do not need to modify this file. better-auth handles all endpoint logic internally.

Auth Client

Client components import authClient from src/lib/auth-client.ts to call auth methods (sign-in, sign-out, sign-up) without directly touching the server:
src/lib/auth-client.ts
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
  baseURL: process.env.BETTER_AUTH_URL,
});
authClient exposes reactive hooks and async methods such as authClient.signIn.email(), authClient.signUp.email(), and authClient.signOut().

Session Management

Sessions are stored in the session table in the SQLite database (see Database for the schema). Each session row contains the user ID, an expiry timestamp, IP address, and user-agent. The requireSession() server function in src/lib/auth-guard.ts is the primary way server components and route handlers enforce authentication:
src/lib/auth-guard.ts
"use server";

import { defaultRoutes } from "@/constants/routes";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { auth } from "./auth";

export async function requireSession() {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session) {
    redirect(defaultRoutes.authPage); // redirects to /auth
  }

  return session;
}
Call requireSession() at the top of any Server Component or Server Action that requires the user to be logged in. It returns the full session object on success, or redirects to /auth if no valid session exists.

Auth Routes

RoutePurpose
/authLogin page (default tab)
/auth?tab=registerRegistration page
/api/auth/[...all]better-auth internal API handler
The defaultRoutes constants in src/constants/routes.ts drive navigation throughout the app:
src/constants/routes.ts
export const defaultRoutes = {
  authPage: "/auth",
  authRedirectPage: "/",
};
Sign-out is handled via handleLogout() in src/lib/auth-libs.ts, which calls authClient.signOut() and redirects to / on success:
src/lib/auth-libs.ts
import { defaultRoutes } from "@/constants/routes";
import { redirect } from "next/navigation";
import { authClient } from "./auth-client";

export async function handleLogout() {
  await authClient.signOut({
    fetchOptions: {
      onSuccess: () => {
        redirect(defaultRoutes.authRedirectPage); // redirects to /
      },
    },
  });
}
Cross-subdomain cookies are enabled in src/lib/auth.ts:
advanced: {
  crossSubDomainCookies: {
    enabled: true,
  },
},
This allows a session cookie set on timify.example.com to be sent to subdomains such as app.timify.example.com. For single-domain deployments this setting has no visible effect.
The useSecureCookies flag is currently set to false to allow development over plain HTTP:
advanced: {
  useSecureCookies: false, // Set to false for local development over HTTP
},
In production, cookies transmitted over HTTP can be intercepted. When deploying behind HTTPS, change this to true in src/lib/auth.ts:
advanced: {
  useSecureCookies: true,
},
This marks session cookies with the Secure attribute so browsers only send them over encrypted connections.

Localization

The better-auth-localization plugin is registered with defaultLocale: "fa-IR" (Persian / Farsi). This localizes built-in better-auth error messages and UI strings to Persian:
plugins: [
  localization({
    defaultLocale: "fa-IR",
  }),
],
To switch to a different locale or add locale-specific overrides, replace "fa-IR" with the desired BCP 47 language tag supported by better-auth-localization.

Trusted Origins

The trustedOrigins list controls which origins are allowed to make authenticated requests to the better-auth API. It is pre-populated with BETTER_AUTH_URL and a local LAN address for development convenience:
trustedOrigins: [
  process.env.BETTER_AUTH_URL || "http://localhost:3000",
  "http://192.168.100.5:3000",
],
In production, make sure BETTER_AUTH_URL is set to your public domain. You can remove the LAN address entry or add additional origins as needed for your deployment topology.

Production Checklist

1

Set a strong BETTER_AUTH_SECRET

Generate a cryptographically random secret and add it to your production environment:
openssl rand -base64 32
2

Set BETTER_AUTH_URL to your public domain

BETTER_AUTH_URL=https://timify.example.com
3

Enable secure cookies

In src/lib/auth.ts, set useSecureCookies: true so session cookies are only transmitted over HTTPS.
4

Apply the database schema

Run npx drizzle-kit push against your production database to ensure all auth tables (user, session, account, verification) exist.

Build docs developers (and LLMs) love