Mock auth for a coding interview
Stand up a fake login + protected list flow in 30 minutes - perfect for take-homes and live coding sessions.
Coding interviews and take-home assessments often ask you to build a "small app with login and a protected list view." The catch is that you do not have a backend, and standing one up wastes an hour you do not have. This recipe shows how to use Go REST as the backend, with the user's access token as the "credential", to mock a complete auth flow in 30 minutes.
What we are building
A two-page app:
/login: a form that takes an access token. On submit, we ping the API to verify the token, store it in localStorage, and redirect./users: a protected list of users. If the token is missing or invalid, we redirect to login.
"Auth" is just: do we have a token, and does the API accept it. That is enough to demonstrate routing, protected pages, and API integration.
The login form
The form takes the token directly. In a real app, the user would type a username and password and the server would issue a token; here, we cut out the issuer step and ask for the token directly. Acceptable for an interview, and cleaner than mocking the whole OAuth dance.
function Login() {
const [token, setToken] = useState(localStorage.getItem("token") || "");
const [error, setError] = useState(null);
const navigate = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
// We are not really authenticating; we are just verifying the token works.
const res = await fetch("https://gorest.co.in/public/v2/users?per_page=1", {
headers: { Authorization: `Bearer ${token}` }
});
if (res.status === 401) {
setError("Invalid token");
return;
}
localStorage.setItem("token", token);
navigate("/users");
}
return (
<form onSubmit={handleSubmit}>
<label>
Access token
<input value={token} onChange={e => setToken(e.target.value)} />
</label>
<p>Get one at <a href="https://gorest.co.in/my-account/access-tokens">gorest.co.in</a></p>
{error && <p role="alert">{error}</p>}
<button>Sign in</button>
</form>
);
}
The "verify the token" call is a realGET /users: if it returns 401, the token is wrong. If it returns 200, save the token and proceed. This is exactly how a real bearer-token client validates credentials.
The protected route
A small wrapper component checks for the token and redirects to login if missing. React Router 6 makes this trivial:
function RequireAuth({ children }) {
const token = localStorage.getItem("token");
if (!token) return <Navigate to="/login" replace />;
return children;
}
// App.jsx
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/users" element={
<RequireAuth><UserList /></RequireAuth>
} />
</Routes>
This is not bullet-proof security (anything in localStorage can be read by JS on the page), but it is correct for the interview brief: "users without a session cannot see the protected page."
An api helper
Putting fetch logic in one place makes the rest of the app cleaner. The helper:
- Adds the bearer token automatically.
- Sets JSON headers.
- Treats 401 as "session expired" and redirects to login.
- Throws on other errors.
- Returns null on 204, parsed JSON otherwise.
// api.js
export async function api(path, options = {}) {
const token = localStorage.getItem("token");
const res = await fetch(`https://gorest.co.in/public/v2${path}`, {
...options,
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json",
"Content-Type": "application/json",
...options.headers
}
});
if (res.status === 401) {
localStorage.removeItem("token");
window.location.href = "/login";
return;
}
if (!res.ok) throw new Error(`Got ${res.status}`);
return res.status === 204 ? null : res.json();
}
Use it from a component
function UserList() {
const [users, setUsers] = useState([]);
const navigate = useNavigate();
useEffect(() => {
api("/users").then(setUsers);
}, []);
function signOut() {
localStorage.removeItem("token");
navigate("/login");
}
return (
<div>
<button onClick={signOut}>Sign out</button>
<ul>
{users.map(u => <li key={u.id}>{u.name} ({u.email})</li>)}
</ul>
</div>
);
}
What this demonstrates
If the interviewer is paying attention, they will see:
- Routing. Public vs protected pages, redirect on missing auth.
- State management. Token in localStorage, list in component state.
- API integration. Bearer-token auth, error handling, JSON parsing.
- Defensive coding. 401 → re-auth, error states, loading states.
All in 30 minutes against a real, live API. No backend setup, no Auth0 account, no dependency hell.
Push it further
If you have time left, the layered improvements:
- Sign out. Clear localStorage, redirect to /login. (Already in the code above.)
- Persist last page. On 401-redirect, remember where the user was, send them back after re-auth.
- Loading + error states. Skeleton on first load, toast on API failures.
- Pagination. Next/previous, sync to URL params. (See thepagination recipe.)
- Search. Debounced
?name=filter. (See thedebounce recipe.)
Each addition is 5-10 minutes and demonstrates one more area of competence. Pick whichever the brief asks for; you do not have to do them all.
What this does NOT demonstrate
Be honest with the interviewer about the simplifications:
- No actual user record; the token IS the identity.
- No registration flow; the user gets the token from a sign-in elsewhere.
- No password handling; bearer tokens skip passwords entirely.
- No CSRF / XSS hardening; localStorage is convenient, not the safest place for tokens.
If they want to discuss what production auth would look like, you can talk about HttpOnly cookies, refresh tokens, OAuth flows, secure session storage, and short-lived JWTs. The mock is a starting point, not a final answer.
Keep building
Test loading skeletons with simulated latency
Use ?delay=N to make Go REST take seconds to respond, so you can verify your skeleton screens render without flicker.
Test 401, 422, 429 and 500 handlers
Force any status code with ?force_status= to make sure your error UI never shows blank screens or eats stack traces.
Build a paginated user list in React
A working component that fetches a page, renders rows, and uses X-Pagination-Pages to render Previous/Next correctly.