Skip to content

Sign out (logout) always returns status 401 with devise 5 #292

@araccaine

Description

@araccaine

I am not sure if this is an issue with devise or devise-jwt, so any redirection to the right people is highly appreciated.

Steps to reproduce

  • Use devise 5.x
  • Send a request to the sign-out endpoint with a valid JWT

Expected

  • Sign out the user, i.e. revoke the token with whatever revocation strategy is defined
  • Endpoint returns status 204 (no content) or similar to tell the client "logout was successful"

Actual behavior

  • Token is revoked (this is good and expected)
  • Endpoint returns 401 unauthorized: this is unexpected

Why is this an issue

By always sending 401 the client cannot distinguish between these two situations:

  1. Client requested a sign-out but used an invalid token
  2. Client requested a sign-out and used a valid token

Regarding devise v4 vs. v5:

The difference between devise v4 and v5 is that they now correctly send 401 if the user is deemed as "already signed out". Note that the PR which added this is from 2018 but was somehow merged in v5 only, see heartcombo/devise#4878 (comment)

If you look at their sessions_controller#L61 you can see that on sign-out they look in the current session and if there is no user, they assume the user was already logged out and send a 401.
My understanding is: The problem is that devise does not know that devise-jwt recognizes the current request as authorized because no session is used at all.

I am unsure on how to deal with this situation. Is this a misconfiguration on my side, a bug or edge case with devise or devise-jwt (or even warden?) or something that I just have to deal with? Maybe the question is also whether this is a simple inconvenience with the response status codes or if this is a deeper issue with (security) implications on my side?


Edit:

This is the log file when signing out with a valid JWT:

Started DELETE "/logout" for 127.0.0.1 at 2026-04-20 09:28:04 +0200
Processing by Devise::SessionsController#destroy as JSON
Filter chain halted as :verify_signed_out_user rendered or redirected
Completed 401 Unauthorized in 0ms (ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.0ms)


  User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 123 LIMIT 1
  User Update (6.8ms)  UPDATE "users" SET "jti" = 'xx-xx' WHERE "users"."id" = 123

And without valid JWT:

Started DELETE "/logout" for 127.0.0.1 at 2026-04-20 09:28:49 +0200
Processing by Devise::SessionsController#destroy as JSON
Filter chain halted as :verify_signed_out_user rendered or redirected
Completed 401 Unauthorized in 0ms (ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.0ms)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions