Circle V2 API Docs
    Preparing search index...

    Module @repo/auth-next

    @repo/auth-next

    Next.js integration for authentication. Provides cookie-backed session management (login, logout, getLoggedInUser, requireUser), composable API route handler factories for every auth flow (login, signup, OAuth, password reset, OTP), client-side React hooks, and a Next.js-specific Supabase auth client. Built on top of @repo/auth.

    %%{init:{"theme":"dark"}}%% graph TD auth_next["@repo/auth-next"] analytics_core["@repo/analytics-core"] auth["@repo/auth"] db["@repo/db"] errors["@repo/errors"] logger["@repo/logger"] safe["@repo/safe"] typescript_config["@repo/typescript-config"] vitest_config["@repo/vitest-config"] auth_next --> analytics_core auth_next --> auth auth_next --> db auth_next --> errors auth_next --> logger auth_next --> safe auth_next -.-> typescript_config auth_next -.-> vitest_config
    %%{init:{"theme":"default"}}%% graph TD auth_next["@repo/auth-next"] analytics_core["@repo/analytics-core"] auth["@repo/auth"] db["@repo/db"] errors["@repo/errors"] logger["@repo/logger"] safe["@repo/safe"] typescript_config["@repo/typescript-config"] vitest_config["@repo/vitest-config"] auth_next --> analytics_core auth_next --> auth auth_next --> db auth_next --> errors auth_next --> logger auth_next --> safe auth_next -.-> typescript_config auth_next -.-> vitest_config
    graph TD
      auth_next["@repo/auth-next"]
      analytics_core["@repo/analytics-core"]
      auth["@repo/auth"]
      db["@repo/db"]
      errors["@repo/errors"]
      logger["@repo/logger"]
      safe["@repo/safe"]
      typescript_config["@repo/typescript-config"]
      vitest_config["@repo/vitest-config"]
      auth_next --> analytics_core
      auth_next --> auth
      auth_next --> db
      auth_next --> errors
      auth_next --> logger
      auth_next --> safe
      auth_next -.-> typescript_config
      auth_next -.-> vitest_config
    Import Resolves to Description
    @repo/auth-next src/index.ts Session helpers: login, logout, getLoggedInUser, requireUser, cookie helpers
    @repo/auth-next/api src/api/index.ts All request handler factories (login, signup, OAuth, password flows, OTP)
    @repo/auth-next/hooks src/hooks/index.ts Client-side React hooks for auth flows
    @repo/auth-next/clients/* src/auth-clients/*.auth-client.ts createSupabaseNextAuthClient
    @repo/auth-next/lib/* src/lib/*.ts Low-level session/cookie utilities
    @repo/auth-next/tests __tests__/index.ts Vitest mocks for all hooks and auth clients
    %%{init:{"theme":"dark"}}%% graph TD subgraph browser [Browser] Hooks["React Hooks"] Pages["Pages / Components"] end

    subgraph nextjs [Next.js Server] Routes["API Route Files"] Factories["Request Handler Factories"] Session["Session Layer (cookies)"] Layout["Layouts (requireUser)"] end

    subgraph authPkg ["@repo/auth"] AuthClient["BaseAuthClient"] GetUser["getUserFromToken"] end

    subgraph infra [Infrastructure] Supabase["Supabase Auth"] end

    Hooks -->|"POST /api/auth/*"| Routes Pages --> Layout Layout --> Session Routes --> Factories Factories --> AuthClient Factories --> Session AuthClient --> Supabase Session -->|"read cookie"| GetUser GetUser --> DB["@repo/db"]

    %%{init:{"theme":"default"}}%% graph TD subgraph browser [Browser] Hooks["React Hooks"] Pages["Pages / Components"] end

    subgraph nextjs [Next.js Server] Routes["API Route Files"] Factories["Request Handler Factories"] Session["Session Layer (cookies)"] Layout["Layouts (requireUser)"] end

    subgraph authPkg ["@repo/auth"] AuthClient["BaseAuthClient"] GetUser["getUserFromToken"] end

    subgraph infra [Infrastructure] Supabase["Supabase Auth"] end

    Hooks -->|"POST /api/auth/*"| Routes Pages --> Layout Layout --> Session Routes --> Factories Factories --> AuthClient Factories --> Session AuthClient --> Supabase Session -->|"read cookie"| GetUser GetUser --> DB["@repo/db"]

    graph TD
    subgraph browser [Browser]
    Hooks["React Hooks"]
    Pages["Pages / Components"]
    end

    subgraph nextjs [Next.js Server] Routes["API Route Files"] Factories["Request Handler Factories"] Session["Session Layer (cookies)"] Layout["Layouts (requireUser)"] end

    subgraph authPkg ["@repo/auth"] AuthClient["BaseAuthClient"] GetUser["getUserFromToken"] end

    subgraph infra [Infrastructure] Supabase["Supabase Auth"] end

    Hooks -->|"POST /api/auth/*"| Routes Pages --> Layout Layout --> Session Routes --> Factories Factories --> AuthClient Factories --> Session AuthClient --> Supabase Session -->|"read cookie"| GetUser GetUser --> DB["@repo/db"]

    Sessions are managed via three HTTP-only cookies:

    Cookie Purpose
    access-token JWT access token from Supabase
    refresh-token Refresh token for silent re-auth
    user-state Session state: "authenticated" or "password-reset"
    import { login, logout, getLoggedInUser, requireUser } from "@repo/auth-next";
    
    Function Description
    login(tokens, options?) Sets all three cookies. Optionally pass { userState: "password-reset" }
    logout() Deletes all three cookies
    getLoggedInUser() Reads the access token cookie, calls getUserFromToken. Returns User | null
    requireUser(options?) Calls getLoggedInUser() and checks user state. Redirects to /login (or options.redirectTo) if not authenticated

    Auth routes follow a three-step pattern:

    // apps/web/lib/auth.client.ts
    import { createSupabaseNextAuthClient } from "@repo/auth-next/clients/supabase-next";

    export const authClient = createSupabaseNextAuthClient({
    oauthRedirectUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth`,
    passwordResetRedirectUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/reset-password`,
    });
    // apps/web/app/api/auth/login/route.ts
    import { createLoginRequestHandler } from "@repo/auth-next/api";

    import { authClient } from "~/lib/auth.client";

    const loginRequestHandler = createLoginRequestHandler({
    authClient,
    });

    export { loginRequestHandler as POST };

    Each factory takes { authClient, logger?, handleError? } and returns a Next.js route handler. Export it as the appropriate HTTP method (POST, GET).

    Factory HTTP Zod Schema Description
    createLoginRequestHandler POST { email, password } Sign in with email/password, sets session cookies
    createSignupRequestHandler POST { email, password } Register a new account
    createGoogleLoginRequestHandler POST (none) Initiate Google OAuth, returns { url }
    createOAuthCallbackRequestHandler GET { code } (query) Exchange OAuth code for session, redirects to app
    createForgotPasswordRequestHandler POST { email } Send password reset email
    createResetPasswordRequestHandler GET { code } (query) Exchange reset code, sets cookies with 10-min expiry, redirects
    createUpdatePasswordRequestHandler POST { password } Update password during reset flow, clears session
    createChangePasswordRequestHandler POST { password } Change password for authenticated user
    createVerifyOTPRequestHandler POST { email, token, type } Verify OTP code (e.g. recovery), sets session

    All factories validate input with Zod, call the appropriate BaseAuthClient method, manage cookies via the session layer, and return a NextResponse.

    All hooks make axios requests to /api/auth/* routes and return Safe<T> results.

    Hook Description
    useLoginWithEmailAndPassword { login, isLoggingIn, error }
    useLoginWithGoogle { loginWithGoogle, isLoggingIn, error }
    useSignupWithEmailAndPassword { signup, isSigningUp, error }
    useForgotPassword { forgotPassword, isLoading, error }
    useVerifyOTP { verifyOTP, isLoading, error }
    useUpdatePassword { updatePassword, isLoading, error }
    useChangePassword { changePassword, isLoading, error }
    "use client";

    import { useLoginWithEmailAndPassword } from "@repo/auth-next/hooks";
    import { useRouter } from "next/navigation";

    function LoginForm() {
    const router = useRouter();
    const { login, isLoggingIn, error } = useLoginWithEmailAndPassword();

    const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    const result = await login({
    email: formData.get("email") as string,
    password: formData.get("password") as string,
    });

    if (result.data) {
    router.push("/");
    }
    };

    return (
    <form onSubmit={handleSubmit}>
    <input name="email" type="email" required />
    <input name="password" type="password" required />
    {error && <p>{error}</p>}
    <button type="submit" disabled={isLoggingIn}>
    {isLoggingIn ? "Signing in..." : "Sign in"}
    </button>
    </form>
    );
    }

    Use requireUser() in server-side layouts or pages. If the user is not authenticated, they are automatically redirected to /login.

    // apps/web/app/(app)/layout.tsx
    import { requireUser } from "@repo/auth-next";

    export default async function AppLayout({ children }: React.PropsWithChildren) {
    const user = await requireUser();

    return (
    <UserProvider user={user}>
    {children}
    </UserProvider>
    );
    }

    For custom redirect targets:

    const user = await requireUser({ redirectTo: "/signin" });
    

    The package exports Vitest mocks for all hooks via @repo/auth-next/tests:

    import "@repo/auth-next/tests";
    import { mockUseLoginWithEmailAndPassword } from "@repo/auth-next/tests";

    // Mocks are automatically wired up via vi.mock
    mockUseLoginWithEmailAndPassword.mockReturnValue({
    login: vi.fn(),
    isLoggingIn: false,
    error: null,
    });
    Script Description
    lint Run Biome checks
    check-types Typecheck with tsc --noEmit
    test Run Vitest with coverage
    test:watch Run Vitest in watch mode

    Modules

    api
    clients/supabase-next
    hooks
    lib/cookies
    lib/session
    lib/session.test
    lib/user-state
    lib/user-state.test