BuildBaseBuildBase

Sign In & Sign Out

Handle sign-in, sign-out, and user state in your React app.

useSaaSAuth() is the primary hook for authentication. Call signIn() to redirect to the BuildBase auth portal, signOut() to end the session.

Step by step, with screenshots: Set up email and password and Set up magic links and email codes.

signOut() revokes the session on the server and then clears local state, so the session id cannot be reused afterwards. It returns a promise, and local state is cleared even if the revoke fails - a user who pressed sign out is signed out in the browser either way.

Note

Server-side revocation landed in SDK 0.0.70. On 0.0.68 and earlier signOut() only cleared local state, and the session stayed valid until it expired. Upgrade if you rely on signing out to end a session.

import { useSaaSAuth } from '@buildbase/sdk/react';

function App() {
  const { user, isAuthenticated, signIn, signOut } = useSaaSAuth();

  if (!isAuthenticated) {
    return <button onClick={() => signIn()}>Sign In</button>;
  }

  return (
    <div>
      <p>Welcome, {user?.name}</p>
      <button onClick={() => signOut()}>Sign Out</button>
    </div>
  );
}

useSaaSAuth() returns

PropertyTypeDescription
userAuthUser or undefinedThe authenticated user: id, name, email, emailVerified, role, org, clientId, image?
sessionAuthSession or nullThe current session: user, sessionId, expires
status'loading' | 'redirecting' | 'authenticated' | 'unauthenticated' | 'authenticating'Session resolution state
isLoadingbooleanTrue while session is resolving
isRedirectingbooleanTrue while redirecting to the auth portal
isAuthenticatedbooleanTrue when signed in
signIn(returnUrl?)functionRedirect to sign-in portal
signOut(options?)functionRevoke the session server-side, then clear local state. { everywhere: true } ends every session
requireReverification(options?)functionRe-confirm before a sensitive change. See Confirm it's you
openWorkspaceSettings(section?)functionOpen workspace settings panel
openCreditStore()functionOpen the credit store
openPlanPicker()functionOpen the plan picker

user also carries mfaEnabled while the person has a second step on, phone and phoneVerified when they have a phone number, username, externalId for someone imported from another system, the owner's custom claims as claims, canChangeEmail, isAnonymous for a guest and deleteAfter while a deletion is scheduled. session carries authTime and amr, how recently and how the person signed in, and impersonation while an app owner is signed in as them.

Signing out everywhere

signOut() ends the session it was called from. Pass { everywhere: true } to end every session that user has, on every device - the usual response to a lost laptop or a password someone else may have seen. It also ends the access of every agent the person connected, and each asks for their consent again.

function SignOutButtons() {
  const { signOut } = useSaaSAuth();

  return (
    <>
      <button onClick={() => signOut()}>Sign out</button>
      <button onClick={() => signOut({ everywhere: true })}>
        Sign out everywhere
      </button>
    </>
  );
}

Three different jobs, so three different calls:

To endCallWhere
This sessionsignOut()useSaaSAuth()
Every session for this usersignOut({ everywhere: true })useSaaSAuth()
One other sessionrevoke(id)useSessions(), see sessions and tokens
Every session on a devicesignOut(deviceId)useDevices(), same page

Outside React, AuthApi.logout(sessionId?, { all? }) does the same thing.

Conditional rendering

import { WhenAuthenticated, WhenUnauthenticated } from '@buildbase/sdk/react';

function Page() {
  return (
    <>
      <WhenAuthenticated>
        <Dashboard />
      </WhenAuthenticated>
      <WhenUnauthenticated>
        <LoginPage />
      </WhenUnauthenticated>
    </>
  );
}

Handle loading state

The SDK checks the session on first render. Always handle loading:

const { isLoading, isAuthenticated } = useSaaSAuth();

if (isLoading) return <Spinner />;
if (!isAuthenticated) return <SignInPage />;
return <Dashboard />;

Error handling

Auth errors surface through the handleAuthentication callback in your provider config. If the callback throws or the auth code exchange fails, the SDK does not update the session - the user stays unauthenticated.

// In your SaaSOSProvider auth config
auth={{
  clientId: 'your-client-id',
  redirectUrl: '/callback',
  callbacks: {
    getSession: async () => localStorage.getItem('sessionId'),
    onSignOut: async () => localStorage.removeItem('sessionId'),
    handleAuthentication: async (code) => {
      try {
        const res = await fetch('/api/auth/verify', {
          method: 'POST',
          body: JSON.stringify({ code }),
        });
        if (!res.ok) throw new Error('Auth failed');
        const data = await res.json();
        return { sessionId: data.sessionId };
      } catch (error) {
        // Show a toast, redirect to an error page, or log
        console.error('Authentication failed:', error);
        throw error; // Re-throw so the SDK knows auth failed
      }
    },
  },
}}

Common failure scenarios:

  • Network error - the callback fetch fails. Show a retry prompt.
  • Invalid code - the auth code expired or was already used. The user needs to sign in again.
  • Server error - your backend returned a non-200 response. Check server logs.

Preserve return URL

Pass a URL to signIn() to redirect the user back after authentication:

<button onClick={() => signIn('/settings')}>Sign in to view settings</button>

Next Steps