
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@identity-base/react-client
Advanced tools
Headless React authentication client for Identity.Base. Provides all the heavy lifting for authentication flows while giving you complete control over your UI.
Built on (and depends on) @identity-base/client-core.
<ProtectedRoute> and useRequireAuthnpm install @identity-base/react-client
import { IdentityProvider, enableDebugLogging } from '@identity-base/react-client'
// Enable debug logging in development
if (import.meta.env.DEV) {
enableDebugLogging(true)
}
function App() {
return (
<IdentityProvider
config={{
apiBase: 'https://your-identity-api.com',
clientId: 'your-spa-client-id',
redirectUri: 'https://your-app.com/auth/callback',
}}
>
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/auth/callback" element={<CallbackPage />} />
</Routes>
</Router>
</IdentityProvider>
)
}
import { useLogin } from '@identity-base/react-client'
function LoginPage() {
const { login, isLoading, error } = useLogin({
onSuccess: () => navigate('/dashboard')
})
const handleSubmit = async (e: FormEvent) => {
e.preventDefault()
await login({ email, password })
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
disabled={isLoading}
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Signing in...' : 'Sign In'}
</button>
{error && <div className="error">{error.message}</div>}
</form>
)
}
import { ProtectedRoute, useAuth } from '@identity-base/react-client'
function DashboardPage() {
return (
<ProtectedRoute fallback={<div>Please sign in...</div>}>
<Dashboard />
</ProtectedRoute>
)
}
function Dashboard() {
const { user, logout } = useAuth()
return (
<div>
<h1>Welcome {user?.displayName}!</h1>
<button onClick={logout}>Sign Out</button>
</div>
)
}
import { useAuthorization } from '@identity-base/react-client'
import { useSearchParams } from 'react-router-dom'
function CallbackPage() {
const [params] = useSearchParams()
const { handleCallback, isLoading, error } = useAuthorization({
onSuccess: () => navigate('/dashboard')
})
useEffect(() => {
const code = params.get('code')
const state = params.get('state')
if (code && state) {
handleCallback(code, state)
}
}, [params, handleCallback])
if (isLoading) return <div>Completing sign in...</div>
if (error) return <div>Sign in failed: {error.message}</div>
return <div>Processing...</div>
}
interface IdentityConfig {
// Required
apiBase: string // 'https://your-identity-api.com'
clientId: string // Your OAuth2 client ID
redirectUri: string // Where to redirect after auth
// Optional
scope?: string // OAuth2 scopes (default: 'openid profile email offline_access')
tokenStorage?: 'localStorage' | 'sessionStorage' | 'memory'
autoRefresh?: boolean // Auto refresh access tokens (default: true)
timeout?: number // API timeout in ms (default: 10000)
}
autoRefresh is true (default), the SDK inspects the JWT exp claim and requests a new access token 30 seconds before expiry. If refresh fails, cached tokens are cleared and the error is rethrown so the UI can prompt for a fresh sign-in.autoRefresh is false, expired access tokens are removed from storage and ensureValidToken() resolves to null, leaving it to the host app to restart authentication.localStorage, sessionStorage, or in-memory) and requires a refresh token to be available.const {
user, // Current user profile or null
isAuthenticated, // Boolean auth state
isLoading, // Initial loading state
error, // Any auth errors
refreshUser, // Manually refresh user data
} = useAuth()
const {
login, // (credentials) => Promise<LoginResponse>
logout, // () => Promise<void>
isLoading, // Boolean loading state
error, // Login/logout errors
} = useLogin({
onSuccess: (response) => {}, // Optional success callback
onError: (error) => {}, // Optional error callback
})
const {
register, // (userData) => Promise<{correlationId}>
isLoading, // Boolean loading state
error, // Registration errors
} = useRegister({
onSuccess: (response) => {}, // Optional success callback
})
const {
startAuthorization, // () => Promise<void> (redirects to auth)
handleCallback, // (code, state) => Promise<User>
isLoading,
error,
} = useAuthorization({
onSuccess: () => {}, // Called after successful auth
})
<ProtectedRoute
fallback={<LoginPrompt />}
redirectTo="/login"
onUnauthenticated={() => {}}
>
<SecretContent />
</ProtectedRoute>
function SecurePage() {
const user = useRequireAuth({
redirectTo: '/login',
onUnauthenticated: () => notify('Please sign in')
})
// user is null during loading/redirecting
// user is UserProfile once authenticated
if (!user) return null
return <div>Hello {user.displayName}</div>
}
The client includes built-in debug logging to help troubleshoot authentication flows. Debug logging is disabled by default.
import { enableDebugLogging } from '@identity-base/react-client'
// Enable only in development
if (import.meta.env.DEV) {
enableDebugLogging(true)
}
// Or conditionally based on environment variable
if (import.meta.env.VITE_ENABLE_IDENTITY_DEBUG === 'true') {
enableDebugLogging(true)
}
// Enable debug logging at runtime
__enableIdentityDebug(true) // Returns true
__enableIdentityDebug(false) // Returns false
// Check current debug state
window.__identityDebugEnabled // true/false
When enabled, you'll see detailed logs for:
Example debug output:
IdentityProvider.tsx: Module loading
IdentityProvider: Creating new instance with config: {...}
IdentityProvider.refreshUser: Starting user refresh
Using Bearer token authentication for /users/me
useMfa.verifyChallenge: Starting MFA verification
import { createTokenStorage } from '@identity-base/react-client'
const storage = createTokenStorage('sessionStorage')
// or implement your own TokenStorage interface
import { useIdentityContext } from '@identity-base/react-client'
function AdvancedComponent() {
const { authManager } = useIdentityContext()
const handleExternalAuth = () => {
const url = authManager.buildExternalStartUrl('google', 'login', '/dashboard')
window.location.href = url
}
}
The package is built with TypeScript and includes complete type definitions:
import type {
UserProfile,
LoginRequest,
IdentityConfig,
ApiError
} from '@identity-base/react-client'
// Debug utilities
import { enableDebugLogging, debugLog } from '@identity-base/react-client'
If you're experiencing issues with authentication flows, enable debug logging to see detailed information:
import { enableDebugLogging } from '@identity-base/react-client'
// Enable in development
if (process.env.NODE_ENV === 'development') {
enableDebugLogging(true)
}
// Or enable via browser console
// __enableIdentityDebug(true)
apiBase, clientId, and redirectUri are correctlocalStorage, sessionStorage, memory)// Check MFA debug output
__enableIdentityDebug(true)
// Look for these debug messages:
// - "useMfa.verifyChallenge: Starting MFA verification"
// - "useMfa.verifyChallenge: MFA verification successful"
// - "useMfa.verifyChallenge: MFA verification failed"
// Debug OAuth flows
__enableIdentityDebug(true)
// Check for PKCE-related logs:
// - "IdentityProvider: Creating new instance with config"
// - Authorization URL generation
// - Token exchange logs
// Enable/disable debug logging
__enableIdentityDebug(true) // Enable
__enableIdentityDebug(false) // Disable
// Check current state
window.__identityDebugEnabled // true or false
// Manual user refresh (when authenticated)
window.__identityRefreshUser?.()
tokenStorage: 'memory'<ProtectedRoute> for better UXMIT
FAQs
Headless React authentication client for Identity.Base
We found that @identity-base/react-client demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer 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
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.