This is indeed the most acceptable solution for integrating JWT and devise I’ve found so far, very nice read!
Regarding Rob’s comments, I’d like to share my own limited experience with JWT authentication. For a past project, we decided to delegate the whole user management to a third party identity provider, what left us with an essentially stateless API with no user model at all, and a very thin front-end app that was supposed to simply render and serve views and client-side logic. The authentication flow was roughly the following:
- The user lands on the login page, hosted by the front-end app. His credentials are exchanged for a JWT token signed by the 3rd party IP. The lifetime of this token can be configured, but should be usefully long.
- This “master” token is stored in an HTTP-only cookie, which is sent to an API endpoint via XHR (it took me some time to realize this can be done for CORS requests).
- The API shares the signing key with the IP and can validate the token. It issues a new, short-lived token that should be present in the headers of all subsequent API calls.
- If an API call fails, the front-end app will attempt to obtain a new API token. Using www-authenticate headers for different 401/403 cases may help, since the front end should react differently in the case of API and “master” token expiration.
- To log out the user, both the cookie and the short-lived token are cleared.
This last point leaves the API open to the user for some limited timeframe, which depending on the use case may be a problem. I haven’t sorted it out.
I think this pattern can be applied if the user management is hosted alongside the backend API as well — use Devise to only generate a “master” token and manage access to all other endpoints via a derived, short-lived token. This should, at least to some extent, remove the need to manually blacklist tokens.