FE Auth Error Handling
Written by Rohman Beny Riyanto
Every endpoint that requires a token can fail auth in four genuinely different ways, and the FE should treat each one differently. They're distinct response_code values on purpose - don't treat them all as one generic "unauthorized":
| Situation | response_code | error (category) | What FE should do |
|---|---|---|---|
| No token / garbage / tampered / revoked token | 083 | AUTH_ERROR | Clear stored token, send to login. |
| Expired token | 423 | AUTH_ERROR | Try the refresh-token flow first (unified-refresh); only fall back to login if refresh also fails. |
| Valid token, wrong role for this endpoint | 424 | AUTH_ERROR | Do not log the user out or clear the token - it's still good for whatever it was issued for. Show "you don't have access to this" and route back to wherever this role's normal experience lives. |
| Valid token, correct role, but this specific permission isn't granted | 403 | AUTH_ERROR | Show a permission-denied message (e.g. "ask an admin to grant you role:create"). Same "don't log out" rule as 424 - the session itself is fine. |
Full response shape and every other code is on the Response Codes reference page - this table only exists to explain when each one fires and what to do about it, which the auto-generated reference (necessarily) can't say.
Why 424 and 403 both mean "no", but shouldn't be handled the same
They look similar (both "you're logged in but can't do this") but mean different things, and conflating them produces a worse error message than either one alone:
424(wrong role) means this account, in principle, was never going to be allowed near this feature - acustomeraccount has no path to ever call an admin endpoint, no matter what gets granted later.403(permission denied) means the account is exactly the right kind of actor, it's just missing a specific grant that an admin could hand out right now via the permission-grant endpoints.
Practically: 424 is "wrong app section for this account," 403 is "right section, ask someone to unlock this specific button."
Quick reference for a fetch/axios interceptor
switch (response.data.response_code) {
case '423': // expired - try refresh, then retry the original request
return refreshTokenAndRetry(originalRequest)
case '083': // invalid/tampered/revoked - token is genuinely no good
clearStoredToken()
redirectToLogin()
break
case '424': // valid token, wrong role for this endpoint
showAccessDeniedForRole()
break
case '403': // valid token + role, specific permission not granted
showPermissionDeniedMessage()
break
}