Authenticate with the JavaScript SDK
Browser apps built with @taruvi/sdk don't handle passwords. Users sign in on
TaruviBase's hosted sign-in page, and the SDK keeps the session that comes back.
This guide adds sign-in and sign-out to a browser app, shows how to check the
session, and covers server code.
It assumes the browser client from JavaScript SDK.
How browser sign-in works#
auth.login()sends the browser to/accounts/login/on your site, withredirect_toset to the page to return to.- The user signs in with a password or a configured SSO provider.
- TaruviBase redirects to
redirect_toand adds#session_token=…to the address, along with short-lived JWTs (access_token,refresh_token). - When your app constructs
Clienton that page, the SDK saves the token inlocalStorage, under the keysession_token, and removes the sign-in values, JWTs included, from the address bar. - Every later request sends the token in the
X-Session-Tokenheader.
Add sign-in and sign-out#
Create the Auth service from your client and gate the app on the session:
import {Auth} from '@taruvi/sdk';
import {taruvi} from './taruvi';
export const auth = new Auth(taruvi);
export async function requireSignedInUser(): Promise<void> {
if (await auth.isUserAuthenticated()) return;
// Leaves the page. The user comes back here after signing in.
auth.login();
}
export function signOut(): Promise<void> {
return auth.logout();
}
Import taruvi before you read the session on the page users return to.
Constructing the client is what captures the token from the address.
login(callbackUrl?)— Opens hosted sign-in. The user returns tocallbackUrl, an absolute URL on your app, or by default to the current origin and path, without the query string and fragment.signup(callbackUrl?)— Opens hosted sign-up, when your site allows self sign-up. The user returns the same way as withlogin().logout(callbackUrl?)— Clears the stored token, then opens/accounts/logout/to end the TaruviBase session. The defaultcallbackUrlis the app's origin.
login() and signup() only work in a browser; elsewhere, such as during
server-side rendering, they log an error and do nothing. Outside a browser,
logout() only clears the stored token. Call them from client code, such as an
event handler.
TaruviBase ends the session and returns the user to callbackUrl only when it
accepts that address as a redirect target. Otherwise it shows its own sign-out
page, and the TaruviBase session stays active until the user confirms there,
so the next login() signs them straight back in. Test sign-out from each
domain your app runs on.
Use a separate sign-in host#
The hosted pages live on apiUrl by default. If your site serves them from
another host, set deskUrl on the client. login(), signup(), and
logout() all use it:
const taruvi = new Client({
apiUrl: 'https://YOUR_SITE_HOST',
deskUrl: 'https://YOUR_SIGN_IN_HOST',
appSlug: 'APP_SLUG',
});
Check and manage the session#
These methods read the stored session without a request:
hasToken()—truewhen a token is stored. It doesn't prove the token is still valid.getSessionToken()— The stored token, ornull.
These ask TaruviBase:
isUserAuthenticated()— Resolvesfalsewhen there is no token or TaruviBase rejects it, withGET /_allauth/app/v1/auth/session.validateSession()— The same request, but rejects withAuthErrorwhen the session is missing, expired, or ended.getCurrentUser()— The user envelope fromGET /api/users/me/, ornullwhen there is no token or the request fails.
And these change it:
handleRedirect(url?)— Captures#session_tokenfrom the address, stores it, and returns it, ornull. Use it withdetectSessionInUrl: false.setSession(token)— Stores a session token your app got elsewhere.clearSession()— Forgets the stored token without redirecting.
Use hasToken() to render quickly, then confirm with isUserAuthenticated()
before you show private data. getCurrentUser() logs and swallows its own
errors; call validateSession() when you need the failure.
Where the session lives#
- In a browser, the token is in
localStorageundersession_token, shared by every tab on the origin. It's cleared whenlogout()runs or TaruviBase answers401,410, or419. - In Node.js and React Native, it's in memory, on the
Clientinstance you passedtokento. It's cleared when the client is discarded or TaruviBase answers401,410, or419.
Any script running on your origin can read localStorage. A cross-site
scripting bug therefore exposes the user's TaruviBase session. Set a strict
Content Security Policy, avoid injecting untrusted HTML, and keep third-party
scripts to a minimum.
Call TaruviBase from a server#
Server code acts either as a signed-in user or, with an API key, as the key's creator.
- As the signed-in user
- With an API key
Send the browser's session token to your own backend, for example in a request header of your choosing:
import {auth} from './auth';
export async function fetchTasks() {
const response = await fetch('/api/tasks', {
headers: {'X-Taruvi-Session': auth.getSessionToken() ?? ''},
});
return response.json();
}
On the server, create a client for that request:
import {Client, Database} from '@taruvi/sdk';
export async function listTasksFor(sessionToken: string) {
const taruvi = new Client({
apiUrl: process.env.TARUVI_SITE_URL!,
appSlug: process.env.TARUVI_APP_SLUG!,
token: sessionToken,
});
return new Database(taruvi).from('tasks').pageSize(20).execute();
}
TaruviBase applies that user's roles and access policies to every request. Treat the token like a password: send it only over HTTPS and don't log it.
Set authMode: 'apiKey' for jobs and services that don't act for a signed-in
user, and keep this client in server-only code:
import {Client} from '@taruvi/sdk';
export const taruviAdmin = new Client({
apiUrl: process.env.TARUVI_SITE_URL!,
appSlug: process.env.TARUVI_APP_SLUG!,
authMode: 'apiKey',
apiKey: process.env.TARUVI_API_KEY!,
});
The SDK sends the key as Authorization: Api-Key … and never sends a session
token alongside it. The constructor throws if it runs in a browser or React
Native. In Next.js, also add import 'server-only'; at the top of this module,
so the build fails if a client component imports it.
An API key is a personal access token that acts as the member of your organization who created it. That member has organization access, so requests made with the key skip app roles and Database row policies. Use keys only for trusted server jobs; to act for a user, forward their session token instead. Generate a key with Generate API Key on Settings → Connect, or under Settings → API Tokens; see Issue API tokens.
Anyone holding an API key can act as the user who created it. Never put
TARUVI_API_KEY in a public build variable such as VITE_*, NEXT_PUBLIC_*,
or EXPO_PUBLIC_*.
Server-rendered apps (Next.js)#
A framework that renders on the server, such as Next.js, needs two changes: it captures the sign-in redirect after hydration, and it hands the session to the server in a cookie so server components can act as the user.
Create the browser client with detectSessionInUrl: false, so creating it
doesn't touch the address:
import {Auth, Client} from '@taruvi/sdk';
export const taruvi = new Client({
apiUrl: process.env.NEXT_PUBLIC_TARUVI_SITE_URL!,
appSlug: process.env.NEXT_PUBLIC_TARUVI_APP_SLUG!,
detectSessionInUrl: false,
});
export const auth = new Auth(taruvi);
Capture the session in a client component rendered on the page users return to, and send it to a route handler that stores it in an httpOnly cookie:
'use client';
import {useRouter} from 'next/navigation';
import {useEffect, useState} from 'react';
import {auth} from '../lib/taruvi-browser';
export function SessionCapture() {
const router = useRouter();
const [failed, setFailed] = useState(false);
useEffect(() => {
const sessionToken = auth.handleRedirect();
if (!sessionToken) return;
fetch('/api/session', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({sessionToken}),
})
.then((response) => {
if (!response.ok) throw new Error(`Session route answered ${response.status}`);
// Re-render the Server Components, which now see the session cookie.
router.refresh();
})
.catch(() => setFailed(true));
}, [router]);
if (!failed) return null;
return (
<p role="alert">
Sign-in couldn't be completed.{' '}
<button type="button" onClick={() => auth.login()}>
Try again
</button>
</p>
);
}
import {cookies} from 'next/headers';
export async function POST(request: Request) {
// Only accept the token from your own pages.
if (request.headers.get('origin') !== new URL(request.url).origin) {
return new Response(null, {status: 403});
}
const {sessionToken} = (await request.json()) as {sessionToken?: string};
if (!sessionToken) return new Response(null, {status: 400});
(await cookies()).set('taruvi_session', sessionToken, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
});
return new Response(null, {status: 204});
}
export async function DELETE() {
(await cookies()).delete('taruvi_session');
return new Response(null, {status: 204});
}
Server components and route handlers then create a client for the request:
import 'server-only';
import {cookies} from 'next/headers';
import {Client} from '@taruvi/sdk';
export async function taruviForRequest(): Promise<Client> {
return new Client({
apiUrl: process.env.TARUVI_SITE_URL!,
appSlug: process.env.TARUVI_APP_SLUG!,
token: (await cookies()).get('taruvi_session')?.value,
});
}
On sign-out, call DELETE /api/session before auth.logout(). When your app
already has the token from somewhere else, auth.setSession(token) stores it,
and auth.clearSession() forgets it without redirecting.
Allow your app's address#
Browsers only let your app call TaruviBase from addresses TaruviBase allows for cross-origin requests. By default these are:
https://*.taruvi.app,https://*.taruvi.cloud, andhttps://*.taruvi.spacehttp://localhost:3000andhttp://localhost:5173, for development
From any other address, every request fails with NetworkError and a
statusCode of 0, and the browser console reports a CORS error.
Contact TaruviBase support to allow your own
domain. Server code isn't affected.
Troubleshooting#
The user signs in but the app still shows them signed out. The page they
return to must construct Client before it checks the session. Import your
client module early on that page.
Requests start failing with AuthError after a while. The session expired
or was ended (401, 410, or 419), and the SDK has already cleared it. Call
auth.login() again.
Every request fails with NetworkError after you deploy. The app's new
address isn't allowed for cross-origin requests. See
Allow your app's address.
login() can only be called in browser environment appears in the console.
The call ran during server-side rendering. Move it into client-only code.
Next steps#
- Method reference — every
Authmethod and option. - Hosted sign-in and recovery — invitations and account recovery.
- Access policies — what a signed-in user is allowed to do.