React component library for easily integrating Franklin/Genoox authentication (using Auth0) into your application.
npm install @genoox/franklin-id # or yarn add franklin-id, or bun install franklin-id
# Make sure you have react and react-dom installed as well
npm install react react-dom
Replace franklin-id
with the actual published package name (e.g., @your-org/franklin-id
) if you used a different name.
Before using this component, ensure you have:
- Configured an Auth0 Application: Set up an application in your Auth0 dashboard (likely a Single Page Application type).
-
Configured an Auth0 API: Set up an API in Auth0 that your application will interact with (e.g.,
https://genoox.eu.auth0.com/api/v2/
). -
Registered Callback URL: You need a backend endpoint (like the provided Supabase function) that handles the OAuth code exchange. Add the URL of this endpoint (e.g.,
https://<your-project-ref>.supabase.co/functions/v1/auth-callback
) to the "Allowed Callback URLs" in your Auth0 Application settings. - Obtained Credentials: Note down your Auth0 Domain, Client ID, and the API Audience identifier.
Import the FranklinAuth
component and provide the necessary configuration props.
import React, { useState, useEffect } from 'react';
import { FranklinAuth } from 'franklin-id'; // Use your package name
// Configuration - Ideally load sensitive values from environment variables
const AUTH0_DOMAIN = process.env.REACT_APP_AUTH0_DOMAIN || 'your-auth0-domain.auth0.com';
const AUTH0_CLIENT_ID = process.env.REACT_APP_AUTH0_CLIENT_ID || 'your_auth0_client_id';
const AUTH0_CALLBACK_URL = process.env.REACT_APP_AUTH0_CALLBACK_URL || 'https://<your-project-ref>.supabase.co/functions/v1/auth-callback'; // Your backend callback handler
const API_AUDIENCE = process.env.REACT_APP_API_AUDIENCE || 'https://your-api-audience.com/';
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [userProfile, setUserProfile] = useState<any>(null); // Store decoded ID token claims
const [accessToken, setAccessToken] = useState<string | null>(null);
const [authError, setAuthError] = useState<any>(null);
// Function to parse ID token (basic example, use a library like jwt-decode for robustness)
const parseJwt = (token: string) => {
try {
return JSON.parse(atob(token.split('.')[1]));
} catch (e) {
return null;
}
};
const handleLoginSuccess = (tokens: { idToken: string, accessToken: string }) => {
console.log("Authentication successful!");
setIsAuthenticated(true);
setAccessToken(tokens.accessToken); // Store access token in state (or context/memory)
setUserProfile(parseJwt(tokens.idToken)); // Decode ID token for user info
setAuthError(null);
// **IMPORTANT**: Avoid storing the Access Token in localStorage due to security risks (XSS).
// Store it in memory (React state, Context API, Redux, Zustand, etc.)
// You might store the ID token or a session flag in localStorage/sessionStorage if needed
// to check login status across page refreshes, but fetch a new Access Token when required.
// Example API Call:
// fetch('https://your.api.com/data', {
// headers: { Authorization: `Bearer ${tokens.accessToken}` }
// })
// .then(...)
};
const handleLoginFailure = (error: any) => {
console.error("Authentication failed:", error);
setIsAuthenticated(false);
setAccessToken(null);
setUserProfile(null);
setAuthError(error);
};
const handleLogout = () => {
// Basic logout: clear local state
setIsAuthenticated(false);
setAccessToken(null);
setUserProfile(null);
setAuthError(null);
// TODO: Add redirect to Auth0 logout endpoint if needed for full session termination
// const logoutUrl = `https://${AUTH0_DOMAIN}/v2/logout?client_id=${AUTH0_CLIENT_ID}&returnTo=${window.location.origin}`;
// window.location.href = logoutUrl;
};
return (
<div>
<h1>My Application</h1>
{!isAuthenticated ? (
<div>
<p>Please log in.</p>
{/* Render the FranklinAuth component directly */}
<FranklinAuth
auth0Domain={AUTH0_DOMAIN}
clientId={AUTH0_CLIENT_ID}
redirectUri={AUTH0_CALLBACK_URL} // Your backend callback handler
audience={API_AUDIENCE}
onAuthSuccess={handleLoginSuccess}
onAuthFailure={handleLoginFailure}
// scope="openid profile email" // Optionally override default scopes
// state="custom_state_value" // Optionally provide custom state
/>
{authError && <p style={{ color: 'red' }}>Error: {authError.error_description || authError.error || 'Login failed'}</p>}
</div>
) : (
<div>
<h2>Welcome, {userProfile?.name || 'User'}!</h2>
<img src={userProfile?.picture} alt="Profile" width="50" style={{ borderRadius: '50%' }} />
<p>You are logged in.</p>
<button onClick={handleLogout}>Logout</button>
{/* Add components that need the access token here */}
{/* Example: <UserProfileEditor accessToken={accessToken} /> */}
</div>
)}
</div>
);
}
export default App;
Prop | Type | Required | Description |
---|---|---|---|
auth0Domain |
string |
Yes | Your Auth0 tenant domain (e.g., your-tenant.auth0.com ). |
clientId |
string |
Yes | The Client ID of your Auth0 Application. |
redirectUri |
string |
Yes | The URL of your backend callback handler (e.g., Supabase function) that Auth0 should redirect to after login. |
audience |
string |
Yes | The unique identifier (API Audience) of the API you want to get an Access Token for. |
onAuthSuccess |
(tokens: { idToken: string, accessToken: string }) => void |
Yes | Callback function executed on successful authentication. Receives an object containing the idToken and accessToken . |
onAuthFailure |
(error: any) => void |
Yes | Callback function executed on authentication failure. Receives the error object from Auth0. |
scope |
string |
No | Optional. Space-separated list of OAuth scopes to request. Defaults to extensive Franklin/Genoox scopes. Override if needed. |
state |
string |
No | Optional. A random string used for CSRF protection. If provided, your callback handler should verify it. Defaults to a placeholder. |
-
idToken
(ID Token):- A JSON Web Token (JWT) containing claims about the authenticated user (e.g., name, email, picture).
- Intended for the client application to understand who the user is.
- Can be decoded (client-side) to display user profile information.
- Verify its signature if making critical decisions based on it (usually handled server-side during code exchange).
-
accessToken
(Access Token):- A token (often a JWT, but can be opaque) that proves the user has authorized the application to access specific resources on their behalf (defined by the
scope
). - Intended for the Resource Server (your target API, e.g.,
https://franklin.genoox.com/api/v2/...
). - Crucially, include this token in the
Authorization: Bearer <accessToken>
header when making requests to your protected API. -
Security: Do NOT store Access Tokens in
localStorage
orsessionStorage
as they are vulnerable to XSS attacks. Store them in application memory (React state, Context, etc.) and fetch new ones when needed (e.g., on page load if the user has a valid session indicated by an ID token or cookie).
- A token (often a JWT, but can be opaque) that proves the user has authorized the application to access specific resources on their behalf (defined by the
(Optional: Add details here if you want contributors to know how to build/test the library locally)
# Clone the repo
git clone ...
cd franklin-id
# Install dependencies
bun install # or npm install / yarn install
# Run the local dev server (if App.tsx is set up for testing)
bun run dev # or npm run dev / yarn dev
# Build the library
bun run build # or npm run build / yarn build