A comprehensive Next.js boilerplate for building customer/partner portals with Firebase authentication, dynamic custom claims system, and admin management capabilities.
- Next.js 15 with App Router and TypeScript
- Firebase Authentication with dynamic custom claims
- Dynamic Custom Claims System - Easily configurable user attributes
- Role-based Access Control (Admin, Customer, Partner)
- Magic Link Authentication for existing users
- Admin Interface for user management with dynamic forms
- Shadcn/ui Components with Tailwind CSS
- API-First Architecture with secure backend endpoints
- ESLint & Prettier for code quality
src/
βββ app/ # Next.js App Router pages
β βββ (auth)/ # Authentication pages
β β βββ login/ # Login page
β β βββ verify-magic-link/ # Magic link verification
β βββ (protected)/ # Protected pages
β β βββ dashboard/ # User dashboard
β β βββ admin/ # Admin interface
β β βββ users/ # User management
β βββ api/ # API routes
β βββ auth/ # Authentication endpoints
β βββ admin/ # Admin API endpoints
βββ components/ # React components
β βββ ui/ # Shadcn/ui components
β βββ admin/ # Admin-specific components
β β βββ DynamicCustomClaimsFields.tsx
β β βββ DynamicCustomClaimsTableCells.tsx
β βββ user/ # User-specific components
β βββ DynamicUserAccountInfo.tsx
βββ config/ # Application configuration
β βββ appConfig.ts # Dynamic custom claims configuration
βββ lib/ # Utility libraries
β βββ firebase/ # Firebase configuration
β β βββ client.ts # Firebase client SDK
β β βββ admin.ts # Firebase Admin SDK
β βββ auth.ts # Authentication utilities
β βββ apiAuth.ts # API authentication & authorization
β βββ utils/ # Utility functions
β βββ customClaims.ts # Custom claims helpers
βββ hooks/ # Custom React hooks
β βββ useAuth.ts # Authentication context & hook
βββ middleware.ts # Next.js middleware for route protection
npm installThe Firebase configuration is already set up with the project credentials. For admin operations, you'll need to:
- Go to Firebase Console
- Navigate to Project Settings > Service Accounts
- Generate a new private key
- Update the
.env.localfile with the service account credentials:
FIREBASE_CLIENT_EMAIL=your-service-account-email
FIREBASE_PRIVATE_KEY="your-private-key-here"- In Firebase Console, go to Authentication > Sign-in method
- Enable Email/Password provider
- Configure authorized domains if needed
npm run devThis boilerplate features a powerful dynamic custom claims system that allows you to easily add, modify, and manage user attributes without changing code throughout the application.
To add a new custom claim, update two places in src/config/appConfig.ts:
export const CUSTOM_CLAIMS_KEYS = {
ROLE: 'role',
ORGANIZATION_ID: 'organizationId',
EXTERNAL_ID: 'externalId',
// Add your new claim here
DEPARTMENT: 'department',
} as const;export const CUSTOM_CLAIMS_CONFIG: CustomClaimFieldConfig[] = [
// ... existing configurations ...
{
key: CUSTOM_CLAIMS_KEYS.DEPARTMENT,
label: 'Department',
type: 'select',
required: false,
options: [
{ value: '', label: 'No Department' },
{ value: 'engineering', label: 'Engineering' },
{ value: 'marketing', label: 'Marketing' },
{ value: 'sales', label: 'Sales' }
],
description: 'User department'
}
];text- Text input fieldemail- Email input with validationnumber- Number input fieldselect- Dropdown with predefined options
Once configured, the new custom claim will automatically appear in:
- β Admin user management forms
- β User account displays
- β API validation
- β Firebase custom claims storage
- β Authentication context
- Root Route (
/): Automatically redirects to/dashboardif authenticated, or/loginif not - Login Page (
/login): Functional login form with email/password - Dashboard (
/dashboard): Protected dashboard showing user info and dynamic custom claims - Admin Users (
/admin/users): Complete user management with dynamic forms - Magic Link Verification (
/verify-magic-link): Handles custom token authentication
- Global State Management:
useAuth()hook provides user state across the app - Automatic Redirects: Pages automatically redirect based on authentication state
- Loading States: Proper loading indicators during authentication checks
- Error Handling: Comprehensive error handling for auth operations
- Dynamic Claims: Real-time custom claims management
- Use the admin interface at
/admin/usersto generate magic links - Or use the API endpoint:
POST /api/auth/generate-magic-link - Requires admin authentication and either
emailoruidin request body - Returns a magic link URL that can be used for authentication
POST /api/auth/login- Login validation endpointPOST /api/auth/logout- Logout processing endpointPOST /api/auth/generate-magic-link- Magic link generation (admin only)GET /api/admin/users- List all users (admin only)POST /api/admin/users- Create new user (admin only)PUT /api/admin/users/[userId]- Update user (admin only)
npm run dev- Start development servernpm run build- Build for productionnpm run start- Start production servernpm run lint- Run ESLintnpm run lint:fix- Fix ESLint issues
- Automatic Routing: Root route redirects based on authentication state
- Context Management: Global authentication state via React Context
- Username/Password Login: Standard Firebase authentication via client SDK
- Magic Links: Admin-generated custom tokens for existing users
- Dynamic Custom Claims: Flexible user attribute system
- API Authorization: Bearer token verification with role-based protection
- Route Protection: Middleware and client-side guards for protected routes
- Firebase ID Token Verification: All API routes verify Firebase tokens
- Role-Based Access Control: Admin, Customer, Partner roles with proper checks
- Organization Isolation: Users can only access their organization's data
- Protected API Routes:
withAuthwrapper for easy API protection - Client-Side Route Guards: Automatic redirects for unauthorized access
- Dynamic Validation: Custom claims validation based on configuration
- Next.js 15 - React framework with App Router
- TypeScript - Type safety and better DX
- Firebase - Authentication and backend services
- Tailwind CSS - Utility-first CSS framework
- Shadcn/ui - Modern UI component library
- ESLint & Prettier - Code quality and formatting
This is a boilerplate project. Follow the existing patterns and conventions when adding new features. The dynamic custom claims system makes it easy to extend user attributes without modifying core components.
MIT License - feel free to use this boilerplate for your projects.
This boiler-plate ships with an opinionated yet fully automated Google Cloud Build + Cloud Run pipeline.
The build recipe lives in cloudbuild.yaml and the helper script scripts/deploy.sh is just a convenience wrapper around gcloud builds submit.
- Create / select a GCP project (e.g.
customer-portals-446012). - Enable APIs: Cloud Build, Cloud Run, Artifact Registry, Secret Manager.
- Create secrets in Secret Manager β they must keep the names below (version
latestis used automatically):FIREBASE_SERVICE_ACCOUNT_JSONJWT_SECRETHUBSPOT_API_KEY(exposed to the build asHUBSPOT_ACCESS_TOKEN)NEXT_PUBLIC_FIREBASE_API_KEYNEXT_PUBLIC_FIREBASE_AUTH_DOMAINNEXT_PUBLIC_FIREBASE_PROJECT_IDNEXT_PUBLIC_FIREBASE_APP_ID
- Grant IAM to the Cloud Build service account
PROJECT_NUMBER@cloudbuild.gserviceaccount.com:roles/secretmanager.secretAccessorroles/run.admin
- Run
./scripts/deploy.sh # builds, pushes and deploys to Cloud Run (region europe-west10)
- In Cloud Console β Cloud Build β Triggers β Manage repositories β Connect repository
- Authorise the Google Cloud Build GitHub app if prompted.
- Pick
montamo/next-firebase-boilerplate(or your fork).
- Create Trigger
- Repository: the repo you just connected.
- Event: "Push to a branch" (e.g.
^main$). - Build config file:
cloudbuild.yaml(leave the substitutions field empty β defaults are hard-coded in the file). - Service account: default Cloud Build SA (has the roles from step 4 above).
- Push to
mainβ Cloud Build will: pull secrets β build image β push to Artifact Registry β deploy to Cloud Run.
The only project-specific values that are hard-coded in cloudbuild.yaml are:
| What | Current Value | How to change |
|---|---|---|
| Service name (Cloud Run) | next-firebase-boilerplate |
search-&-replace the string in cloudbuild.yaml (3 occurrences) |
| Region | europe-west10 |
change the --region= flag in the deploy step |
| Extra build args (Firebase bucket & sender ID) | montamo-portals-v2.firebasestorage.app, 448992902846 |
edit the corresponding --build-arg values |
Everything elseβDockerfile, build logic, Secret namesβcan stay the same as long as you recreate the Secret Manager entries in the new project.
Tip: if you prefer zero code edits, you can re-introduce Cloud Build substitutions (e.g.
_SERVICE_NAME,_REGION) and reference them in the file β just make sure every substitution is actually used or Cloud Build will throw an error.
# (optional) override service name or region on the fly
SERVICE_NAME=my-service REGION=europe-north1 \
gcloud builds submit --config cloudbuild.yaml \
--substitutions=_SERVICE_NAME=$SERVICE_NAME,_REGION=$REGION