JWT Refresh Endpoint: 200 vs 401 when no refresh token exists?
I'm building a React app with JWT authentication (access token + refresh token in httpOnly cookie). I have a /refresh endpoint that generates a new access token.
My confusion: What HTTP status should I return when there's no refresh token in the request?
Context:
- My React app calls /refresh on mount to check if user is logged in
- If user never logged in → no refresh token cookie exists
- This is expected behavior, not an error
Two approaches I've seen:
Approach A: Return 401 Unauthorized
if (!refreshToken) {
return res.status(401).json({ success: false });
}
- Pro: Follows HTTP semantics (endpoint requires auth)
- Pro: RESTful - missing auth = 401
- Con: Shows red error in browser Network tab (looks broken to beginners)
- Con: Feels wrong to call "not logged in yet" an error
Approach B: Return 200 OK
if (!refreshToken) {
return res.status(200).json({ success: false, message: "Not logged in" });
}
- Pro: No errors in Network tab
- Pro: "Not logged in" isn't really an error - it's a valid state
- Con: Not following HTTP status code semantics
- Con: Success (200) for a failed auth check feels wrong
My question: What's the industry standard? What do production apps (Auth0, Firebase, etc.) do?
I've checked professional websites and don't see auth errors in their console, so I'm assuming they handle this somehow. Do they:
1. Use 200 responses?
2. Use 401 but handle it gracefully in frontend?
3. Not call /refresh on public pages like /login?
And btw this is like for everything else aswell, if login fails, should i do 401 or 200 with success: false? cause on professional apps i never see it in devtools console.
Would appreciate guidance from experienced developers. Thanks!
My confusion: What HTTP status should I return when there's no refresh token in the request?
Context:
- My React app calls /refresh on mount to check if user is logged in
- If user never logged in → no refresh token cookie exists
- This is expected behavior, not an error
Two approaches I've seen:
Approach A: Return 401 Unauthorized
if (!refreshToken) {
return res.status(401).json({ success: false });
}
- Pro: Follows HTTP semantics (endpoint requires auth)
- Pro: RESTful - missing auth = 401
- Con: Shows red error in browser Network tab (looks broken to beginners)
- Con: Feels wrong to call "not logged in yet" an error
Approach B: Return 200 OK
if (!refreshToken) {
return res.status(200).json({ success: false, message: "Not logged in" });
}
- Pro: No errors in Network tab
- Pro: "Not logged in" isn't really an error - it's a valid state
- Con: Not following HTTP status code semantics
- Con: Success (200) for a failed auth check feels wrong
My question: What's the industry standard? What do production apps (Auth0, Firebase, etc.) do?
I've checked professional websites and don't see auth errors in their console, so I'm assuming they handle this somehow. Do they:
1. Use 200 responses?
2. Use 401 but handle it gracefully in frontend?
3. Not call /refresh on public pages like /login?
And btw this is like for everything else aswell, if login fails, should i do 401 or 200 with success: false? cause on professional apps i never see it in devtools console.
Would appreciate guidance from experienced developers. Thanks!
