Yep, it seems to me that people started working with microframeworks on nodejs etc. thinking they understood what they were doing, then realized they're missing all the convenient features they used to have and didn't have a clue of how they were implemented so they just picked some things that sounded cool (JWT & localStorage) and went with those without even a single second of thought as to "why isn't everyone doing this?" or similar.
Secure information, such as session information, should almost universally be stored in httpOnly & secure cookies, potentially with SameSite=strict. I just don't see any reason for JS to touch the cookies in general, just use a /me endpoint or similar to check if you're logged in and to fetch your user's information so you don't need to touch the secure cookies.
I don't really understand what your thinking of stateless is here. If your cookie holds all the necessary data, e.g. your user information in encrypted form or JWT style tokens or similar, then your backend server really needs to know no other "state".
To be clear, you're suggesting using cookies for authentication in a REST API? In my experience this is not a successful pattern and certainly doesn't conform to some of the core principles of REST.
It does conform to REST. There's nothing not RESTFUL about cookies.
Cookies are purely an HTTP header. When a cookie is sent from a browser->server it is sent via the Cookie: name=value format. This is it.
What you're comparing it to is the Authorization header, which looks like this: Authorization: .... There is NO difference whatsoever here except for the name of the header.
This is a really common misconception. I've started replying to comments (this article really blew up while I've been busy working...) but I plan to write a more in-depth article covering some of the arguments I only briefly mentioned here in more depth.
On top of that, not all APIs have to be RESTful and assumed to be accessed by 3rd parties.
If it's your own application, you can still make pretty RESTful APIs, using cookies, and not have to worry about the difference between using Authorization: ... headers vs. Cookie: ... headers. Authorization: header is great when you assume the client is a non-browser application, and you can cater to those easily with your middleware. For your own browser applications cookies are better.
Keep in mind that some sandbox environments don't allow arbitrary headers to be set or even cookies to be added, but they do support the RFC for Authorization headers. So, it may seem like the same thing, but when your local API (e.g. WkWebView had (has?) this as a limitation) - then you're going to have to use it that way.
So what you're trying to say is that since some systems are limited, it's ok to use a bad practice everywhere?
Of course you're allowed to work with the limitations of your system (though finding a less idiotic system is a better choice), but that doesn't mean you should use the bad practice from the bad system when working with good systems or systems you have control over and can fix.
Good article, but the language is too strong. You made a few good points but also some arguments are controversial.
I am not sure if using authentication cookie for API is a common pattern out there. I haven't seen many APIs using Cookie for authentication. Cookie and Authorisation are designed for different purposes. Yes, they are just different names behind the scene but applications treat them differently. Authorisation header is not automatically pre-filled by browsers while cookie is. That means using Cookie as authentication you're prone to CRSF. That's why we use Authorisation header to avoid CRSF as much as possible.
And last point, no one puts a few MB of data in a JWT token.
I hope nobody is putting a few MB of data in my localstorage either. If you use Authorization header, the token can be extracted by XSS etc. and sent to malicious servers and they can then use it however they wish.
If you use a secure; httpOnly cookie it can't be stolen by malicious JS and is bound to your browser's security model. Add to that proper CSRF tokens, and e.g. sameSite=strict and you've got a decent system.
Can you clarify what you mean by a "stateless" API? If I'm reading you right, the concept seems kind of nonsensical to me. I'm going to respond to what I think you're saying, but I'm worried that I'm constructing a strawman in doing so.
I see three worlds in which a /me endpoint wouldn't be "stateless." The first is a world in which the current user is considered a form of state. Whether that's true in some absolute sense is an abstract philosophical question. The more practical question: if we're considering tracking the current user (ie "performing authentication") to be antithetical to a stateless API, then how are we supposed to then perform authorization? Is everyone just supposed to be allowed to do/see everything?
The second: if you're using a /me endpoint to retrieve user data, in lieu of retrieving it from a cookie, presumably some earlier interaction has logged the user in. I suppose there's implicit state in the fact that those two interactions must occur in order. But even if we're using bearer token auth, that token needs to have been generated somehow. Complaining about the presence of that state doesn't seem very practical to me.
The third: cookies are inherently stateful because they require session data to be present on the server. This is kind of true? But if we want to use a bearer token instead, presumably we need to have generated it. We can't generate secure bearer tokens idempotently or reversibly, so we need to store some kind of data about the bearer token on the server... which to me is not philosophically all that different from creating a session?
Anyway, again, I feel like I may well be arguing against a strawman here! If there's some fourth definition I haven't considered, I'd love to hear how you're defining stateless APIs and why you feel that that definition produces more useful/usable API designs.
cookies are inherently stateful because they require session data to be present on the server
Not exactly true. Many cookies use signatures or encryption to make the data trustworthy for the server, so there does not need to be the somewhat traditional "sessions" list in a database.
Well, it doesn't necessarily work. One way is to keep short validity, another is to tie it to e.g. the last update of the relevant user's password, or similar - you wouldn't have to check the contents of the package, just that it's been signed after the password has last changed.
There are lots of little options you could use, but it's not always necessary.
Why do you assume I have access to a an endpoint where I can fetch this user's information? This user's claims could come from a trusted 3rd-party, and their claims are verified on my end by their pubkey.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Yep, it seems to me that people started working with microframeworks on nodejs etc. thinking they understood what they were doing, then realized they're missing all the convenient features they used to have and didn't have a clue of how they were implemented so they just picked some things that sounded cool (JWT & localStorage) and went with those without even a single second of thought as to "why isn't everyone doing this?" or similar.
Secure information, such as session information, should almost universally be stored in
httpOnly
&secure
cookies, potentially withSameSite=strict
. I just don't see any reason for JS to touch the cookies in general, just use a/me
endpoint or similar to check if you're logged in and to fetch your user's information so you don't need to touch the secure cookies.The
/me
endpoint solution doesn't work with a stateless API.I don't really understand what your thinking of stateless is here. If your cookie holds all the necessary data, e.g. your user information in encrypted form or JWT style tokens or similar, then your backend server really needs to know no other "state".
To be clear, you're suggesting using cookies for authentication in a REST API? In my experience this is not a successful pattern and certainly doesn't conform to some of the core principles of REST.
It does conform to REST. There's nothing not RESTFUL about cookies.
Cookies are purely an HTTP header. When a cookie is sent from a browser->server it is sent via the
Cookie: name=value
format. This is it.What you're comparing it to is the Authorization header, which looks like this:
Authorization: ...
. There is NO difference whatsoever here except for the name of the header.This is a really common misconception. I've started replying to comments (this article really blew up while I've been busy working...) but I plan to write a more in-depth article covering some of the arguments I only briefly mentioned here in more depth.
On top of that, not all APIs have to be RESTful and assumed to be accessed by 3rd parties.
If it's your own application, you can still make pretty RESTful APIs, using cookies, and not have to worry about the difference between using
Authorization: ...
headers vs.Cookie: ...
headers.Authorization:
header is great when you assume the client is a non-browser application, and you can cater to those easily with your middleware. For your own browser applications cookies are better.Keep in mind that some sandbox environments don't allow arbitrary headers to be set or even cookies to be added, but they do support the RFC for Authorization headers. So, it may seem like the same thing, but when your local API (e.g. WkWebView had (has?) this as a limitation) - then you're going to have to use it that way.
So what you're trying to say is that since some systems are limited, it's ok to use a bad practice everywhere?
Of course you're allowed to work with the limitations of your system (though finding a less idiotic system is a better choice), but that doesn't mean you should use the bad practice from the bad system when working with good systems or systems you have control over and can fix.
Good article, but the language is too strong. You made a few good points but also some arguments are controversial.
I am not sure if using authentication cookie for API is a common pattern out there. I haven't seen many APIs using Cookie for authentication. Cookie and Authorisation are designed for different purposes. Yes, they are just different names behind the scene but applications treat them differently. Authorisation header is not automatically pre-filled by browsers while cookie is. That means using Cookie as authentication you're prone to CRSF. That's why we use Authorisation header to avoid CRSF as much as possible.
And last point, no one puts a few MB of data in a JWT token.
I hope nobody is putting a few MB of data in my localstorage either. If you use Authorization header, the token can be extracted by XSS etc. and sent to malicious servers and they can then use it however they wish.
If you use a
secure; httpOnly
cookie it can't be stolen by malicious JS and is bound to your browser's security model. Add to that proper CSRF tokens, and e.g.sameSite=strict
and you've got a decent system.Can you clarify what you mean by a "stateless" API? If I'm reading you right, the concept seems kind of nonsensical to me. I'm going to respond to what I think you're saying, but I'm worried that I'm constructing a strawman in doing so.
I see three worlds in which a
/me
endpoint wouldn't be "stateless." The first is a world in which the current user is considered a form of state. Whether that's true in some absolute sense is an abstract philosophical question. The more practical question: if we're considering tracking the current user (ie "performing authentication") to be antithetical to a stateless API, then how are we supposed to then perform authorization? Is everyone just supposed to be allowed to do/see everything?The second: if you're using a
/me
endpoint to retrieve user data, in lieu of retrieving it from a cookie, presumably some earlier interaction has logged the user in. I suppose there's implicit state in the fact that those two interactions must occur in order. But even if we're using bearer token auth, that token needs to have been generated somehow. Complaining about the presence of that state doesn't seem very practical to me.The third: cookies are inherently stateful because they require session data to be present on the server. This is kind of true? But if we want to use a bearer token instead, presumably we need to have generated it. We can't generate secure bearer tokens idempotently or reversibly, so we need to store some kind of data about the bearer token on the server... which to me is not philosophically all that different from creating a session?
Anyway, again, I feel like I may well be arguing against a strawman here! If there's some fourth definition I haven't considered, I'd love to hear how you're defining stateless APIs and why you feel that that definition produces more useful/usable API designs.
Not exactly true. Many cookies use signatures or encryption to make the data trustworthy for the server, so there does not need to be the somewhat traditional "sessions" list in a database.
Cool, I didn't know that! How does credential revocation work in that context? Or do you just maintain a really short validity window?
Well, it doesn't necessarily work. One way is to keep short validity, another is to tie it to e.g. the last update of the relevant user's password, or similar - you wouldn't have to check the contents of the package, just that it's been signed after the password has last changed.
There are lots of little options you could use, but it's not always necessary.
I think lietu has just described JWT in a cookie ...
Why do you assume I have access to a an endpoint where I can fetch this user's information? This user's claims could come from a trusted 3rd-party, and their claims are verified on my end by their pubkey.