Basic Usage

Faire Auth provides built-in authentication support for:

  • Email and password
  • Social provider (Google, GitHub, Apple, and more)

But also can easily be extended using plugins, such as: username, magic link, passkey, two factor, and more.

Email & Password

To enable email and password authentication:

auth.ts
import { faireAuth, defineOptions } from "faire-auth"

const cfg = defineOptions({
    baseURL: "http://localhost:3000",
    emailAndPassword: {    
        enabled: true
    } 
})

export const auth = faireAuth(cfg)
export const App = auth.$Infer.App(cfg)

Sign Up

To sign up a user, call signUp.email.$post on the client with the user's information:

sign-up.ts
import { authClient } from "@/lib/auth-client"; //import the auth client

const { data, error } = await authClient.signUp.email.$post(
    {
        json: {
            email, // user email address
            password, // user password -> min 8 characters by default
            name, // user display name
            image, // User image URL (optional)
            callbackURL: "/dashboard" // A URL to redirect to after the user verifies their email (optional)
        },
    },
    {
        fetchOptions: {
            onRequest: (ctx) => {
                //show loading
            },
            onSuccess: (ctx) => {
                //redirect to the dashboard or sign in page
            },
            onError: (ctx) => {
                // display the error message
                alert(ctx.error.message);
            },
        },
    },
);

By default, the users are automatically signed in after they successfully sign up. To disable this behavior you can set autoSignIn to false.

auth.ts
import { faireAuth, defineOptions } from "faire-auth"

const cfg = defineOptions({
    baseURL: "http://localhost:3000",
    emailAndPassword: {
    	enabled: true,
    	autoSignIn: false //defaults to true
    },
})

export const auth = faireAuth(cfg)

Sign In

To sign a user in, you can use signIn.email.$post on the client:

sign-in.ts
const { data, error } = await authClient.signIn.email.$post(
    {
        json: {
            email,
            password,
            callbackURL: "/dashboard", // optional
            rememberMe: false // remember session after browser close, default: true
        },
    },
    {
        fetchOptions: {
            // callbacks
        },
    },
);

Always invoke client methods from the client side. Don't call them from the server.

Server-Side Authentication

To authenticate a user on the server, you can use the auth.api methods. The server API uses json: for request bodies:

server.ts
import { auth } from "./auth"; // path to your Faire Auth server instance

const response = await auth.api.signInEmail(
    { json: { email, password } },
    { asResponse: true }, // returns a response object instead of data
);

If the server cannot return a response object, you'll need to manually parse and set cookies. But for frameworks like Next.js we provide a plugin to handle this automatically

Social Sign-On

Faire Auth supports multiple social providers, including Google, GitHub, Apple, Discord, and more. To use a social provider, you need to configure the ones you need in the socialProviders option on your auth object.

auth.ts
import { faireAuth, defineOptions } from "faire-auth";

const cfg = defineOptions({
    baseURL: "http://localhost:3000",
    socialProviders: { 
        github: { 
            clientId: process.env.GITHUB_CLIENT_ID!, 
            clientSecret: process.env.GITHUB_CLIENT_SECRET!, 
        } 
    }, 
})

export const auth = faireAuth(cfg)

Sign in with social providers

To sign in using a social provider you need to call signIn.social.$post. It takes an object with the following properties:

sign-in.ts
import { authClient } from "@/lib/auth-client"; //import the auth client

await authClient.signIn.social.$post({
    json: {
        provider: "github",
        callbackURL: "/dashboard", // A URL to redirect after the user authenticates with the provider
        errorCallbackURL: "/error", // A URL to redirect if an error occurs
        newUserCallbackURL: "/welcome", // A URL to redirect if the user is newly registered
        disableRedirect: true, // disable the automatic redirect to the provider (default: false)
    },
});

You can also authenticate using idToken or accessToken from the social provider instead of redirecting the user to the provider's site. See social providers documentation for more details.

Signout

To signout a user, you can use signOut.$post on the client:

user-card.tsx
await authClient.signOut.$post();

you can pass fetchOptions to redirect onSuccess

user-card.tsx
await authClient.signOut.$post(
    {},
    {
        fetchOptions: {
            onSuccess: () => {
                router.push("/login"); // redirect to login page
            },
        },
    },
);

Session

Once a user is signed in, you'll want to access the user session. Faire Auth allows you to easily access the session data from both the server and client sides.

Client Side

Use Session

Faire Auth provides a useSession hook to easily access session data on the client side. This hook is implemented using nanostore and has support for each supported framework and vanilla client, ensuring that any changes to the session (such as signing out) are immediately reflected in your UI.

user.tsx
import { authClient } from "@/lib/auth-client" // import the auth client

export function User(){

    const { 
        data: session, 
        isPending, //loading state
        error, //error object
        refetch //refetch the session
    } = authClient.useSession() 

    return (
        //...
    )
}
index.vue
<script setup lang="ts">
import { authClient } from "~/lib/auth-client"

const session = authClient.useSession() 
</script>

<template>
    <div>
        <div>
            <pre>{{ session.data }}</pre>
            <button v-if="session.data" @click="authClient.signOut.$post()">
                Sign out
            </button>
        </div>
    </div>
</template>
user.svelte
<script lang="ts">
import { authClient } from "$lib/auth-client"; 

const session = authClient.useSession(); 
</script>
<p>
    {$session.data?.user.email}
</p>
user.ts
import { authClient } from "~/lib/auth-client"; //import the auth client

authClient.useSession.subscribe((value)=>{
    //do something with the session //
})
user.tsx
import { authClient } from "~/lib/auth-client"; 

export default function Home() {
    const session = authClient.useSession() 
    return (
        <pre>{JSON.stringify(session(), null, 2)}</pre>
    );
}

Get Session

If you prefer not to use the hook, you can use getSession.$get:

user.tsx
import { authClient } from "@/lib/auth-client" // import the auth client

const { data: session, error } = await authClient.getSession.$get({ query: {} })

You can also use it with client-side data-fetching libraries like TanStack Query.

Server Side

The server provides a getSession endpoint that you can use to access the session data. It requires request headers to be passed.

Example: Using some popular frameworks

server.ts
import { auth } from "./auth"; // path to your Faire Auth server instance
import { headers } from "next/headers";

const session = await auth.api.getSession(
    { query: {} },
    { headers: await headers() },
)
route.ts
import { auth } from "lib/auth"; // path to your Faire Auth server instance

export async function loader({ request }: LoaderFunctionArgs) {
    const session = await auth.api.getSession(
        { query: {} },
        { headers: request.headers },
    )

    return json({ session })
}
index.astro
---
import { auth } from "./auth";

const session = await auth.api.getSession(
    { query: {} },
    { headers: Astro.request.headers },
);
---
<!-- Your Astro Template -->
+page.ts
import { auth } from "./auth";

export async function load({ request }) {
    const session = await auth.api.getSession(
        { query: {} },
        { headers: request.headers },
    )
    return {
        props: {
            session
        }
    }
}
index.ts
import { auth } from "./auth";

const app = new Hono();

app.get("/path", async (c) => {
    const session = await auth.api.getSession(
        { query: {} },
        { headers: c.req.raw.headers },
    )
});
server/session.ts
import { auth } from "~/utils/auth";

export default defineEventHandler((event) => {
    const session = await auth.api.getSession(
        { query: {} },
        { headers: event.headers },
    )
});
app/routes/api/index.ts
import { auth } from "./auth";
import { createAPIFileRoute } from "@tanstack/start/api";

export const APIRoute = createAPIFileRoute("/api/$")({
    GET: async ({ request }) => {
        const session = await auth.api.getSession({
            headers: request.headers
        })
    },
});

For more details check session-management documentation.

Using Plugins

One of the unique features of Faire Auth is a plugins ecosystem. It allows you to add complex auth related functionality with small lines of code.

Below is an example of how to add two factor authentication using two factor plugin.

Server Configuration

To add a plugin, you need to import the plugin and pass it to the plugins option of the auth instance. For example, to add two factor authentication, you can use the following code:

auth.ts
import { faireAuth, defineOptions } from "faire-auth"
import { twoFactor } from "faire-auth/plugins"

const cfg = defineOptions({
    baseURL: "http://localhost:3000",
    plugins: [ 
        twoFactor() 
    ] 
})

export const auth = faireAuth(cfg)
export const App = auth.$Infer.App(cfg)

now two factor related routes and method will be available on the server.

Migrate Database

After adding the plugin, you'll need to add the required tables to your database. You can do this by running the migrate command, or by using the generate command to create the schema and handle the migration manually.

generating the schema:

terminal
npx @faire-auth/cli generate

using the migrate command:

terminal
npx @faire-auth/cli migrate

If you prefer adding the schema manually, you can check the schema required on the two factor plugin documentation.

Client Configuration

Once we're done with the server, we need to add the plugin to the client. To do this, you need to import the plugin and pass it to the plugins option of the auth client. For example, to add two factor authentication, you can use the following code:

auth-client.ts
import { createAuthClient } from "faire-auth/client";
import { twoFactorClient } from "faire-auth/client/plugins"; 
import type { App } from "./auth"; 

const authClient = createAuthClient<typeof App>()({ 
    plugins: [ 
        twoFactorClient({ 
            twoFactorPage: "/two-factor" // the page to redirect if a user needs to verify 2nd factor
        }) 
    ] 
})

now two factor related methods will be available on the client.

profile.ts
import { authClient } from "./auth-client"

const enableTwoFactor = async() => {
    const data = await authClient.twoFactor.enable.$post({
        json: { password } // the user password is required
    }) // this will enable two factor
}

const disableTwoFactor = async() => {
    const data = await authClient.twoFactor.disable.$post({
        json: { password } // the user password is required
    }) // this will disable two factor
}

const signInWith2Factor = async() => {
    const data = await authClient.signIn.email.$post({
        json: { email, password }
    })
    //if the user has two factor enabled, it will redirect to the two factor page
}

const verifyTOTP = async() => {
    const data = await authClient.twoFactor.verifyTOTP.$post({
        json: {
            code: "123456", // the code entered by the user
            trustDevice: true // If trusted, the user won't need 2FA again on the same device
        }
    })
}

On this page