authsafe-react
v0.0.5
Published
Official React SDK for AuthSafe. Provides reusable components, authentication hooks, and utilities to securely integrate AuthSafe into your React applications.
Maintainers
Readme
AuthSafe React SDK
Official React SDK for AuthSafe — a secure, multi-tenant identity and access management platform with full OAuth 2.1 and OpenID Connect (OIDC) support.
Features
- ✅ OAuth 2.1 & OIDC Compliant - Full implementation using
oidc-client-ts - ✅ PKCE Support - Automatic Proof Key for Code Exchange (required by OAuth 2.1)
- ✅ Multiple Signin Methods - Redirect, Popup, and Silent refresh
- ✅ Token Management - Automatic token refresh with refresh tokens
- ✅ React Query Integration - Efficient user data caching and invalidation
- ✅ TypeScript First - Fully typed API for better DX
- ✅ Customizable Branding - Logo, theme, colors, and layout
- ✅ SSR Compatible - Works with Next.js and other SSR frameworks
- ✅ Secure Storage - sessionStorage for OIDC state, HTTP-only cookies for refresh tokens
Installation
npm install authsafe-reactOr with Yarn:
yarn add authsafe-reactQuick Start
1. Wrap Your App with AuthProvider
import { AuthProvider } from 'authsafe-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider
config={{
clientId: "your-client-id",
redirectUri: "http://localhost:3000/callback",
scope: ["openid", "email", "profile", "offline_access"],
env: "development",
}}
>
<YourApp />
</AuthProvider>
</QueryClientProvider>
);
}2. Add Login Button
import { useLogin } from 'authsafe-react';
function LoginButton() {
const { signinRedirect, isLoading } = useLogin();
return (
<button onClick={() => signinRedirect()} disabled={isLoading}>
{isLoading ? "Redirecting..." : "Sign In with AuthSafe"}
</button>
);
}3. Handle OAuth Callback
// pages/Callback.tsx
import { useOAuthCallback, useAuth } from 'authsafe-react';
import { useNavigate } from 'react-router-dom';
import { useEffect } from 'react';
function CallbackPage() {
const { loading, error } = useOAuthCallback();
const { isAuthenticated } = useAuth();
const navigate = useNavigate();
useEffect(() => {
if (!loading && isAuthenticated) {
navigate('/dashboard');
}
}, [loading, isAuthenticated, navigate]);
if (loading) return <div>Authenticating...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Success! Redirecting...</div>;
}4. Access User Information
import { useAuth } from 'authsafe-react';
function Dashboard() {
const { user, isAuthenticated, isLoading } = useAuth();
if (isLoading) return <div>Loading...</div>;
if (!isAuthenticated) return <div>Please sign in</div>;
return (
<div>
<h1>Welcome, {user?.email}!</h1>
<p>User ID: {user?.id}</p>
</div>
);
}5. Logout
import { useLogout } from 'authsafe-react';
function LogoutButton() {
const { signoutRedirect, isLoading } = useLogout();
return (
<button onClick={() => signoutRedirect()} disabled={isLoading}>
{isLoading ? "Logging out..." : "Sign Out"}
</button>
);
}Authentication Methods
Signin with Redirect (Recommended)
Full-page redirect to the authorization server:
const { signinRedirect } = useLogin();
// Basic signin
await signinRedirect();
// With extra parameters
await signinRedirect({
prompt: 'login', // Force re-authentication
login_hint: '[email protected]',
ui_locales: 'en-US',
});Signin with Popup
Opens a popup window for authentication:
const { signinPopup } = useLogin();
// Popup signin (doesn't reload the page)
await signinPopup();Logout Methods
const { signoutRedirect, signoutPopup, signoutLocal } = useLogout();
// Redirect to server logout endpoint
await signoutRedirect({
post_logout_redirect_uri: 'http://localhost:3000',
});
// Logout in popup
await signoutPopup();
// Local logout (doesn't contact server)
await signoutLocal();Token Management
Access Tokens in API Calls
import { useAuth } from 'authsafe-react';
function MyComponent() {
const { tokens } = useAuth();
const callApi = async () => {
const response = await fetch('/api/protected', {
headers: {
'Authorization': `Bearer ${tokens.accessToken}`,
},
});
return response.json();
};
return <button onClick={callApi}>Call API</button>;
}Token Refresh
const { refresh, tokens } = useAuth();
// Manual refresh
if (tokens.refreshToken) {
await refresh();
}Automatic Token Renewal
Enable in config:
<AuthProvider
config={{
clientId: "your-client-id",
redirectUri: "http://localhost:3000/callback",
automaticSilentRenew: true, // ✅ Enable auto-refresh
}}
>
<App />
</AuthProvider>User Query & Invalidation
Track User Authentication
import { useAuth } from 'authsafe-react';
function ProtectedRoute({ children }) {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) return <div>Loading...</div>;
if (!isAuthenticated) return <Navigate to="/login" />;
return children;
}Logout by Invalidating User Query
import { useLogout } from 'authsafe-react';
import { useQueryClient } from '@tanstack/react-query';
import { userQueryKeys } from 'authsafe-react';
function LogoutButton() {
const { signoutLocal } = useLogout();
const queryClient = useQueryClient();
const handleLogout = async () => {
await signoutLocal();
// Invalidate user query to trigger refetch
queryClient.invalidateQueries({ queryKey: userQueryKeys.all() });
};
return <button onClick={handleLogout}>Logout</button>;
}Advanced Usage
Direct OAuthService Access
import { useInternals } from 'authsafe-react';
function AdvancedComponent() {
const { oauthService } = useInternals();
const performSilentRefresh = async () => {
if (oauthService) {
const user = await oauthService.signinSilent();
console.log('Silently refreshed:', user);
}
};
return <button onClick={performSilentRefresh}>Silent Refresh</button>;
}Custom OAuthService Instance
import { OAuthService } from 'authsafe-react';
const oauthService = new OAuthService({
clientId: "your-client-id",
redirectUri: "http://localhost:3000/callback",
scope: ["openid", "email"],
authority: "https://custom-auth-server.com",
});
await oauthService.signinRedirect();Configuration Options
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| clientId | string | ✅ | - | Your OAuth client ID |
| redirectUri | string | ✅ | - | Callback URL after authentication |
| scope | string[] | ❌ | ["openid", "email"] | OAuth scopes to request |
| env | "development" \| "production" | ❌ | "development" | Environment |
| authority | string | ❌ | Auto-detected | Custom OAuth server URL |
| popupRedirectUri | string | ❌ | Same as redirectUri | Popup callback URL |
| automaticSilentRenew | boolean | ❌ | false | Enable automatic token renewal |
API Reference
Hooks
useLogin()- Initiate signin flow (redirect or popup)useOAuthCallback()- Handle OAuth callback (redirect)useOAuthPopupCallback()- Handle OAuth callback (popup)useLogout()- Logout methods (redirect, popup, local)useAuth()- Access auth state, user, and tokensuseUserQuery()- Query user information
Services
OAuthService- Core OAuth/OIDC service using oidc-client-tsHttpService- HTTP client with auth headers
Components
AuthProvider- Context provider for authenticationProfile- Pre-built profile component
Documentation
- 📘 OIDC Usage Guide - Complete usage documentation
- 📝 Examples - Copy-paste examples for common scenarios
- 🔧 OAuth Implementation - Technical details
Security Features
- ✅ PKCE (RFC 7636) - Proof Key for Code Exchange (automatic)
- ✅ State Parameter - CSRF protection (automatic)
- ✅ Nonce - Replay protection for ID tokens (automatic)
- ✅ Token Validation - JWT signature and claims validation
- ✅ Secure Storage - sessionStorage for state, HTTP-only cookies for refresh tokens
- ✅ OAuth 2.1 - Latest OAuth security best practices
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Requirements
- React 18+
- Node.js 16+
- TypeScript 5+ (optional, but recommended)
Development
Install Dependencies
npm installStart Dev Server
npm run devBuild
npm run buildRun Tests
npm run testTroubleshooting
"State does not match" Error
- Ensure sessionStorage is enabled
- Don't refresh page during OAuth flow
- Verify redirect_uri matches exactly (including trailing slash)
Popup Blocked
- Allow popups for your domain
- Use redirect method as fallback
No Refresh Token
- Include
offline_accessscope in config
For more troubleshooting tips, see OIDC Usage Guide.
Support
- 📧 Email: [email protected]
- 📚 Documentation: https://docs.authsafe.io
- 🐛 Issues: https://github.com/authsafe/authsafe-react/issues
License
Proprietary License © 2025 AuthSafe. All rights reserved.
Built with ❤️ by the AuthSafe Team
