| title | Authentication |
|---|---|
| description | Set up OAuth2/OIDC authentication with Tailor Platform's Auth service using the AuthProvider component |
AppShell provides built-in OAuth2/OIDC authentication through the AuthProvider component, which integrates seamlessly with Tailor Platform's Auth service. The provider supports any IdP configured in your Tailor Platform application (built-in IdP, Google, Okta, Auth0, etc.).
First, create an auth client, then wrap your app with AuthProvider:
import {
createAuthClient,
AuthProvider,
AppShell,
SidebarLayout,
} from "@tailor-platform/app-shell";
const authClient = createAuthClient({
clientId: CLIENT_ID,
appUri: TAILOR_PLATFORM_URL,
});
const App = () => (
<AuthProvider client={authClient} autoLogin={true} guardComponent={() => <LoadingScreen />}>
<AppShell modules={modules}>
<SidebarLayout />
</AppShell>
</AuthProvider>
);Find the above values in Tailor Console:
-
Tailor Platform URL (
appUri): Your application's base URL- Obtained from the Application Overview screen in Tailor Platform
- Use the domain portion of the 'Accessing the API endpoint of this application' setting
- Example:
"https://xyz.erp.dev"(no/querysuffix needed)
-
Client ID (
clientId): Authentication client identifier- Found in Application > Auth screen in your Tailor Platform console
The above code will:
- Automatically redirect unauthenticated users to the login page (if
autoLoginis true) - Show the
guardComponentwhile loading or when unauthenticated - Handle token management and session persistence automatically
Use the useAuth hook to access authentication state and methods:
import { useAuth } from "@tailor-platform/app-shell";
const UserProfile = () => {
const { isReady, isAuthenticated, login, logout } = useAuth();
if (!isReady) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <button onClick={login}>Sign In</button>;
}
return <button onClick={logout}>Sign Out</button>;
};| Property | Type | Description |
|---|---|---|
isAuthenticated |
boolean |
Whether the user is currently authenticated |
isReady |
boolean |
Whether the initial authentication check has completed |
error |
string | null |
Error message if authentication failed |
login |
() => Promise<void> |
Initiates the login/redirect flow |
logout |
() => Promise<void> |
Clears tokens and ends the session |
checkAuthStatus |
() => Promise<AuthState> |
Re-checks auth status (always makes a network request) |
Use useAuthSuspense when you want React Suspense to handle the loading state:
import { Suspense } from "react";
import { useAuthSuspense } from "@tailor-platform/app-shell";
function App() {
return (
<AuthProvider client={authClient}>
<Suspense fallback={<div>Loading authentication...</div>}>
<ProtectedContent />
</Suspense>
</AuthProvider>
);
}
function ProtectedContent() {
// isReady is guaranteed to be true here — Suspense handles the loading state
const { isAuthenticated, login, logout } = useAuthSuspense();
if (!isAuthenticated) {
return <button onClick={login}>Log In</button>;
}
return <button onClick={logout}>Log Out</button>;
}Use createAuthClient to create an authentication client that you pass to AuthProvider. The client handles token management and provides an authenticated fetch method for use with GraphQL clients.
import { createAuthClient } from "@tailor-platform/app-shell";
const authClient = createAuthClient({
clientId: "your-client-id",
appUri: "https://xyz.erp.dev",
});Pass authClient.fetch directly to your GraphQL client (e.g., urql). It transparently handles DPoP proof generation and token refresh on every request:
import { createAuthClient, AuthProvider } from "@tailor-platform/app-shell";
import { createClient, Provider } from "urql";
const authClient = createAuthClient({
clientId: "your-client-id",
appUri: "https://xyz.erp.dev",
});
const urqlClient = createClient({
url: `${authClient.getAppUri()}/query`,
fetch: authClient.fetch,
});
function App() {
return (
<AuthProvider client={authClient} autoLogin={true}>
<Provider value={urqlClient}>
<YourAppComponents />
</Provider>
</AuthProvider>
);
}createAIGatewayClient reuses the same authenticated fetch path as the rest of AppShell. Requests go through authClient.fetch, so DPoP proof generation and token refresh stay in the auth layer.
import { createAuthClient, createAIGatewayClient, useAIChat } from "@tailor-platform/app-shell";
const authClient = createAuthClient({
clientId: "your-client-id",
appUri: "https://xyz.erp.dev",
});
const aiClient = createAIGatewayClient({
gatewayUri: "https://your-ai-gateway.example.com",
authClient,
});
function ChatScreen() {
const { messages, sendMessage, status, stop } = useAIChat({
client: aiClient,
model: "gpt-5-mini",
});
return (
<div>
{messages.map((message) => (
<div key={message.id}>
{message.role}: {message.content}
</div>
))}
<button onClick={() => void sendMessage("Hello")}>Send</button>
<button onClick={stop} disabled={status !== "submitted" && status !== "streaming"}>
Stop
</button>
</div>
);
}| Property | Type | Required | Description |
|---|---|---|---|
clientId |
string |
Yes | OAuth2 client ID from Tailor Platform console |
appUri |
string |
Yes | Your Tailor Platform application URL |
redirectUri |
string |
No | OAuth2 redirect URI (defaults to window.location.origin) |
| Method / Property | Type | Description |
|---|---|---|
getAppUri() |
() => string |
Returns the appUri used to create this client |
fetch |
typeof fetch |
Authenticated fetch with built-in DPoP proof generation and token refresh |
| Prop | Type | Required | Description |
|---|---|---|---|
client |
EnhancedAuthClient |
Yes | Auth client created with createAuthClient |
autoLogin |
boolean |
No | Automatically redirect unauthenticated users to login |
guardComponent |
() => React.ReactNode |
No | Rendered while loading or when not authenticated |
The authentication provider works seamlessly with AppShell's data layer, automatically handling:
- OAuth2 token management
- Authenticated fetch with DPoP proof generation
- Session persistence and token refresh
- Automatic redirects for protected routes (via
autoLogin)
OAuth callback parameters (code, state) are automatically cleaned from the URL after a successful login — no dedicated callback page is needed.