npm package: @nettie/franklin-react-auth
Description: React component library for easily integrating Franklin/Genoox authentication (using Auth0) into your application.
Latest version: 0.1.5

# Franklin ID Auth Component

React component library for easily integrating Franklin/Genoox authentication (using Auth0) into your application.

## Installation

```bash
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.

## Prerequisites

Before using this component, ensure you have:

1.  **Configured an Auth0 Application**: Set up an application in your Auth0 dashboard (likely a Single Page Application type).
2.  **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/`).
3.  **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.
4.  **Obtained Credentials**: Note down your Auth0 Domain, Client ID, and the API Audience identifier.

## Usage

Import the `FranklinAuth` component and provide the necessary configuration props.

```tsx
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;
```

## Component Props

| 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.         |

## Handling Tokens

-   **`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` or `sessionStorage` 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).

## Development

(Optional: Add details here if you want contributors to know how to build/test the library locally)

```bash
# 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
```