A Next.js app that demonstrates Role-Based Access Control (RBAC) with Firebase Authentication custom claims and Firestore Security Rules.
The UI lets any authenticated user read posts. Only users with the admin
role can create posts. Admin privileges are enforced by Firestore rules (not by client-side checks), so unauthorized writes fail securely.
- Read posts from Firestore for all users (and guests see login screen)
- Email/Password authentication
- Admin-only create access, enforced via Firestore rules using custom claims
- Minimal, modern UI using Tailwind CSS
- Next.js
- Firebase (Auth, Firestore, Admin)
- Tailwind CSS
- Users authenticate with Firebase Auth (Email/Password).
- An admin role is attached to a user via a custom claim:
request.auth.token.role == 'admin'
. - Firestore Security Rules check the claim and allow only admins to write to the
posts
collection. Everyone can read posts.
This app intentionally catches write errors client-side and shows a friendly message if the user lacks permissions.
- Node.js 18+ and npm
- A Firebase project with:
- Authentication (Email/Password) enabled
- Firestore database created
- Go to the Firebase Console → create a project.
- Add a Web app to obtain your config values.
- In Authentication → Sign-in method, enable Email/Password.
- Create at least one test user (email + password) to sign in.
Set rules so anyone can read posts, but only admins can write:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /posts/{postId} {
// Anyone logged in can read
allow read: if request.auth != null;
// Only admins can write
allow write: if request.auth.token.role == "admin";
}
}
}
Publish the rules.
Create a .env.local
in the project root with your Firebase Web app config:
NEXT_PUBLIC_FIREBASE_API_KEY=...
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=...
NEXT_PUBLIC_FIREBASE_PROJECT_ID=...
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=...
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=...
NEXT_PUBLIC_FIREBASE_APP_ID=...
These are read in src/lib/firebase.js
during client initialization.
npm install
npm run dev
Open http://localhost:3000
, sign in with your test user, and try adding a post.
- Non-admins will see an error alert on write due to security rules.
- Admins (with the custom claim) can add posts successfully.
This repo includes a small Node script to set a user’s custom claim using the Firebase Admin SDK.
Files: firebase-admin-scripts/assignRole.js
and a placeholder for your service account key.
Steps:
-
In the Firebase Console → Project settings → Service accounts → Generate new private key. Save the JSON into
firebase-admin-scripts/
and update the filename inassignRole.js
if needed. -
In the same folder, create a
.env
with your target user’s UID:UID=your-firebase-auth-user-uid
-
From the project root, run:
cd firebase-admin-scripts npm install node assignRole.js
If successful, it will print something like:
Assigned role "admin" to user <UID>
Sign the user out and back in on the client so the updated custom claims refresh in the ID token.
Tip: You can get a user’s UID from Firebase Console → Authentication → Users.
src/lib/firebase.js
: Initializes Firebase app, exportsauth
anddb
.src/app/page.js
: UI for login/logout, list posts, and add post.firebase-admin-scripts/assignRole.js
: Setsrole: "admin"
claim for a given UID.
- Collection:
posts
- Document fields:
{ text: string }
- Document fields:
This demo intentionally keeps the schema minimal.
-
Write attempts fail even for admin:
- Ensure the user’s ID token has refreshed after assigning the claim. Sign out/in.
- Verify Firestore rules are published and include the
request.auth.token.role == 'admin'
check. - Confirm
.env.local
is correctly set and the app is pointing to the right Firebase project.
-
Cannot sign in:
- Ensure Email/Password is enabled and the user exists.
npm run dev
: Start Next.js dev servernpm run build
: Production buildnpm run start
: Start production servernpm run lint
: Run ESLint