User gets logged out after an hour of inactivity, whether or not :rememberable is used
unikitty37 opened this issue · 4 comments
I'm trying to write a Rails backend that uses devise-jwt to manage logins from a Vue frontend. However, after an incredibly short period of inactivity, the user is told they are logged out.
I would like to configure it so that the user can be inactive for 24 hours before being automatically logged out if they did not check "Remember Me", and for 6 weeks if they did. When the app was all-Rails, and just using Devise, the code I had behaved correctly. However, it does not seem to work as expected with devise-jwt, and I'm not sure if this is something I've done wrong or a limitation of the way JWT works.
I have the following in my Devise initialiser:
# ==> Configuration for :rememberable
config.remember_for = 6.weeks
config.expire_all_remember_me_on_sign_out = true
config.extend_remember_period = true
and in my User model:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:lockable, :confirmable,
:jwt_authenticatable, jwt_revocation_strategy: JwtDenylist
I do not have :timeoutable
enabled (I did originally, and thought that was the cause of the problem — but I have just been logged out after about an hour of inactivity).
The JSON being passed on login is
{
"user": {
"email": "username@example.com",
"password": "superdupersecret",
"remember_me": 1
}
}
Expected behavior
The user is not logged out unless one of the following is true:
- 24 hours have elapsed since the last API call made with that token, and they logged in without passing
remember_me: 1
. - 6 weeks have elapsed since the last API call made with that token, and they passed
remember_me: 1
when logging in. - The user explicitly clicked the "log out" button.
Actual behavior
After about one hour since the last access, the user is reported as being logged out.
Steps to Reproduce the Problem
- Configure devise-jwt with the options above.
- Create a user with email
username@example.com
and passwordsuperdupersecret
. - Make a POST to the route that points at
sessions#create
, passing the JSON above. - Verify the user is logged in.
- Wait a couple of hours.
Debugging information
Provide following information. Please, format pasted output as code. Feel free to remove the secret key value.
- Version of
devise-jwt
in use: 0.8.0 - Version of
rails
in use: 6.0.3.2 - Version of
warden-jwt_auth
in use: 0.5.0 - Output of
Devise::JWT.config
=> #<Dry::Configurable::Config values={:secret=>"REDACTED", :expiration_time=>3600, :dispatch_requests=>[], :revocation_requests=>[], :aud_header=>"JWT_AUD", :request_formats=>{}}>
- Output of
Warden::JWTAuth.config
=> #<Dry::Configurable::Config values={:secret=>"REDACTED", :algorithm=>"HS256", :expiration_time=>3600, :aud_header=>"JWT_AUD", :mappings=>{:user=>User (call 'User.connection' to establish a connection)}, :dispatch_requests=>[["POST", /^\/api\/v1\/users\/login$/], ["POST", /^\/api\/v1\/users$/]], :revocation_requests=>[["DELETE", /^\/api\/v1\/users\/logout$/]], :revocation_strategies=>{:user=>JwtDenylist (call 'JwtDenylist.connection' to establish a connection)}}>
- Output of
Devise.mappings
=> {:user=>
#<Devise::Mapping:0x000055ace4fb5658
@class_name="User",
@controllers=
{:registrations=>"registrations",
:sessions=>"sessions",
:passwords=>"devise/passwords",
:confirmations=>"devise/confirmations",
:unlocks=>"devise/unlocks"},
@failure_app=Devise::FailureApp,
@format=nil,
@klass=#<Devise::Getter:0x000055ace4fb50b8 @name="User">,
@modules=[:database_authenticatable, :rememberable, :recoverable, :registerable, :validatable, :confirmable, :lockable, :trackable, :jwt_authenticatable],
@path="users",
@path_names=
{:registration=>"",
:new=>"new",
:edit=>"edit",
:sign_in=>"login",
:sign_out=>"logout",
:password=>"password",
:sign_up=>"sign_up",
:cancel=>"cancel",
:confirmation=>"confirmation",
:unlock=>"unlock"},
@path_prefix="/api/v1",
@router_name=nil,
@routes=[:session, :password, :registration, :confirmation, :unlock],
@scoped_path="users",
@sign_out_via=:delete,
@singular=:user,
@used_helpers=[:session, :password, :registration, :confirmation, :unlock],
@used_routes=[:session, :password, :registration, :confirmation, :unlock]>}
You can configure the expiration time of your tokens:
https://github.com/waiting-for-dev/devise-jwt#expiration_time
Rememberable works with cookies, so it makes no sense in the context of JWT tokens.
If you need more persistence with tokens, you're probably better off with something like OAuth.
Thanks — I think the problem is that I don't want users (myself included) having to log in all the time, but I had got the impression that setting the expiration time to something in the order of weeks was a security risk.
The only other way I can think of would be storing the user's credentials in the browser's local storage and retrying the login with the saved credentials if a 401 is returned, but that seems even less secure…
I don't really want to implement OAuth here, as it seems very complicated for a simple site (plus, sooner or later, someone will ask for Facebook support — and I don't do Facebook :)
Incidentally, is the expiration time extendable? As I was using config.extend_remember_period = true
, I would want to have the token expire after it hasn't been used for a certain amount of time, rather than a fixed period from its inception…
No, it isn't. The expiration time is hardwired in the token's payload.