Knock: Does this support automatic refresh tokens?

Created on 8 May 2016  路  18Comments  路  Source: nsarno/knock

I'm new to JWT, but it looks better than just using the same API token forever.

I know the token expires after a day (by default), but does that mean the user will have to re-login again?

  • this could lead to some un-desired behavior if someone's token is about to expire when they are in the middle of work.

How does auth work between multiple devices? (esp in the case of a refresh token)

Most helpful comment

For those that stumble on this thread in the future, here's another way to send back a token on every request (skip this action on controllers that don't need to be authenticated)

class ApplicationController < ActionController::API
  include Knock::Authenticable
  before_action :authenticate_and_set_token

  private

    def authenticate_and_set_token
      authenticate_user
      headers['Authorization'] = Knock::AuthToken.new(payload: current_user.to_token_payload).token
    end
end

If you haven't set up a User.to_token_payload method, you can just use payload: { sub: current_user.id } inside Knock::AuthToken.new to return a default token.

All 18 comments

Does this support automatic refresh tokens?

No, Knock does not support refresh tokens out of the box. I never felt the need for it and I believe that adding long-lived refresh tokens would add extra security concerns and diminish the simplicity inherent to using JWT.

I know the token expires after a day (by default), but does that mean the user will have to re-login again?

Yes, the user will have to login again after a token expires.

this could lead to some un-desired behavior if someone's token is about to expire when they are in the middle of work.

I understand the reason why you would want to implement something like a refresh token. Although I think there's other ways to address this kind of issues. Consider sliding sessions for example.

With sliding sessions, you would send a new short-lived token with every authenticated action made by the user. As long as the user is active he will stay authenticated. If the user sends an expired token, it means he has been inactive for a while.

How does auth work between multiple devices? (esp in the case of a refresh token)

There's nothing special about multiple devices authentication. I don't have an answer in the case of refresh tokens in particular, because I did not implement it.

I hope this answers your questions. I'm not saying I'm entirely close-minded to refresh tokens if it's something that becomes unavoidable in the future. But I don't believe it is the case at the moment.

Documenting sliding sessions in the README and making sure it is easily implementable would be a good actionable for this issue.

Thanks for your feedback 馃憤

Yeah, so thanks for all of this! this is all wonderful information!

I never considered that implementation of sliding sessions, and I think that would be the ideal way to do things.
For sliding sessions, does the server then have to keep track of the session (making auth no longer stateless?), or is that something auth0 could provide?

Right now, for my app, I'm managing tokens myself, and have been looking at auth0 + jwt as an alternative, I really want to understand all this before dumping a bunch of time in to implementing (which would still probably be less time than hacking on Devise)

No, this would still be entirely stateless. No need to keep the token server-side.

  • The server adds a token field to every response.

e.g.

{
  "resource": { ... },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
}
  • The client needs to replace the token after every authenticated request with the one from the response.

If the tokens are generated with a 24h expiration, then the user will only need to re-enter his password after being inactive for a day.

I believe the beauty of JWT is the simplicity both of the concept and implementation. I encourage you to try it on a small dummy application to understand it better if you need to.

If you can afford it, Auth0 is also a great solution if you want to decouple authentication entirely from your app.

Happy I could help! :)

Closing this for now, but feel free to comment back on this issue to tell us how you went with this! 馃憤

I'm new to JWT but most of this is making sense to me. The only concept I'd love to see some example code for is in regards to creating a new sliding session token with Knock. I'm comfortable with the concept of sliding sessions. However, I'm unclear where Knock would typically tie into the Rails response cycle and how it can create a new sliding session token to send back to the client. I see in the README that we can modify the token payload but I'm not sure if that's how sliding session tokens are handled. Any more clarity and documentation covering sliding sessions would be greatly appreciated. Thanks!

@nsarno Hi. I don't understand how your project can be suitable for mobile devices without long lived refresh token...

@freemind- I think when you refresh, you're supposed to get a new refresh token that then has a new expiration

@freemind- what is the specificity around mobile devices?

I don't get it, how can I get new fresh token on request with valid authorization bearer token? How should response look like? I need just endpoint which will give me new fresh auth token. Nothing more ;)

Okay if anyone has problem like I had, there is solution. Just invoke:
token = Knock::AuthToken.new(payload: { sub: current_user.id }).token

with before_action :authenticate_user to set current_user.

Hope this helps, for me it was hard to find this method, so I think it can help someone.

@enterteg can you post a sample controller to exhibit how this works in context? I'm very interested in how you've implemented this. Thanks.

Sure, I don't know if I did it properly, if I did something wrong, please correct me ;)

class TokenActionsController < ApplicationController`
  before_action :authenticate_user

  def validate_token
    # if token is working method returns 200 OK
    # if not - authenticate_user returns 401 - not authenticated
    render status: 200
  end

  def refresh_token
    token = Knock::AuthToken.new(payload: { sub: current_user.id }).token
    render json: { jwt: token }
  end
end

For those that stumble on this thread in the future, here's another way to send back a token on every request (skip this action on controllers that don't need to be authenticated)

class ApplicationController < ActionController::API
  include Knock::Authenticable
  before_action :authenticate_and_set_token

  private

    def authenticate_and_set_token
      authenticate_user
      headers['Authorization'] = Knock::AuthToken.new(payload: current_user.to_token_payload).token
    end
end

If you haven't set up a User.to_token_payload method, you can just use payload: { sub: current_user.id } inside Knock::AuthToken.new to return a default token.

I was able to get @aust1nz's auto refreshing code working, but with a few tweaks. I'm checking if the request includes the Authorization: Bearer Header, and if the token was valid, before refreshing the token. this way, routes that don't require authentication will still work as normal. I then continue to call before_action :authenticate_user for any specific routes that require authorization.

class ApplicationController < ActionController::API
  include Knock::Authenticable

  before_action :refresh_bearer_auth_header, if: :bearer_auth_header_present

  private

    def bearer_auth_header_present
      request.env["HTTP_AUTHORIZATION"] =~ /Bearer/
    end

    def refresh_bearer_auth_header
      authenticate_user

      if current_user
        headers['Authorization'] = Knock::AuthToken.new(payload: { sub: current_user.id }).token
      end
    end
end

What's not clear to me is how to handle the case where the user is inactive for more than 24 hours. Sliding sessions don't prevent that scenario. At that point I would like the front-end code to clear the token and log the user out but it's tricky to know if the front-end is getting 401 Unauthorized because of an expired token or because the user is actually trying to access a forbidden resource.

Put another way, I don't want to accidentally log out the user if he simply tries accessing a forbidden resource.

Is a common solution then to also keep track of the time the token was stored on the client side and clear it if more than 24 hours passes?

Edit: Another scenario that can happen is a server deploy happens and invalidates all existing tokens. How should that case be handled on the front-end?

@mhluska : can you tell us how you managed to do it?

Two notes

@catalinux at the time of writing I was using a React frontend and worked around this by storing a timestamp in localstorage. I've since switched to Ember and it seems ember-simple-auth-token handles that complexity for me.

Check my fork, I just implemented a secure refresh token for knock
https://github.com/ShiningBomb/knock

@ShiningBomb hey this looks really good - what are the limitations or security concerns with this? From the client perspective, where would you store refresh token? cookies?

Was this page helpful?
0 / 5 - 0 ratings