Closed sky-code closed 3 years ago
in issue #92 , @breakingrobot said
Also, setting httpOnly will involve that Nuxt will not be able to modify the cookie client side, which for a static website or a SPA would be a bad idea.
which for a static website or a SPA would be a bad idea.
Why is a bad idea? I just want to get more secure solution, and for previous version this strategy work fine, with this config
token: {
enabled: false,
localStorage: false,
cookie: false,
}
but for new version it doesn't work.
There are no reasons to not support this token storage method, just need a documentation how to use auth-module
with https only cookies
By using httpOnly cookie, Nuxt won't be able to interact with it on client-side.
@pi0 we should have a discussion about cookies again asap.
By using httpOnly cookie, Nuxt won't be able to interact with it on client-side.
Yes I know that, but it is also not necessary.
Auth module hit user
endpoint, if response is 200 OK
then set loggedIn
to true, otherwise false, cookies will be sent automatically, access to cookies isn't required, same logic with logout.
In V3 I used such approach and it worked.
@breakingrobot approach is not wrong, is different, I understanding that it not the best option, but In my case, HttpOnly much better for security, thats all.
If you don't need this, use another approach, but auth-module
can handle this storage method for those who need this.
I will start to use this module, but I agree with @sky-code about the possibility to use a HttpOnly cookie at least for the refresh_token. Is this one also stored in a non HttpOnly cookie? I think that it can be vulnarable to XSS attacks, am I wrong?
Did you make any progress on this @sky-code ? I'm currently trying to achieve the same goal.
@NetBzz No, I have switched off HttpOnly for now
@breakingrobot Storing the tokens in a httpOnly
cookie does not break anything, it's just another approach. Actually, it's a lot safer approach as it mitigates XSS vulnerabilities. If you're storing secret tokens in a non-httpOnly
cookie you're a very easy target. It is most certainly not a safe way to store tokens on the client-side.
Take a look at this: https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage
@sarneeh that's what i am talking about.
auth-module
must have the first class support, for the httpOnly cookie auth flow and it must also have the clear instruction how to use it.
But if no have httpOnly flow, how we can protect our cookies?
I am using an approach where I split my JWT into two cookies containing signature.payload
and signature
. The cookie containing signature
is httpOnly and signature.payload
can be accessed by the browser. This way I have the safety of httpOnly cookies while getting the user info in the payload and not risking of leaking the full JWT, as they are separated.
I think this is a reasonable and should be implemented. Meanwhile I guess I will have to write a custom solution.
I'm a bit confused on the outcome of this conversation; Am I right in thinking its currently not possible to authenticate and keep session using httpOnly cookies?
@blowsie try to use https://auth.nuxtjs.org/schemes/local.html#tokenrequired tokenRequired option
Thanks @sky-code but doing this had no effect for me.
The login
request work perfectly fine, sends set-cookie
headers, but the next request to user
does not send any cookies.
My config;
auth: {
strategies: {
local: {
endpoints: {
login: {
url: '/api/auth/login',
method: 'post',
propertyName: 'result'
},
logout: { url: '/api/auth/logout', method: 'post' },
user: { url: '/api/auth/ping', method: 'get', propertyName: 'result' }
},
tokenRequired: false
}
}
},
This authentication Rest API is working perfectly fine in other solutions of mine
This was due to my application using secure cookies but i wasnt running https 🤦♂ . Thanks!
But if no have httpOnly flow, how we can protect our cookies?
Is it a good idea to encrypt the token?
@Jack74r The problem is not that the token is going to be decoded, but stolen. Encrypting the token won't give you anything in a situation of a stolen access token.
@sky-code @sarneeh OMG!!! I am in the process of migrating a vuejs SPA app to nuxt framework (SPA only and not Universal) and I just realized this issue. I really don't understand the complication here (I think I am missing something). I always listen for 401 from server responses and invalidate the local session.
@breakingrobot is there any way to extend this behavior with a plugin?
@sky-code @sarneeh @breakingrobot Sorry! I guess my problem is not relevant here.... I solved the issue with following configuration
auth: {
strategies: {
local: {
endpoints: {
login: {
url: '/login',
method: 'post',
propertyName: 'data',
withCredentials: true
},
user: {
url: '/is-logged-in',
method: 'get',
withCredentials: true
},
logout: false
},
tokenRequired: false,
tokenType: false
}
}
}
Setting the axios to 'withCredentials: true' solves my issue!
This question any progress? How to set httpOnly
useapolloHelpers.onLogin
?
Setting propertyName: false
for user endpoint made it work for me
auth: {
strategies: {
local: {
endpoints: {
login: { url: '/auth/login', method: 'post'},
logout: { url: '/auth/logout', method: 'post' },
user: { url: '/auth/me', method: 'post', propertyName: false }
},
tokenRequired: false,
tokenType: false
}
}
},
The new version will also provide a schema for refresh tokens. Will it be possible to set them as httpOnly cookies here? Especially refresh tokens, which are long-lived, should not be stored just like that.
I left a pretty lengthy explanation of why you should absolutely not listen to anyone telling you it's ok to set session credentials anywhere client side JavaScript can access on the original issue https://github.com/nuxt-community/auth-module/issues/92
I've been digging into trying to get to get httpOnly to work with the module for about an hour, and come to the conclusion that maybe I shouldn't be using an auth module where the authors express ideas and opinions like those shared above ^
I am using an approach where I split my JWT into two cookies containing
signature.payload
andsignature
. The cookie containingsignature
is httpOnly andsignature.payload
can be accessed by the browser. This way I have the safety of httpOnly cookies while getting the user info in the payload and not risking of leaking the full JWT, as they are separated. I think this is a reasonable and should be implemented. Meanwhile I guess I will have to write a custom solution.
I agree with @rur0 's approach.
Server only verifies cookie with httpOnly (signature
)
In what cases it's important to use payload compared to doing request against /auth/me? Because of unnecessary api call on every route change?
I have httpOnly cookie setup in the following way:
if ($request->hasCookie('...')) {
$token = $request->cookie('...');
$request->headers->add(['Authorization' => 'Bearer ' . $token]);
}
And in my nuxt config
strategies: {
local: {
endpoints: {
user: { url: '/auth/me', method: 'post', propertyName: false }
},
tokenRequired: false,
tokenType: false
}
}
In what cases it's important to use payload compared to doing request against /auth/me? Because of unnecessary api call on every route change?
I have httpOnly cookie setup in the following way:
- When I log in, server sends back httpOnly cookie.
- When I receive a request on backend, I have a middleware that sets the Bearer token (Laravel)
if ($request->hasCookie('...')) { $token = $request->cookie('...'); $request->headers->add(['Authorization' => 'Bearer ' . $token]); }
And in my nuxt config
strategies: { local: { endpoints: { user: { url: '/auth/me', method: 'post', propertyName: false } }, tokenRequired: false, tokenType: false } }
@chanar Your httpOnly cookie came from Laravel API? So that cookie will show in the API request instead of storing in the browser, In that case, that cookie will disappear when you are refreshing the browser. what you did for persisting your httpOnly cookie?
Not that we needed another opinion in here, but just to make it clear -- HTTPOnly cookies are absolutely a security best practice, it protects from many common forms of XSS attacks.
There seems to already be a PR for this:
https://github.com/nuxt-community/auth-module/pull/390
I'm going to have to move off this library but it would be nice if that PR was given a second look, I'd be willing to help work on it if the submitter is no longer responsive.
[EDIT] - I finally got around to writing a small post on how I got cookie-based auth working without nuxt-auth
for now. As always thanks for all the hard work to the nuxt-auth
team, will be looking to see if that PR gets looked at/merged!
Not using HTTPOnly cookies is often reported by Dynamic application security testing tools like Burp and penetration testing tools like OWASP Zed. It is important to use them and it should at least be optional to use them in this marvelous module. I think it is bad to just ignore them. Also, this issue is dating back from 2018 :(
Maybe it will be helpful https://github.com/nuxt/nuxt.js/issues/575#issuecomment-393514815
Interesting article on why this is important: https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id
I think this is a pressing problem at 2020 for an authentication module..
A potential malicious actor that had somehow managed to execute untrusted JavaScript in the web application would be able to take over other users' sessions.
At the very minimum I'd add a warning in the module's documentation and a technical explanation what's taking this long.
@chanar Your httpOnly cookie came from Laravel API? So that cookie will show in the API request instead of storing in the browser, In that case, that cookie will disappear when you are refreshing the browser. what you did for persisting your httpOnly cookie?
@harsha935 Yes from Laravel but from any backend, send the cookie with httpOnly.
Just for the update if it helps anyone. I have removed auth configuration from my nuxt config. My axios module has credentials set to true and that's it for my app.
axios: {
baseURL: process.env.API_URL,
credentials: true
}
When I login/register, I send a response with httpOnly cookie with value of token.
Whenever I refresh the page, close the tab or window I still have my cookie token included in request headers. When I need to use axios "outside" the Nuxt I use options { withCredentials: true }
Hi. A quick update on this issue:
Closing issue as cookie scheme is already supported with v5 (which is now used as main docs) and most of concerns are related to API/Frontend misconfiguration not auth module. Still we would need to improve docs to make it more bold.
I'm trying to use the cookie-schema. After logging in, I can see my cookie being set, but the front-end still doesn't think I am logged in. This appears to be because the cookie that gets set is http-only. When I refresh the page, the site now knows I am logged in.
I am not sure what I'm doing wrong. This is my auth configuration:
strategies: {
cookie: {
cookie: {
name: 'SHOP_TOKEN'
},
user: {
property: 'body.data.customer',
autoFetch: false,
},
endpoints: {
login: { url: '/customerLogin', method: 'post' },
}
}
}
After logging in, I see the SHOP_TOKEN
cookie being set:
But $auth.loggedIn
remains false
until I do a full page refresh. What am I missing here? I've been at this for hours and am not having any luck. I must be missing something obvious. Can anybody help?
I'm trying to use the cookie-schema. After logging in, I can see my cookie being set, but the front-end still doesn't think I am logged in. This appears to be because the cookie that gets set is http-only. When I refresh the page, the site now knows I am logged in.
I am not sure what I'm doing wrong. This is my auth configuration:
strategies: { cookie: { cookie: { name: 'SHOP_TOKEN' }, user: { property: 'body.data.customer', autoFetch: false, }, endpoints: { login: { url: '/customerLogin', method: 'post' }, } } }
After logging in, I see the
SHOP_TOKEN
cookie being set:But
$auth.loggedIn
remainsfalse
until I do a full page refresh. What am I missing here? I've been at this for hours and am not having any luck. I must be missing something obvious. Can anybody help?
Same for me, did you get it to work?
Hi @spire-mike and @sPaCeMoNk3yIam - I had the same issue with cookie scheme on HTTP-only cookies, but found a way to make it work.
After way too much time debugging it, I got it to work simply by removing the cookie name in my auth config (ie: the name of my HTTP-only cookie sent from my backend). It worked either by setting strategies.cookie.cookie.name to null, or removing the name key completely, or removing the cookie.cookie key altogether. So under strategies.cookie I just have 'endpoints' and 'user'. (From source code, when Nuxt instantiates the cookie scheme, it has a default of cookie.coookie.name = null... so that's what will end up getting set)
I also got it to work by keeping the name of my backend cookie in auth config (cookie.cookie.name = 'name_of_my_cookie') but making it not HTTP-only. Obviously that is not advised, and not what we all were trying to do. I only did it to help figure out what was happening.
Anyways, removing the cookie name (and keeping the cookie HTTP-only) then allowed Nuxt to successfully set the $auth state to loggedIn = true, and it fetched the user data from the user endpoint.
I didn't completely go through all the source code to understand how this works, but I believe that setting cookie.cookie.name to the name of an HTTP-only cookie is what causes problems. If you DON'T provide the name of your own cookie, then Nuxt seems to look at its own cookie(s) (ie: auth._token.cookie with a value of true) and that's what it will use to know the logged in state. But if you DO provide the name of your own cookie in cookie.cookie.name, then it will try to use that to know the logged-in state (via the Scheme.check() method I believe) but if its HTTP-only it won't find the cookie, and Scheme.check() will return false, Scheme.fetchUser() will never get executed during the initial login. Not exactly sure why doing a hard refresh worked... but I noticed the same thing.
Bottom line... removing cookie name made it work, even with HTTP-only cookie.
I didn't completely go through all the source code to understand how this works, but I believe that setting cookie.cookie.name to the name of an HTTP-only cookie is what causes problems.
The check is happening here: https://github.com/nuxt-community/auth-module/blob/a6ee431fe1c696fc184d6b2ad94fd79a706acac7/src/schemes/cookie.ts#L68
If you DON'T provide the name of your own cookie, then Nuxt seems to look at its own cookie(s) (ie: auth._token.cookie with a value of true) and that's what it will use to know the logged in state.
Before this line you can see the call to super.check()
which checks if there was a successful token request in the past (retrieved from auth.$storage.syncUniversal
) and based on that assume you are logged in (or not).
If you'd have a cookie name set it would now check if that cookie is still set/has not expired yet (and would fail). But so it returns true
for "user has successfully requested a token in the past".
(Slightly off topic: it would also be nice if httpOnly
cookies would at least be "visible" (as in cookies[name] -> true
but without revealing its contents) to JS as knowing that a cookie is set (whatever its contents) simply means that it was once received and hasn't expired yet ... another debate for another time)
So what is happening is:
auth\login
login
request and sends back your httpOnly
cookie (so yes: the httpOnly
cookie needs to be set by your auth-server -- not auth
itself)set-cookie
header but auth
will never know about that (because the cookie is not script accessible) and it will only save token=true
in memoryauth
as if there is no token or cookie at all - it just has to belief in good faith that the cookie exists and didnt expire yet and that requests to the auth-server (with axios config credentials: true
) will be successfull ... until they arentThis also means:
if you want to know if your cookie meanwhile expired you might want to do a userFetch()
on each page-load just to make sure you have still the necessary valid credentials in form of that invisible cookie
In my case i have no bearer at all and need to authorize on a third party API and reuse the cookie set by api in further calls. can anybody hint what will be the strategy settings for this case? There is no refresh token and
I want to store my auth token in HTTP only cookie, with version
3.4.1
this scheme worked, but after update to4.1.0
I get strange behavior, I can authorize but after reloading page, I am not authorized any more. So can you please add proper support for HTTP only cookie auth strategy, and add documentation for this usage scenario ?