Welcome to a fullyβfunctional starter you can fork and deploy in minutes π₯
This monorepo template combines Viteβ―7, HeroUIβ―v2, and a powerful authentication abstraction supporting multiple OAuth providers (Auth0, Dex, and others). The repo includes a backend Cloudflare Worker demo with builtβin automatic permission provisioning and OpenAPI documentation, a polished landing page with oneβclick login, and complete i18n support.
Under the hood it uses Turborepo and Yarnβ―4 workspaces for fast installs, parallel builds and a great developer experience.
If you appreciate my work, please consider giving it a star! π€©
Click the screenshot below to try the public deployment. Visitors see an attractive landing page with a Logβ―in button and direct links to a sample API and autoβgenerated OpenAPI/Swagger docs β no auth required to view the interface.
Ths plugin uses our @sctg/vite-plugin-github-pages-spa Vite 6 plugin for handling the Github Pages limitations with SPA.
- π Fast development with Vite 7
- π¨ Beautiful UI components from HeroUI v2
- π Flexible authentication with multiple OAuth providers (Auth0, Dex)
- π Polished landing page with login button and quick links to API demo & builtβin OpenAPI/Swagger docs (works even when unauthenticated).
- π οΈ Auth0 User & Permission Management: Built-in administration panel to manage users and sync permissions.
- π Localized Session Details: Real-time technical information modal with JWT payload analysis.
- π Internationalization with i18next (6 languages included)
- π Dark/Light mode support
- π± Responsive design
- πͺ Cookie consent management
- π§© Type-safe with TypeScript
- π§Ή Code quality with ESLint 9
- π¦ Optimized build with manual chunk splitting
- β‘ Turborepo for intelligent caching and parallel builds
- π§Ά Yarn 4 workspaces for efficient dependency management
- Vite 7
- HeroUI
- Tailwind CSS 4
- Tailwind Variants
- React 19
- i18next
- Auth0 React SDK
- OIDC Client TS (For Dex and other OAuth providers)
- ESLint 9
- TypeScript
- Framer Motion
- Turborepo (Monorepo build system)
- Yarn 4 (Package manager with workspaces)
# Clone the repository
git clone https://github.com/sctg-development/vite-react-heroui-auth0-template.git
# Change to project directory
cd vite-react-heroui-auth0-template
# Ensure Yarn 4 is active (corepack included in modern Node)
corepack enable
yarn set version 4.2.2
# Install dependencies (monorepo workspaces)
yarn install
# Create a `.env` with your Auth0 credentials
cat <<EOF > .env
AUTHENTICATION_PROVIDER_TYPE=auth0
AUTH0_CLIENT_ID=your-auth0-client-id
AUTH0_CLIENT_SECRET=your-auth0-client-secret
AUTH0_DOMAIN=your-auth0-domain
AUTH0_SCOPE="openid profile email read:api write:api admin:api"
AUTH0_AUDIENCE=http://localhost:5173
API_BASE_URL=http://localhost:8787/api
CORS_ORIGIN=http://localhost:5173 # can be a single origin or comma-separated list
# e.g. CORS_ORIGIN=https://sctg-development.github.io,https://www.tmplace.com
READ_PERMISSION=read:api
WRITE_PERMISSION=write:api
ADMIN_PERMISSION=admin:api
AUTHENTICATION_PROVIDER_TYPE=auth0
EOF
# Spin up frontend + worker with environment vars
yarn dev:env
# Open your browser at http://localhost:5173/
# You'll land on a friendly home page with a login CTA and links to
# the example API and Swagger docs. Click "Log in" to exercise the
# built-in Auth0 permission provisioning and explore the secured routes.For more detailed commands, see the Turborepo Guide.
- Vite, OAuth & HeroUI Template
- Star the project
- Live demo
- On Github Pages ?
- Features
- Technologies Used
- Quick Start
- Table of Contents
- Authentication
- Administration & User Management
- Technical Information Modal
- Internationalization
- Cookie Consent
- Project Structure
- Available Scripts in the frontend application
- Deployment
- Tailwind CSS 4
- How to Use
- Contributing
- License
- Authentication Architecture
- Auth0 Automatic Permissions
This template provides a flexible authentication system with support for multiple OAuth providers. The architecture uses an abstraction layer that allows you to easily switch between different providers while maintaining a consistent API. Currently, the template supports:
- Auth0 (Default) - Using the Auth0 React SDK
- Dex - Using the OIDC Client TS library
The authentication system can be extended to support other OAuth providers like Azure AD, Okta, or any OAuth 2.0 compliant service by implementing the provider interface.
-
Create an Auth0 Account:
- Go to Auth0 and sign up for a free account.
-
Create a New Application:
- In the Auth0 dashboard, navigate to the "Applications" section.
- Click on "Create Application".
- Choose a name for your application.
- Select "Single Page Web Applications" as the application type.
- Click "Create".
-
Configure Application Settings:
- In the application settings, you will find your
Client IDandDomain. - Set the "Allowed Callback URLs" to
http://localhost:5173(or your development URL). - Set the "Allowed Logout URLs" to
http://localhost:5173(or your development URL). - Set the "Allowed Web Origins" to
http://localhost:5173(or your development URL).
- In the application settings, you will find your
-
Sample settings:
- The settings used by the demo deployment on GitHub Pages are:
- Allowed Callback URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template - Allowed Logout URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template - Allowed Web Origins:
https://sctg-development.github.io - On Github repository settings, the
AUTH0_CLIENT_IDsecret is set to the Auth0 client ID and theAUTH0_DOMAINsecret is set to the Auth0 domain. - The full Auth0 configuration screenshot is available here.
- Allowed Callback URLs:
- The settings used by the demo deployment on GitHub Pages are:
To keep your Auth0 credentials secure, use environment variables. Create a .env file in the root of your project and add the following:
AUTH0_CLIENT_ID=your-auth0-client-id
AUTH0_CLIENT_SECRET=your-auth0-secret
AUTH0_MANAGEMENT_API_CLIENT_ID="from Auth0 Management API (Test Application)"
AUTH0_MANAGEMENT_API_CLIENT_SECRET="from Auth0 Management API (Test Application)"
AUTH0_DOMAIN=your-auth0-domain
AUTH0_SCOPE="openid profile email read:api write:api"
AUTH0_AUDIENCE=https://myapi.example.com
API_BASE_URL=https://myapi.example.com/api
CORS_ORIGIN=https://your-github-username.github.io
READ_PERMISSION=read:api
WRITE_PERMISSION=write:api
ADMIN_PERMISSION=admin:api
ADMIN_AUTH0_PERMISSION="auth0:admin:api"
AUTHENTICATION_PROVIDER_TYPE=auth0For using the provided GitHub Actions workflows, you need to add the following secrets to your repository:
AUTH0_CLIENT_ID=your-auth0-client-id
AUTH0_CLIENT_SECRET=your-auth0-secret
AUTH0_MANAGEMENT_API_CLIENT_ID="from Auth0 Management API (Test Application)"
AUTH0_MANAGEMENT_API_CLIENT_SECRET="from Auth0 Management API (Test Application)"
AUTH0_DOMAIN=your-auth0-domain
AUTH0_SCOPE="openid profile email read:api write:api"
AUTH0_AUDIENCE=https://myapi.example.com
API_BASE_URL=https://myapi.example.com/api
CORS_ORIGIN=https://your-github-username.github.io
READ_PERMISSION=read:api
WRITE_PERMISSION=write:api
ADMIN_PERMISSION=admin:api
ADMIN_AUTH0_PERMISSION="auth0:admin:api"
AUTHENTICATION_PROVIDER_TYPE=auth0each secrets should be manually entered in Github like:

You can use the AuthenticationGuard component to protect routes that require authentication. This component works with any configured provider and will redirect users to the login page if they are not authenticated.
import { AuthenticationGuard } from "./authentication";
<Route element={<AuthenticationGuard component={DocsPage} />} path="/docs" />;The template includes a fully-configured secure API call system that demonstrates how to communicate with protected backend services using Auth0 token authentication.
To enable secure API calls in your application:
-
Create an API in Auth0 Dashboard:
- Navigate to "APIs" section in the Auth0 dashboard
- Click "Create API"
- Provide a descriptive name (e.g., "My Application API")
- Set the identifier (audience) - typically a URL or URI (e.g.,
https://api.myapp.com) - Configure the signing algorithm (RS256 recommended)
-
Configure API Settings:
- Enable RBAC (Role-Based Access Control) if you need granular permission management
- Define permissions (scopes) that represent specific actions (e.g.,
read:api,write:api) - Configure token settings as needed (expiration, etc.)
- Access tokens are shortβlived (e.g. 1h). If you make very frequent API calls, you can reduce the duration (more secure) or increase it (fewer refreshes) depending on your use-case.
- ID tokens are also timeβlimited and are used for user identity information (not API access).
- For SPA clients, enable Refresh Token Rotation and set a sensible Refresh Token Expiration (Idle / Absolute) in the Advanced Settings β OAuth section of your Auth0 Application. This template uses silent refresh via the Auth0 React SDK; if the refresh token expires, users must re-authenticate.
- The client code is already wired to:
- proactively refresh the access token if it is about to expire (within ~10s)
- retry a request once on
401by forcing a token refresh (ignoreCache: true) - avoid infinite retry loops (only one retry per failed request)
- Include permissions in the access token
-
Set Environment Variables: Add the following to your
.envfile:AUTH0_AUDIENCE=your-api-identifier AUTH0_SCOPE="openid profile email read:api write:api" API_BASE_URL=http://your-api-url.com
-
Sample Configuration: For reference, view the Auth0 API configuration used in the demo deployment.
The template provides a hook useSecuredApi that handles token acquisition and authenticated requests for any configured provider:
import { useSecuredApi } from "@/authentication";
// Inside your component:
const { getJson, postJson, deleteJson } = useSecuredApi();
// GET request to a secured API endpoint
const apiData = await getJson(`${import.meta.env.API_BASE_URL}/endpoint`);
// POST request to a secured API endpoint
const apiData = await postJson(`${import.meta.env.API_BASE_URL}/endpoint`, {
data: "example",
});
// DELETE request to a secured API endpoint
const apiData = await deleteJson(`${import.meta.env.API_BASE_URL}/endpoint`);This function automatically:
- Requests the appropriate token with configured audience and scope
- Attaches the token to the request header
- Handles errors appropriately
- Returns the JSON response
For more control, you can use the authentication provider API directly:
import { useAuth } from "@/authentication";
// Inside your component:
const auth = useAuth();
// Access authentication state
const isLoggedIn = auth.isAuthenticated;
const userData = auth.user;
// Perform authentication actions
await auth.login();
await auth.logout();
// Get tokens for API calls
const token = await auth.getAccessToken();
// Make API calls
const data = await auth.getJson(`${import.meta.env.API_BASE_URL}/endpoint`);
await auth.postJson(`${import.meta.env.API_BASE_URL}/endpoint`, {
key: "value",
});This function automatically:
- Requests the appropriate token with configured audience and scope
- Attaches the token to the request header
- Handles errors appropriately
- Returns the JSON response
You can check user permissions with any configured authentication provider:
import { useAuth } from "@/authentication";
// Inside your component:
const auth = useAuth();
const canReadData = await auth.hasPermission("read:api");
// Or using the useSecuredApi hook
import { useSecuredApi } from "@/authentication";
const { hasPermission } = useSecuredApi();
const canReadData = await hasPermission("read:api");The permission system works across different providers, with each implementation handling the specific token format of that provider.
This template includes a AuthenticationGuardWithPermission component that works with any configured provider and wraps a component to check if the user has the required permission:
import { AuthenticationGuardWithPermission } from "@/authentication";
<AuthenticationGuardWithPermission permission="read:api">
<ProtectedComponent />
</AuthenticationGuardWithPermission>;For demonstration purposes, the template includes a Cloudflare Worker that acts as a secured backend API:
- Start the Worker with environment variables:
# From the root directory
yarn dev:worker:env- Test API Integration:
With both your application and the worker running, navigate to the
/apiroute in your application to see the secure API call in action.
- Your application requests an access token from Auth0 with specific audience and scope
- Auth0 issues a JWT token containing the requested permissions
- Your application includes this token in the Authorization header
- The backend API validates the token using Auth0's public key
- If valid, the API processes the request according to the permissions in the token
This template includes a powerful administration interface (route /admin/users) for managing users and permissions directly via the Auth0 Management API.
When a user with the ADMIN_AUTH0_PERMISSION (default auth0:admin:api) opens
the technical info modal they see a chip labelled with the permission. Clicking
that chip navigates to the admin page.
The admin page itself doesnβt talk to Auth0 directly from the browser β instead
it obtains a shortβlived Management API token from the Cloudflare Worker by
posting to POST /api/__auth0/token. The worker is configured with two
secrets:
AUTH0_MANAGEMENT_API_CLIENT_ID=your-m2m-client-id
AUTH0_MANAGEMENT_API_CLIENT_SECRET=your-m2m-client-secretThese credentials belong to an Auth0 MachineβtoβMachine application that has at
least the update:users and read:users scopes. The worker caches the token
in KV to reduce Auth0 rate limit usage.
Once the frontend receives the token, it uses it to call the Auth0 Management
API (listing users, adding/removing permissions, deleting accountsβ¦). The
useSecuredApi hook provides helper methods such as
getAuth0ManagementToken, listAuth0Users, addPermissionToUser, etc. All of
those methods simply wrap fetch calls to Auth0 endpoints using the supplied
token.
Because the service token never leaves the worker, your client code and end
users never see the underlying M2M credentials β they only receive the
shortβlived access token returned by /api/__auth0/token, which expires after
about an hour.
The client application exposes a set of utility functions (exported by
useSecuredApi in auth-components.tsx) that wrap the Management API logic
used by the admin page. In addition to listing users, adding/removing
permissions and deleting accounts, the helpers include several functions
related to your Auth0 resource server scopes:
getResourceServers,getResourceServerScopesβ query the configured APIs.updateResourceServerScopesβ patch an API with a new list of scopes.checkResourceServerScopesβ compare the current scopes against a target list and returntruewhen they match.- variant helpers that accept an audience URL instead of an ID.
These methods power the βSync Auth0β button on the admin page. The button
compares the local Permission enum with the scopes stored in Auth0 and,
if they differ, updates the resource server in one call. A user must not
only possess the ADMIN_AUTH0_PERMISSION to reach the page, they also need
additional Management API privileges in order to read and write resource
server definitions β the same M2M client configured via
AUTH0_MANAGEMENT_API_CLIENT_ID/SECRET should be granted the read:api
and update:api (or more generally read:resource_servers and
update:resource_servers) scopes.
You can also invoke the same functions directly from your own code (e.g. as part of a migration script or CI job) to keep definitions in sync programmatically.
Having these utilities means the template is not only a sample UI, it also provides a convenient API layer for managing scopes without writing raw fetches or dealing with the Management API boilerplate yourself.
- In your Auth0 dashboard create a Machine-to-Machine Application.
- Grant it access to the Management API with the following scopes:
read:usersupdate:users- (optionally
delete:usersif you want to allow account removal)
- Copy the generated Client ID and Client Secret and set them as the
worker/environment variables
AUTH0_MANAGEMENT_API_CLIENT_IDandAUTH0_MANAGEMENT_API_CLIENT_SECRET. - Add the
ADMIN_AUTH0_PERMISSION(typicallyauth0:admin:api) to the list of permissions for any user who should be able to reach the admin panel. In the demo we automatically grant it on first login via the autoβpermission provisioner.
With this configuration the admin interface will function correctly, and only users who already possess the required permission can access it.
Accessible via /admin/users, the management page allows administrators to:
- List & Search Users: View all users registered in the Auth0 tenant.
- Manage Permissions: Assign or revoke specific API permissions to any user in real-time.
- Sync Auth0 Permissions: Automatically synchronize local permission definitions with the Auth0 Resource Server (API) scopes.
- Delete Users: Remove users directly from the application.
Important
To use these features, you must configure the Auth0 Management API credentials and set the ADMIN_AUTH0_PERMISSION in your environment variables.
The template provides a comprehensive technical modal for developers and power users to inspect their current session.
- JWT Analysis: Decodes and displays the current Access Token payload.
- Localized Expiration: Displays a real-time countdown in a human-readable "n days h:m:s" format, fully localized across all 6 supported languages.
- Permission Overview: Lists all permissions associated with the current session.
- Quick Links: Integrated "Admin Panel" shortcut for users with appropriate privileges.
This template includes an optional feature to automatically assign a set of predefined permissions to users upon their first login (or whenever permissions are missing). This is particularly useful for onboarding new users with a default set of "read" or "basic" access levels without manual administrator intervention.

The automatic provisioning follows a robust 5-step lifecycle designed to be efficient and secure:
- Detection: Immediately after a successful login, the
AutoPermissionProvisionercomponent (client-side) checks the user's current scopes against the requiredAUTH0_AUTOMATIC_PERMISSIONSlist. - Provisioning Request: If permissions are missing, the client calls the Cloudflare Worker's
/api/__auth0/autopermissionsendpoint. - Server-Side Assignment: The worker verifies the user's identity, obtains a Management API token (using a high-performance KV-cached mechanism), and calls the Auth0 Management API to assign the missing permissions.
- Token Refresh: Upon success, the client triggers a silent token refresh with
cacheMode: "off". This forces the Auth0 SDK to bypass the local cache and fetch a fresh JWT containing the newly assigned scopes. - Persistent Guard & Cleanup: To prevent infinite refresh loops (e.g., during Auth0 propagation delays), a
sessionStorageguard tracks the provisioning attempt for that specific user. Once the user is confirmed to have all required permissions, the flag is automatically cleaned up.
To enable this feature, configure the following variables:
Cloudflare Worker (Secrets/Env):
AUTH0_AUTOMATIC_PERMISSIONS: Comma-separated list of scopes (e.g.,read:api,user:profile).AUTH0_MANAGEMENT_API_CLIENT_ID&AUTH0_MANAGEMENT_API_CLIENT_SECRET: Credentials for an Auth0 M2M application withupdate:usersandread:usersscopes.
Client (Vite Env):
AUTH0_AUTOMATIC_PERMISSIONS: An array of strings mirroring the worker's configuration.
This template uses i18next for internationalization. The configuration and available languages are defined in the src/i18n.ts file.
To add a new language to the application, follow these steps:
-
Update the
availableLanguagesarray:- Open the
src/i18n.tsfile. - Add a new object to the
availableLanguagesarray with the following properties:code: The ISO 639-1 language code (e.g., "en-US").nativeName: The native name of the language (e.g., "English").isRTL: Whether the language is right-to-left (e.g.,false).
- Open the
-
Create a Translation File:
- In the
src/locales/basedirectory, create a new JSON file named with the language code (e.g.,en-US.json). - Add the translations for the new language in this file.
- In the
-
Update the Load Path:
- In the
src/i18n.tsfile, manually add a switch case to theloadPathfunction to handle the new JSON file for the added language.
- In the
The LanguageSwitch component allows users to switch between the available languages. It is defined in the src/components/language-switch.tsx file.
- The component uses the i18n instance to change the language and update the document metadata.
- It automatically updates the document direction based on the language (left-to-right or right-to-left).
- The selected language is stored in
localStorageto persist the user's preference.
To use the LanguageSwitch component in your application, simply include it in your JSX:
<LanguageSwitch
availableLanguages={[
{ code: "en-US", nativeName: "English", isRTL: false, isDefault: true },
{ code: "fr-FR", nativeName: "FranΓ§ais", isRTL: false },
]}
/>or more simply using the availableLanguages array defined in the src/i18n.ts file:
import { availableLanguages } from "@/i18n";
<LanguageSwitch availableLanguages={availableLanguages} />;This component will render a dropdown menu with the available languages, allowing users to switch languages easily.
The default configuration uses the i18next-http-backend plugin for language lazy loading. This means that translations are loaded only when needed, improving the application's performance.
- Configuration:
src/i18n.ts - Translations:
src/locales/base - Language Switch:
src/components/language-switch.tsx
By following the steps above, you can easily add new languages and manage internationalization for your application.
This template includes a cookie consent management system to comply with privacy regulations like GDPR. The system displays a modal dialog asking users for consent to use cookies and stores their preference in the browser's localStorage.

- Modern modal-based UI with blur backdrop
- Internationalized content for all supported languages
- Stores user preferences in localStorage
- Provides a context API for checking consent status throughout the application
- Supports both accepting and rejecting cookies
The cookie consent feature can be enabled or disabled through the site configuration:
- Enable/Disable Cookie Consent:
- Open the
src/config/site.tsfile - Set the
needCookieConsentproperty totrueorfalse:
- Open the
export const siteConfig = () => ({
needCookieConsent: true, // Set to false if you don't need cookie consent
// ...other configuration
});- Context Provider:
src/contexts/cookie-consent-context.tsx- Provides a React context to manage consent state - UI Component:
src/components/cookie-consent.tsx- Renders the consent modal using HeroUI components - Consent Status: The consent status can be one of three values:
pending: Initial state, user hasn't made a decision yetaccepted: User has accepted cookiesrejected: User has rejected cookies
You can access the cookie consent status in any component using the useCookieConsent hook:
import { useCookieConsent } from "@/contexts/cookie-consent-context";
const MyComponent = () => {
const { cookieConsent, acceptCookies, rejectCookies, resetCookieConsent } =
useCookieConsent();
// Load analytics only if cookies are accepted
useEffect(() => {
if (cookieConsent === "accepted") {
// Initialize analytics, tracking scripts, etc.
}
}, [cookieConsent]);
// ...rest of your component
};- Modify the appearance of the consent modal in
src/components/cookie-consent.tsx - Add custom tracking or cookie management logic in the
acceptCookiesandrejectCookiesfunctions insrc/contexts/cookie-consent-context.tsx - Update the cookie policy text in the language files (e.g.,
src/locales/base/en-US.json)
This template follows a monorepo structure managed by Turborepo with Yarn 4 workspaces, containing the frontend application and Cloudflare Worker.
vite-react-heroui-auth0-template/
βββ package.json # Root package.json with Turborepo + workspaces
βββ turbo.json # Turborepo configuration
βββ .yarnrc.yml # Yarn 4 configuration
βββ yarn.lock # Unified lockfile for all packages
βββ TURBOREPO-GUIDE.md # Turborepo usage guide
βββ apps/
β βββ client/ # Frontend application
β β βββ public/ # Static assets
β β βββ src/
β β β βββ authentication/ # Authentication system
β β β β βββ auth-components.tsx # Authentication UI components
β β β β βββ auth-root.tsx # Root authentication provider
β β β β βββ index.ts # Exports
β β β β βββ providers/ # Provider implementations
β β β β βββ auth-provider.ts # Provider interface
β β β β βββ auth0-provider.tsx # Auth0 implementation
β β β β βββ dex-provider.tsx # Dex implementation
β β β β βββ use-auth.tsx # Auth context and hooks
β β β βββ components/ # Reusable UI components
β β β βββ config/ # Configuration files
β β β βββ hooks/ # Custom React hooks
β β β βββ layouts/ # Page layout components
β β β βββ locales/ # Translation files
β β β βββ pages/ # Page components
β β β βββ styles/ # Global styles
β β β βββ types/ # TypeScript definitions
β β β βββ App.tsx # Main application component
β β β βββ i18n.ts # i18next configuration
β β β βββ main.tsx # Application entry point
β β β βββ provider.tsx # HeroUI provider setup
β β βββ tailwind.config.js # Tailwind CSS configuration
β β βββ vite.config.ts # Vite configuration
β β βββ update-heroui.ts # Helper script to update HeroUI packages
β βββ cloudflare-worker/ # Cloudflare Worker for testing API
β βββ src/
β βββ wrangler.jsonc # Cloudflare Worker configuration
β βββ package.json # Worker dependencies
βββ .github/ # GitHub workflows and configuration
βββ .vscode/ # VS Code configuration
βββ template.code-workspace # VS Code workspace configuration
This monorepo uses Turborepo for task orchestration. All scripts can be run from the root directory:
# Start all applications in development mode
yarn dev
# Start all applications with environment variables
yarn dev:env
# Start only the client application
yarn dev:client
# Start only the client application with environment variables
yarn dev:client:env
# Start only the Cloudflare Worker
yarn dev:worker
# Start only the Cloudflare Worker with environment variables
yarn dev:worker:env# Build all applications
yarn build
# Build all applications with environment variables
yarn build:env
# Build only the client application
yarn build:client
# Build only the client with environment variables
yarn build:client:env
# Build only the Cloudflare Worker
yarn build:worker
# Build only the Cloudflare Worker with environment variables
yarn build:worker:env# Run ESLint on all packages
yarn lint
# Run type checking on all packages
yarn type-check
# Run tests on all packages
yarn test
# Clean all build artifacts and caches
yarn clean
# Deploy the Cloudflare Worker
yarn deploy:worker
# Update HeroUI packages (run from client directory)
cd apps/client && yarn update:herouiFor more detailed information, see the Turborepo Guide.
This template includes a GitHub Actions workflow to automatically deploy your application to GitHub Pages. To use this feature:
- Enable GitHub Pages in the repository settings and set the source to GitHub Actions
- Enable GitHub Actions in the repository settings
- Add your Auth0 credentials as GitHub repository secrets:
AUTH0_CLIENT_IDAUTH0_DOMAIN
- Push the changes to your repository
- The application will be deployed automatically on each push to the main branch
This template uses Tailwind CSS 4, which is a utility-first CSS framework. You can customize the styles by modifying the tailwind.config.js file.
HeroUI supports Tailwind CSS 4 out of the box starting from version 2.8.
To clone the project, run the following command:
git clone https://github.com/sctg-development/vite-react-heroui-auth0-template.gitThis project uses Yarn 4 with workspaces. Install all dependencies from the root:
# Enable Yarn 4 if not already done
corepack enable
yarn set version 4.9.2
# Install all dependencies
yarn install# Start all applications with environment variables
yarn dev:env
# Or start individual applications
yarn dev:client:env # Client only
yarn dev:worker:env # Cloudflare Worker onlyThis template uses Turborepo which provides:
- Intelligent caching: Build outputs are cached and shared across team members
- Parallel execution: Tasks run in parallel when possible
- Dependency awareness: Tasks run in the correct order based on dependencies
- Incremental builds: Only rebuild what changed
- Remote caching: Share build caches across your team (optional)
If you're migrating from the previous npm-based setup, here's the command mapping:
| Old npm command | New Yarn command |
|---|---|
cd client && npm run dev:env |
yarn dev:client:env |
cd cloudflare-fake-secured-api && npm run dev:env |
yarn dev:worker:env |
cd client && npm run build:env |
yarn build:client:env |
cd cloudflare-fake-secured-api && npm run build:env |
yarn build:worker:env |
cd client && npm run lint |
yarn lint (runs on all packages) |
In the apps/client/vite.config.ts file, all @heroui packages are manually split into a separate chunk. This is done to reduce the size of the main bundle. You can remove this configuration if you don't want to split the packages.
Contributions are welcome! Please feel free to submit a Pull Request.
This template is primarily licensed under the MIT license.
Exception: Four specific files (site-loading.tsx, language-switch.tsx, vite.config.ts, and auth0.tsx) are licensed under the AGPL-3.0 license as they contain code originating from my other repositories.
The authentication system uses a provider-based architecture that allows you to easily switch between different OAuth providers:
All authentication providers implement a common interface that defines standard authentication methods:
export interface AuthProvider {
// Authentication state
isAuthenticated: boolean;
isLoading: boolean;
user: AuthUser | null;
// Core authentication methods
login(options?: LoginOptions): Promise<void>;
logout(options?: LogoutOptions): Promise<void>;
getAccessToken(options?: TokenOptions): Promise<string | null>;
// Permission handling
hasPermission(permission: string): Promise<boolean>;
// API interaction helpers
getJson(url: string): Promise<any>;
postJson(url: string, data: any): Promise<any>;
deleteJson(url: string): Promise<any>;
}To use the authentication system in your application, wrap your components with the AuthenticationProvider:
import { AuthenticationProvider } from "./authentication";
// For Auth0 (default)
<AuthenticationProvider providerType="auth0">
<App />
</AuthenticationProvider>
// For Dex
<AuthenticationProvider
providerType="dex"
>
<App />
</AuthenticationProvider>To use Auth0, follow these steps:
-
Create an Auth0 Account:
- Go to Auth0 and sign up for a free account.
-
Create a New Application:
- In the Auth0 dashboard, navigate to the "Applications" section.
- Click on "Create Application".
- Choose a name for your application.
- Select "Single Page Web Applications" as the application type.
- Click "Create".
-
Configure Application Settings:
- In the application settings, you will find your
Client IDandDomain. - Set the "Allowed Callback URLs" to
http://localhost:5173(or your development URL). - Set the "Allowed Logout URLs" to
http://localhost:5173(or your development URL). - Set the "Allowed Web Origins" to
http://localhost:5173(or your development URL).
- In the application settings, you will find your
-
Sample settings:
- The settings used by the demo deployment on GitHub Pages are:
- Allowed Callback URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template,https://sctg-development.github.io/vite-react-heroui-auth0-template/ - Allowed Logout URLs:
https://sctg-development.github.io/vite-react-heroui-auth0-template,https://sctg-development.github.io/vite-react-heroui-auth0-template/ - Allowed Web Origins:
https://sctg-development.github.io - On Github repository settings, the
AUTH0_CLIENT_IDsecret is set to the Auth0 client ID and theAUTH0_DOMAINsecret is set to the Auth0 domain. - The full Auth0 configuration screenshot is available here.
- Allowed Callback URLs:
β‘ Small tip: Auth0 takes care of the final
/in the URLs, so you may set it with and without the trailing slash. - The settings used by the demo deployment on GitHub Pages are:
-
Configure API in Auth0:
- Navigate to "APIs" section in the Auth0 dashboard
- Click "Create API"
- Provide a descriptive name (e.g., "My Application API")
- Set the identifier (audience) - typically a URL or URI (e.g.,
https://api.myapp.com) - Configure the signing algorithm (RS256 recommended)
-
Configure API Settings:
- Enable RBAC (Role-Based Access Control) if you need granular permission management
- Define permissions (scopes) that represent specific actions (e.g.,
read:api,write:api) - Configure token settings as needed (expiration, etc.)
- Include permissions in the access token
The client includes a small JWKS caching utility at apps/client/src/authentication/utils/jwks.ts. It provides getLocalJwkSet(domain), which:
- Fetches the JWKS from
https://<domain>/.well-known/jwks.jsonand builds a verifier usingjose.createLocalJWKSet - Caches the result in-memory and in
sessionStorageto reduce network calls - Stores a timestamp (
uat) with the stored JWKS and honors a TTL to expire the cache - Deduplicates concurrent fetches (so parallel callers only trigger one network request)
- Is resilient to
sessionStorageerrors (read/write failures are silently ignored)
The cache TTL defaults to 300 seconds but can be changed with the environment variable AUTH0_CACHE_DURATION_S (set it in your .env file). In code you can use it like this:
import { getLocalJwkSet } from "@/authentication/utils/jwks";
const JWKS = await getLocalJwkSet(import.meta.env.AUTH0_DOMAIN);
const verified = await jwtVerify(token, JWKS, {
issuer: `https://${import.meta.env.AUTH0_DOMAIN}/`,
audience: import.meta.env.AUTH0_AUDIENCE,
});Tip: increase the TTL in stable environments where the JWKS rarely changes; lower it if you expect frequent key rotations.
A small, reusable Router for Cloudflare Workers lives at apps/cloudflare-worker/src/routes/router.ts.
Features:
- Route registration helpers:
router.get,router.post,router.put,router.delete. - Path parameters (e.g.
/api/items/:id) are parsed and injected asrequest.paramsinside handlers. - Optional permission checks: pass a permission string (e.g.
env.READ_PERMISSION) when registering a route; the router validates theAuthorizationheader and uses the workercheckPermissions()helper (seeapps/cloudflare-worker/src/auth0.ts). - Rate limiting support: if your Worker has a
RATE_LIMITERbinding the router will callenv.RATE_LIMITER.limit({ key })and return HTTP 429 when the quota is exceeded. - Standard CORS handling and consistent JSON error responses.
Quick example (see apps/cloudflare-worker/src/routes/index.ts):
import { Router } from "./routes/router";
import { setupRoutes } from "./routes";
export default {
async fetch(request: Request, env: Env) {
const router = new Router(env);
setupRoutes(router, env);
return await router.handleRequest(request, env);
}
} as ExportedHandler<Env>;Unit tests are present in apps/cloudflare-worker/test/router.spec.ts (Vitest). The router now supports Rocket-style paths like /api/get/<user> and catch-all patterns /files/<path..> which translate internally to URLPattern for reliable matching and parameter extraction.
-
Set Environment Variables: Add the following to your
.envfile:AUTHENTICATION_PROVIDER_TYPE=auth0 AUTH0_AUDIENCE=your-api-identifier AUTH0_SCOPE="openid profile email read:api write:api" API_BASE_URL=http://your-api-url.com
-
Sample Configuration: For reference, view the Auth0 API configuration used in the demo deployment.
Dex is an identity service that uses OpenID Connect to drive authentication for other apps. To use Dex as your authentication provider:
-
Setup a Dex Server:
- Install and configure a Dex server following the official documentation
- Configure Dex to support the OAuth 2.0 authorization code flow
-
Register your Application in Dex:
- Add your application to the Dex configuration
- Set the redirect URI to your application's callback URL (e.g.,
http://localhost:5173)
-
Configure the Dex Provider:
- Create a
.envfile with your Dex configuration:
AUTHENTICATION_PROVIDER_TYPE=dex DEX_AUTHORITY=https://your-dex-server.com DEX_CLIENT_ID=your-dex-client-id DEX_SCOPE="openid profile email" DEX_AUDIENCE=https://your-api.com DEX_JWKS_ENDPOINT=https://your-dex-server.com/dex/keys
- Create a
-
Initialize the Dex Provider:
import { AuthenticationProvider } from "./authentication"; <AuthenticationProvider providerType="dex"> <App /> </AuthenticationProvider>;
To add support for additional OAuth providers:
- Create a new provider implementation file in
src/authentication/providers/ - Implement the
AuthProviderinterface - Add the new provider to the
AuthProviderWrapperinsrc/authentication/providers/use-auth.tsx - Add configuration in
src/authentication/auth-root.tsx
The modular design makes it easy to extend the authentication system with new providers while maintaining a consistent API throughout your application.
