Role-based access control helps decide who can do what in your app. It keeps things safe by letting only the right people see or change certain parts.
Role-based access control in Remix
import { json } from '@remix-run/node'; export async function loader({ request }) { const user = await getUserFromRequest(request); if (!user || !user.roles.includes('admin')) { throw new Response('Unauthorized', { status: 401 }); } return json({ data: 'secret data' }); }
Use the loader function to check user roles before loading data.
Throwing a Response with status 401 stops unauthorized users.
import { json } from '@remix-run/node'; export async function loader({ request }) { const user = await getUserFromRequest(request); if (!user || !user.roles.includes('editor')) { throw new Response('Forbidden', { status: 403 }); } return json({ message: 'Welcome editor!' }); }
import { json, redirect } from '@remix-run/node'; export async function loader({ request }) { const user = await getUserFromRequest(request); if (!user) { return redirect('/login'); } if (user.roles.includes('member')) { return json({ content: 'Member content' }); } return json({ content: 'Guest content' }); }
This Remix loader checks if the user is logged in and has the 'admin' role. If not logged in, it redirects to login. If not admin, it shows an error. Otherwise, it allows access.
import { json, redirect } from '@remix-run/node'; // Mock function to get user info from request async function getUserFromRequest(request) { // In real app, check cookies or headers return { id: 1, roles: ['user', 'admin'] }; } export async function loader({ request }) { const user = await getUserFromRequest(request); if (!user) { return redirect('/login'); } if (!user.roles.includes('admin')) { throw new Response('Unauthorized', { status: 401 }); } return json({ message: 'Welcome Admin! You have access.' }); } export default function AdminPage() { return <h1>Admin Page</h1>; }
Always check roles on the server side to keep your app secure.
Use redirects to guide users to login if they are not authenticated.
Throwing a Response with status 401 or 403 helps browsers and tools understand access issues.
Role-based access control limits what users can do based on their roles.
Check roles in Remix loader functions to protect data and pages.
Use redirects and error responses to handle unauthorized access smoothly.
Practice
Solution
Step 1: Understand RBAC concept
RBAC means users get permissions based on their roles, like admin or guest.Step 2: Identify RBAC purpose in Remix
In Remix, RBAC controls what pages or data a user can access depending on their role.Final Answer:
To limit user actions based on their assigned roles -> Option DQuick Check:
RBAC = limit actions by role [OK]
- Confusing RBAC with performance optimization
- Thinking RBAC is for styling UI
- Mixing RBAC with password security
Solution
Step 1: Identify async user fetching
Using await with getUser(request) is correct to get user info asynchronously.Step 2: Check role comparison and response
const user = await getUser(request); if (user.role !== 'admin') throw new Response('Forbidden', { status: 403 }); correctly compares role with !== and throws 403 response if not admin.Final Answer:
const user = await getUser(request); if (user.role !== 'admin') throw new Response('Forbidden', { status: 403 }); -> Option AQuick Check:
Async fetch + role check + 403 response = const user = await getUser(request); if (user.role !== 'admin') throw new Response('Forbidden', { status: 403 }); [OK]
- Forgetting await when calling async functions
- Using assignment (=) instead of comparison (== or !==)
- Returning wrong responses on unauthorized access
export async function loader({ request }) {
const user = await getUser(request);
if (user.role !== 'admin') {
return redirect('/login');
}
return json({ message: 'Welcome Admin' });
}Solution
Step 1: Analyze role check condition
The code redirects if user.role is not 'admin'. A 'user' role is not 'admin'.Step 2: Determine loader behavior for 'user' role
Since role is 'user', the redirect('/login') runs, sending user to login page.Final Answer:
The user is redirected to '/login' page -> Option BQuick Check:
Non-admin role triggers redirect [OK]
- Thinking user sees admin message
- Assuming 403 error instead of redirect
- Ignoring redirect and expecting blank page
export async function loader({ request }) {
const user = getUser(request);
if (user.role !== 'admin') {
throw new Response('Unauthorized', { status: 401 });
}
return json({ data: 'Secret Data' });
}Solution
Step 1: Check async function usage
getUser is likely async, so missing await means user is a Promise, not user data.Step 2: Understand impact of missing await
Without await, user.role is undefined, causing runtime error or wrong behavior.Final Answer:
Missing await before getUser causing user to be a Promise -> Option AQuick Check:
Async call missing await = Missing await before getUser causing user to be a Promise [OK]
- Ignoring missing await and expecting user object
- Confusing 401 and 403 status codes
- Thinking throwing Response is invalid in loader
export async function loader({ request }) {
const user = await getUser(request);
// Fill in the role check
}Solution
Step 1: Understand role conditions for access
Users allowed are 'admin' or 'manager', so others must be redirected.Step 2: Choose correct logical operator
Use AND (&&) with !== to check if user is neither 'admin' nor 'manager'.Step 3: Verify redirect logic
If user role is not admin and not manager, redirect to '/not-authorized'.Final Answer:
if (user.role !== 'admin' && user.role !== 'manager') return redirect('/not-authorized'); -> Option CQuick Check:
Redirect if not admin AND not manager = if (user.role !== 'admin' && user.role !== 'manager') return redirect('/not-authorized'); [OK]
- Using || instead of && causing wrong redirects
- Checking for both roles with && equality (impossible)
- Redirecting allowed roles instead of others
Ready to prove it?
Pass the test and this pattern moves unknown
