
Security News
Feross on TBPN: Socket's Series C and the State of Software Supply Chain Security
Feross Aboukhadijeh joins TBPN to discuss Socket's $60M Series C, 500%+ ARR growth, AI's impact on open source, and the rise in supply chain attacks.
@workos/authkit-tanstack-react-start
Advanced tools
The WorkOS library for TanStack React Start provides convenient helpers for authentication and session management using WorkOS & AuthKit with TanStack React Start.
Authentication and session management for TanStack Start applications using WorkOS AuthKit.
[!NOTE] This library is designed for TanStack Start v1.0+. TanStack Start is currently in beta - expect some API changes as the framework evolves.
npm install @workos/authkit-tanstack-react-start
pnpm add @workos/authkit-tanstack-react-start
Create a .env file in your project root with the following required variables:
WORKOS_CLIENT_ID="client_..." # Get from WorkOS dashboard
WORKOS_API_KEY="sk_test_..." # Get from WorkOS dashboard
WORKOS_REDIRECT_URI="http://localhost:3000/api/auth/callback"
WORKOS_COOKIE_PASSWORD="..." # Min 32 characters
Generate a secure cookie password (32+ characters):
openssl rand -base64 24
| Variable | Default | Description |
|---|---|---|
WORKOS_COOKIE_MAX_AGE | 34560000 (400 days) | Cookie lifetime in seconds |
WORKOS_COOKIE_NAME | wos-session | Session cookie name |
WORKOS_COOKIE_DOMAIN | None | Cookie domain (for multi-domain sessions) |
WORKOS_COOKIE_SAMESITE | lax | SameSite attribute (lax, strict, none) |
WORKOS_API_HOSTNAME | api.workos.com | WorkOS API hostname |
Create or update src/start.ts:
import { createStart } from '@tanstack/react-start';
import { authkitMiddleware } from '@workos/authkit-tanstack-react-start';
export const startInstance = createStart(() => ({
requestMiddleware: [authkitMiddleware()],
}));
Create src/routes/api/auth/callback.tsx:
import { createFileRoute } from '@tanstack/react-router';
import { handleCallbackRoute } from '@workos/authkit-tanstack-react-start';
export const Route = createFileRoute('/api/auth/callback')({
server: {
handlers: {
GET: handleCallbackRoute(),
},
},
});
Make sure this matches your WORKOS_REDIRECT_URI environment variable.
If you want to use useAuth() or other client hooks, wrap your app with AuthKitProvider in src/routes/__root.tsx:
import { AuthKitProvider } from '@workos/authkit-tanstack-react-start/client';
import { Outlet, createRootRoute } from '@tanstack/react-router';
export const Route = createRootRoute({
component: RootComponent,
});
function RootComponent() {
return (
<AuthKitProvider>
<Outlet />
</AuthKitProvider>
);
}
If you're only using server-side authentication (getAuth() in loaders), you can skip this step.
http://localhost:3000/api/auth/callbackUse getAuth() in route loaders or server functions to access the current session:
import { createFileRoute, redirect } from '@tanstack/react-router';
import { getAuth, getSignInUrl } from '@workos/authkit-tanstack-react-start';
export const Route = createFileRoute('/dashboard')({
loader: async () => {
const { user } = await getAuth();
if (!user) {
const signInUrl = await getSignInUrl();
throw redirect({ href: signInUrl });
}
return { user };
},
component: DashboardPage,
});
function DashboardPage() {
const { user } = Route.useLoaderData();
return <div>Welcome, {user.firstName}!</div>;
}
For client components that need reactive auth state, use the useAuth() hook:
'use client'; // Not actually needed in TanStack Start, but shows intent
import { useAuth } from '@workos/authkit-tanstack-react-start/client';
function ProfileButton() {
const { user, loading, signOut } = useAuth();
if (loading) return <div>Loading...</div>;
if (!user) return <a href="/signin">Sign In</a>;
return (
<div>
<span>{user.email}</span>
<button onClick={() => signOut()}>Sign Out</button>
</div>
);
}
Server-side (in route loader):
import { signOut } from '@workos/authkit-tanstack-react-start';
export const Route = createFileRoute('/logout')({
loader: async () => {
await signOut(); // Redirects to WorkOS logout, then back to '/'
},
});
Client-side (from useAuth hook):
const { signOut } = useAuth();
await signOut({ returnTo: '/goodbye' });
Switch the active organization for multi-org users:
Server-side:
import { switchToOrganization } from '@workos/authkit-tanstack-react-start';
// In a server function or loader
const auth = await switchToOrganization({
data: { organizationId: 'org_456' },
});
// Session now has org_456's role, permissions, etc.
Client-side:
const { switchToOrganization, organizationId } = useAuth();
await switchToOrganization('org_456');
// Auth state updates automatically
Use layout routes to protect multiple pages:
// src/routes/_authenticated.tsx
import { createFileRoute, redirect } from '@tanstack/react-router';
import { getAuth, getSignInUrl } from '@workos/authkit-tanstack-react-start';
export const Route = createFileRoute('/_authenticated')({
loader: async ({ location }) => {
const { user } = await getAuth();
if (!user) {
const signInUrl = await getSignInUrl({
data: { returnPathname: location.pathname },
});
throw redirect({ href: signInUrl });
}
return { user };
},
});
// Now all routes under _authenticated require auth:
// - _authenticated/dashboard.tsx
// - _authenticated/profile.tsx
// etc.
These functions can be called from route loaders, server functions, or server route handlers.
getAuth()Retrieves the current user session.
const { user } = await getAuth();
if (user) {
console.log(user.email);
console.log(user.firstName);
}
Returns: UserInfo | NoUserInfo
UserInfo fields:
user - The authenticated user objectsessionId - WorkOS session IDorganizationId - Active organization (if in org context)role - User's role in the organizationroles - Array of role stringspermissions - Array of permission stringsentitlements - Array of entitlement stringsfeatureFlags - Array of feature flag stringsimpersonator - Impersonator details (if being impersonated)accessToken - JWT access tokensignOut(options?)Signs out the current user and redirects to WorkOS logout.
await signOut();
await signOut({ data: { returnTo: '/goodbye' } });
Options:
returnTo - Path to redirect to after logout (default: /)switchToOrganization(options)Switches to a different organization and refreshes the session with new claims.
const auth = await switchToOrganization({
data: {
organizationId: 'org_123',
returnTo: '/dashboard', // optional
},
});
Options:
organizationId - The organization ID to switch to (required)returnTo - Path to redirect to if auth failsReturns: UserInfo with updated organization claims
getSignInUrl(options?)Generates a sign-in URL for redirecting to AuthKit.
// Basic usage
const url = await getSignInUrl();
// With return path
const url = await getSignInUrl({
data: { returnPathname: '/dashboard' },
});
Options:
returnPathname - Path to return to after sign-ingetSignUpUrl(options?)Generates a sign-up URL for redirecting to AuthKit.
const url = await getSignUpUrl();
const url = await getSignUpUrl({
data: { returnPathname: '/onboarding' },
});
Options:
returnPathname - Path to return to after sign-upgetAuthorizationUrl(options)Advanced: Generate a custom authorization URL with full control.
const url = await getAuthorizationUrl({
data: {
screenHint: 'sign-in',
returnPathname: '/dashboard',
redirectUri: 'https://example.com/callback', // override default
},
});
Options:
screenHint - 'sign-in' or 'sign-up'returnPathname - Return path after authenticationredirectUri - Override the default redirect URIhandleCallbackRouteHandles the OAuth callback from WorkOS. Use this in your callback route.
Basic usage:
import { createFileRoute } from '@tanstack/react-router';
import { handleCallbackRoute } from '@workos/authkit-tanstack-react-start';
export const Route = createFileRoute('/api/auth/callback')({
server: {
handlers: {
GET: handleCallbackRoute(),
},
},
});
With hooks for custom logic:
export const Route = createFileRoute('/api/auth/callback')({
server: {
handlers: {
GET: handleCallbackRoute({
onSuccess: async ({ user, authenticationMethod }) => {
// Create user record in your database
await db.users.upsert({ id: user.id, email: user.email });
// Track analytics
analytics.track('User Signed In', { method: authenticationMethod });
},
onError: ({ error, request }) => {
// Custom error handling
console.error('Auth failed:', error);
return new Response(JSON.stringify({ error: 'Authentication failed' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
},
}),
},
},
});
Options:
onSuccess?: (data) => Promise<void> - Called after successful authentication with user data, tokens, and authentication methodonError?: ({ error, request }) => Response - Custom error handler that returns a ResponsereturnPathname?: string - Override the redirect path after authentication (defaults to state or /)Available from @workos/authkit-tanstack-react-start/client. Requires <AuthKitProvider> wrapper.
useAuth(options?)Access authentication state and methods in client components.
import { useAuth } from '@workos/authkit-tanstack-react-start/client';
function MyComponent() {
const { user, loading, signOut } = useAuth();
if (loading) return <div>Loading...</div>;
if (!user) return <div>Not signed in</div>;
return (
<div>
<p>{user.email}</p>
<button onClick={() => signOut()}>Sign Out</button>
</div>
);
}
Options:
ensureSignedIn?: boolean - If true, automatically triggers sign-in flow for unauthenticated usersReturns: AuthContextType with:
user - Current user or nullloading - Loading statesessionId, organizationId, role, roles, permissions, entitlements, featureFlags, impersonatorgetAuth() - Refresh auth staterefreshAuth(options) - Refresh session with optional org switchsignOut(options) - Sign outswitchToOrganization(orgId) - Switch organizationsuseAccessToken()Manage access tokens with automatic refresh.
import { useAccessToken } from '@workos/authkit-tanstack-react-start/client';
function ApiCaller() {
const { accessToken, loading, getAccessToken } = useAccessToken();
const callApi = async () => {
const token = await getAccessToken(); // Always fresh
const response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});
};
return <button onClick={callApi}>Fetch Data</button>;
}
Returns:
accessToken - Current token (may be stale)loading - Loading stateerror - Last error or nullrefresh() - Manually refresh tokengetAccessToken() - Get guaranteed fresh tokenuseTokenClaims()Parse and decode JWT claims from the access token.
import { useTokenClaims } from '@workos/authkit-tanstack-react-start/client';
function ClaimsDisplay() {
const claims = useTokenClaims();
if (!claims) return null;
return (
<div>
<p>Session ID: {claims.sid}</p>
<p>Organization: {claims.org_id}</p>
<p>Role: {claims.role}</p>
</div>
);
}
authkitMiddleware()Processes authentication on every request. Validates tokens, refreshes sessions, and provides auth context to server functions.
Already shown in setup, but can be imported separately if needed.
This library is fully typed. Common types:
import type {
User,
Session,
UserInfo,
NoUserInfo,
Impersonator
} from '@workos/authkit-tanstack-react-start';
// User object from WorkOS
const user: User = {
id: string;
email: string;
firstName: string | null;
lastName: string | null;
emailVerified: boolean;
profilePictureUrl: string | null;
// ... more fields
};
// Auth result from getAuth()
const auth: UserInfo | NoUserInfo = await getAuth();
Route loaders get full type inference:
export const Route = createFileRoute('/profile')({
loader: async () => {
const { user } = await getAuth();
return { user }; // Fully typed
},
component: ProfilePage,
});
function ProfilePage() {
const { user } = Route.useLoaderData(); // user is typed!
}
getAuth() - retrieves auth from middleware contextgetAuth() in loaders - no provider neededuseAuth(), useAccessToken(), etc.// Get sign-in URL in loader
export const Route = createFileRoute('/')({
loader: async () => {
const { user } = await getAuth();
const signInUrl = await getSignInUrl();
return { user, signInUrl };
},
component: HomePage,
});
function HomePage() {
const { user, signInUrl } = Route.useLoaderData();
if (!user) {
return <a href={signInUrl}>Sign In with AuthKit</a>;
}
return <div>Welcome, {user.firstName}!</div>;
}
// src/routes/_authenticated.tsx
import { createFileRoute, redirect } from '@tanstack/react-router';
import { getAuth, getSignInUrl } from '@workos/authkit-tanstack-react-start';
export const Route = createFileRoute('/_authenticated')({
loader: async ({ location }) => {
const { user } = await getAuth();
if (!user) {
const signInUrl = await getSignInUrl({
data: { returnPathname: location.pathname },
});
throw redirect({ href: signInUrl });
}
return { user };
},
});
// All child routes require authentication:
// - _authenticated/dashboard.tsx
// - _authenticated/settings.tsx
import { useAuth } from '@workos/authkit-tanstack-react-start/client';
function OrgSwitcher() {
const { organizationId, switchToOrganization } = useAuth();
return (
<select
value={organizationId || ''}
onChange={(e) => switchToOrganization(e.target.value)}
>
<option value="org_123">Acme Corp</option>
<option value="org_456">Other Company</option>
</select>
);
}
Loader (server-side):
loader: async () => {
const { user, organizationId, role } = await getAuth();
return { user, organizationId, role };
};
Component (from loader data):
function MyPage() {
const { user } = Route.useLoaderData();
// ...
}
Client hook (reactive):
function MyClientComponent() {
const { user, loading } = useAuth();
// Updates on session changes
}
You forgot to add authkitMiddleware() to src/start.ts. See step 1 in setup.
You're calling useAuth() but haven't wrapped your app with <AuthKitProvider>. See step 3 in setup.
If you don't need client hooks, use getAuth() in loaders instead.
The middleware validates configuration on first request. If you see errors about missing variables:
.env file existsWORKOS_COOKIE_PASSWORD is 32+ charactersMake sure you're importing from the right path:
// Server functions
import { getAuth, signOut } from '@workos/authkit-tanstack-react-start';
// Client hooks
import { useAuth } from '@workos/authkit-tanstack-react-start/client';
Don't import client hooks in server code or vice versa.
You're trying to call a server function from a beforeLoad hook or client component.
Wrong:
beforeLoad: async () => {
const { user } = await getAuth(); // ❌ Runs on client during hydration
};
Right:
loader: async () => {
const { user } = await getAuth(); // ✅ Server-only during SSR
};
Use useAuth() client hook for client components, or move logic to a loader.
Check the /example directory for a complete working application demonstrating:
Run it locally:
cd example
pnpm install
pnpm dev
MIT
FAQs
The WorkOS library for TanStack React Start provides convenient helpers for authentication and session management using WorkOS & AuthKit with TanStack React Start.
The npm package @workos/authkit-tanstack-react-start receives a total of 19,743 weekly downloads. As such, @workos/authkit-tanstack-react-start popularity was classified as popular.
We found that @workos/authkit-tanstack-react-start demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Feross Aboukhadijeh joins TBPN to discuss Socket's $60M Series C, 500%+ ARR growth, AI's impact on open source, and the rise in supply chain attacks.

Security News
OSV withdrew 157 OSV malware reports after automated false positives incorrectly flagged trusted npm and PyPI packages, sending bad records into tools that rely on OSV data.

Research
/Security News
TrapDoor crypto stealer hits 36 malicious packages across npm, PyPI, and Crates.io, targeting crypto, DeFi, AI, and security developers.