Further to an old issue requesting enhancement, it seems it may have got more complex.
https://github.com/nuxt-community/auth-module/issues/739
In the LocalScheme, the token sync function looks for a token accessible in the storage. I am looking to keep the JWT token very secure and have it in an HTTP only cookie
Configurable option in Local Scheme to skip the token check when calling fetchUser so that we can simply make the request to the user endpoint without knowing whether a token exists or not.
The cookie could be accessible from javascript, but it'd be better practice if not. Especially with a refresh token scheme I have in place.
I'm hoping a simple option to disable checking for the existing token in this scheme would be sufficient for my needs and to allow for the most secure JWT storage techniques.
So I did a very simple update in my runtime by changing the check method just to return that I have a valid token so the fetch user function runs and it worked as expected. Why is it important for this scheme to actually be storing the token and not just the user credentials? Is it necessary to be checking for the JWT token before making the request or is this just an optimisation? Would it be safer to add a parameter to the fetchUser method itself so thatI can skip this check? e.g. fetchUser(endpoint, skipTokenCheck = false).
I have also just tried to make my own scheme which extends the local scheme and then just trying to override the one function check so that it always returns that we have a valid token (optimistic assumption). But I get a module build error with the fs dependency not found, so I'm wondering if I'm extending it right, but I'll look into this as a solution for me for now, while this feature is considered for the default LocalScheme.
Hi @silverbackdan! I believe you can achieve the same goal without the need of modifing the scheme. All you need to do is set required: false in token option. (I'm assuming you're using auth v5). This way a flag will be stored instead of the token.
Why is it important for this scheme to actually be storing the token and not just the user credentials?
We don't store the user credentials. After page refresh, it's needed to re-fetch the user data. Most of system need an Authorization header (which is the access token) in order to this request be successful. As Authorization header is also lost after page refresh, we store the access token to be able to set it again.
Is it necessary to be checking for the JWT token before making the request or is this just an optimisation?
Our system needs to know when the token is available or not in order to work properly. That's why I recommend the approach above if you don't need the token, which will use a flag instead.
Thanks for this! I really appreciate such fast and wonderfully detailed replies.
I am using required:false but at some point the storage is empty while the cookie is still there and valid. So I came across this issue when trying to then manually call fetchUser which runs the check on the token. I'll see if I can find the part which inserts a placeholder token and if I can reuse that somehow to get that in the storage again so I can re-authenticate my user.
I'm not 100% sure if it's just in development I'm getting into the state where the user credentials are no longer there, but either way, if I call this.$auth.setUserToken(true) to set a dummy token in there and then call fetch user again it works.
Have you any ideas why this may happen?
I worry that if I just enforce doing this whenever I'm not a user could cause a delay for every visitor that is genuinely not logged into a page
I'm not sure if I understand what issue you are facing here. Can you show me an example where you're using the fetchUser? Also I would appreciate if you could show me your config :)
You can also take a look at the cookie scheme. You may want to use it instead of local scheme.
I do completely understand I haven't made the evolving issue clear.
I think I'll go through and work out the scenarios where I will need to re-check if the user exists before performing actions based on their state. That way the delay is only there if the user authentication is actually required. If a check needs to take place or we get a 401 response.
I think I'll look at the cookie scheme as well as it seems like storing the login state like this will be more stable, and it could be simply that my local storage is being cleared when my cookie isn't for some reason. As the token is in an HTTP only cookie as well, it'd make sense for me to store the login state in the same place.
If I come across further issues and can make a clear case I'll raise another issue.
Huge thanks for your time and prompt responses!
Ok, good luck then! I'm glad to help :)
Can I ask one quick thing that I can't seem to see the answer for in a quick look. I see that the local storage is still used to set some data even with cookie scheme. One is a token expiration. When this token expiration is reached, is the module meant to then get the user endpoint again to check if the user is logged in and refresh the token? If not, how do I go about adjusting this expiration or possibly adding that sort of functionality?
I think that may have been what was happening when I still have a valid JWT http only cookie but the auth module then thought I was logged out.
Let me explain how it works.
When the access token expires, we check whether the refresh token has expired or not based on its expiration value (_refresh_token_expiration). If it still valid, we intercept the next request and refresh the token (the original request will await for refresh request). If not, then we log the user out.
You can take a look at the request handler
Note that refresh functionality is avaliable in refresh scheme. You can still set required: false for both token and refreshToken options.
If you want to add a built-in auto refresh feature based on timer, you can take a look at #634
The built-in auto refresh feature was our first attempt to add refresh functionality to auth module. We changed our approach to only refresh when a request with an expired token was made. This way we don't need an internal timer running in background.
We also don't intercept 401 requests anymore. A 401 error means that you are unauthorized to access the target resource, but it doesn't necessarily mean that the token has expired. That's why we base the refresh on token expiration.
I see that the local storage is still used to set some data even with cookie scheme.
We sync the data of local storage and cookies, but both can be disabled, you just need one of them to system work properly.
Note that you must use cookies in order to work in server side.
You can disable local storage by adding localStorage: false to your auth options. You can check local storage options and cookie options
OK great, thanks again for all of this.
Fortunately my system works in a way whereby if the JWT token is expired, there is an internal refresh token in the API which will be checked, and a new cookie response sent back to renew that user's jwt token. If the refresh token has expired, the API will return a set cookie header to clear the user's authentication cookie as well as return the appropriate http status.
I do not configure the refresh endpoint so that would never be called currently, and probably rightly so judging on how this module works.
With all this, once again, very useful information and overview I'll have a think about the best approach to deal with the front-end detecting whether the user continues to be logged in or not. I imagine it'll be based on a 401, but not a 403 and that can trigger a redirect to the login page. And I can probably set the expiry time to something appropriately large or disabled. So I can stick in an axios interceptor which will detect the request to my API when the 401 is returned.
It's a tricky business this! Thank you.
Glad to help! Just ping me if you need help with something else! :)
how do I go about adjusting this expiration
You can set maxAge in token option. This way the system will know when your token expires. (same can be done for refresh token in refresh scheme)
The maxAge property is used as a fallback, when we couldn't decode the token or no token was provided.