Skip to main content

React SDK

React SDK for Guardhouse/OIDC authentication in React 18+ applications.

Install

npm install @guardhouse/react

Package: @guardhouse/react
Repository: github.com/legiosoft/guardhouse-sdk-js


Provider Setup

Wrap your app with GuardhouseProvider:

import { GuardhouseProvider } from "@guardhouse/react";

export function App() {
return (
<GuardhouseProvider
config={{
authority: "https://your-tenant.guardhouse.cloud",
clientId: "your-client-id",
redirectUri: window.location.origin,
audience: "https://api.example.com",
}}
>
<YourApp />
</GuardhouseProvider>
);
}

useAuth Hook

import { useAuth } from "@guardhouse/react";

export function LoginButton() {
const { isAuthenticated, isLoading, user, loginWithRedirect, logout } = useAuth();

if (isLoading) return <div>Loading...</div>;

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

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

API Reference

PropertyTypeDescription
isLoadingbooleanInitial auth check in progress
isAuthenticatedbooleanUser has valid session
userUser | nullOIDC user profile
errorstring | nullAuth error message
loginWithRedirect(options?)() => Promise<void>Start login flow
logout(options?)() => Promise<void>Start logout flow
getAccessToken()() => Promise<string | null>Get valid token (refreshes if expired)
getAccessTokenSilently()() => Promise<string | null>Silent token retrieval

Protected Routes

ProtectedRoute Component

import { ProtectedRoute } from "@guardhouse/react";
import { Routes, Route } from "react-router-dom";

<Routes>
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
</Routes>

Custom loading state:

<ProtectedRoute onRedirecting={() => <Spinner />}>
<Dashboard />
</ProtectedRoute>

withAuthenticationRequired HOC

import { withAuthenticationRequired } from "@guardhouse/react";

function SettingsPage() {
return <div>Protected Settings</div>;
}

export default withAuthenticationRequired(SettingsPage, {
returnTo: "/settings",
onRedirecting: () => <Spinner />,
});

Call Protected APIs

import { useAuth } from "@guardhouse/react";

export function useApi() {
const { getAccessToken } = useAuth();

const fetchProtected = async (url: string) => {
const token = await getAccessToken();
if (!token) throw new Error("Not authenticated");

const res = await fetch(url, {
headers: { Authorization: `Bearer ${token}` },
});
return res.json();
};

return { fetchProtected };
}

Configuration

OptionRequiredDescription
authorityYesOIDC issuer URL
clientIdYesOAuth client ID
redirectUriYesCallback URI (must match IdP registration)
audienceRecommendedAPI audience for authorization code flow
scopeNoDefault: openid profile email
logoutRedirectUriNoPost-logout redirect URI
onRedirectCallbackNoCalled after login callback with appState
allowOfflineAccessScopeNoRequired if requesting offline_access
debugNoEnable SDK debug logs

Login & Logout Options

Login

loginWithRedirect({
scope: "openid profile email custom_scope",
audience: "https://api.example.com",
prompt: "login",
appState: { returnTo: "/dashboard" },
});

Logout

logout({
returnTo: window.location.origin,
federated: true, // Sign out from IdP too
});

Post-Login Redirect

<GuardhouseProvider
config={{
// ...
onRedirectCallback: (appState) => {
if (appState?.returnTo) {
window.location.href = appState.returnTo;
}
},
}}
>

SSR / Next.js / Remix

GuardhouseProvider is client-side only.

"use client";

import { GuardhouseProvider } from "@guardhouse/react";

export function AuthProvider({ children }: { children: React.ReactNode }) {
return (
<GuardhouseProvider config={{ /* ... */ }}>
{children}
</GuardhouseProvider>
);
}

Always check isLoading before rendering protected UI.


Session Storage

  • Tokens stored in sessionStorage (not localStorage)
  • Session key: gh_oidc_session
  • Session cleared when browser closes

Troubleshooting

Infinite redirect loop

  • Ensure redirectUri matches exactly what's registered in your IdP
  • Check isLoading state before triggering loginWithRedirect

Token not refreshing

  • Add offline_access to scope
  • Set allowOfflineAccessScope: true in config

User profile is null

  • Ensure openid profile email scopes are requested
  • Check /connect/userinfo endpoint is accessible