I’m currently reading through RFC 6749 (“The OAuth 2.0 Authorization Framework”) and RFC 6750 (“The OAuth 2.0 Authorization Framework: Bearer Token Usage”).
I wonder if there is a way to send the Authorization: Bearer ... header from a browser-based client, that automatically links the token to requests, like there is with Authorization: Basic ..., which can be triggered by sending a WWW-Authenticate: Basic realm="..." in a response. The browser then asks for a username and password and sets the Authorization header automatically in the next request.
Is there a way to do something similar for bearer tokens? Especially to link the token to a host or similar context that works across page refreshes?
The reason I’m asking is to avoid an unnecessary delay in having to load and parse some JavaScript that extracts the bearer token from – say – LocalStorage and setting the Authorization header. This would also allow me to have protected assets which are not requested via Ajax or Fetch requests, e.g. images (img tags).
I know a common workaround is to substitute the bearer token for a session cookie. But I’d like to know if there are other solutions to this problem.
ACCESS TOKEN USAGE
There are no options that will send access tokens automatically during HTML requests. They are designed to only be sent when your code explicitly requests it. This prevents certain vulnerabilities that were common with cookies.
HYBRID APPROACH
I've come to think that the best all round option for modern SPAs is to adopt the following approach:
Use access tokens in the browser - to support fast cross domain API calls
Use HTTP only cookies to handle aspects related to page reloads and multi tab browsing - where the cookie can also store or link to a refresh token
SECURING HTML ASSETS
It feels like a cookie is also the only option that will work well for your scenario. As you say, a cookie will be sent on image requests before your Javascript bundles are fully downloaded.
MY SCENARIO
I had different reasons for wanting the benefits of both cookies and tokens, to work around some token renewal problems during multi tab browsing. I wanted the overall behaviour to be that of an SPA though.
LIMITED USAGE COOKIES
In my case I used a cookie, but in a very targeted way. Perhaps in your case you could do something similar, while continuing to use access tokens for API calls.
Related
I am currently working on an application (React frontend, node.js server and api) and am using JWT. I am having a hard time wrapping my head around the process of storing and sending the token using HTTP "Authorization" header.
Tutorials online seem to do a great job of showing me how to send this token once it is stored somewhere, but how does it actually get stored there in the first place is where my confusion arises.
I have two ways of thinking of approaching this:
The token is generated on login, then returned to the frontend, then stored in localstorage. Then, when a request is made, the HTTP "Authorization" header is set by pulling the token from local storage.
The token is generated on login, then returned to the frontend. It is somehow stored already in the "Authorization" HTTP header (Does this even make sense?). Then when a request is made, the header is already set.
Do option 1 but use a cookie (or session-cookie?) (don't know how to do this approach).
I would like to know:
A. Which of the 3 (if any) is the right approach
B. If approach 2 is the correct way, how do you actually STORE this header once you get the token?
C. If NOT approach 2, where is the preferred place to store this token (localstorage, cookie, etc.)?
I have tried approach 1, it works but seems unsafe and not best practice.
I have NOT tried approach 2, because I have no idea how to do it, and couldn't find anything online.
I have NOT tried approach 3, but I assume it could work in a similar fashion to 1?
First, your instincts are right; you should not use local storage for a secure token because local storage persists even when the browser is completely closed.
The most straightforward way to store your token is to just keep it in memory. A global variable in Javascript, or even attaching it to window, works fine. Then with every XHR call you insert Bearer [Token] into the Authorization header yourself. There are a bajillion npm packages to help with XHR requests but virtually all of them should let you insert this header. If you haven't already you should write a single wrapper function to encapsulate all your API calls and insert the token at this point.
The drawback with this is that the token won't persist across different pages on the same domain or across browser tabs, nor will it work with non-XHR requests like img src="https://secure_api_route_that_returns_an_image.jpg", as there's no way to programmatically inject custom headers in those cases. I don't know if any of that is a problem for you. Usually with React SPAs you aren't going to be bouncing around truly different "pages" on the same domain, but you may well want to be securely sourcing images with what are technically API routes, or keeping the session alive across browser tabs; I'm not sure what your requirements are.
Anyway if you do need any of that functionality, you have to use a "session cookie". (Technically you could use session storage if you only need to preserve the token across page navigation but it doesn't work across tabs, so I find it to be pretty useless over just storing in-memory). This just means a cookie with no expiration date, which is automatically purged when all browser tabs have been closed. You must enforce earlier expiration on the backend rather than through the cookie header.
I use .NET backends rather than Node, so I can't give you specific code, but basically you need to return this header in a successful login API response:
set-cookie: myCookie={jwt}; Path=/; Secure; HttpOnly; SameSite=Strict;
Don't omit Secure, HttpOnly or SameSite=Strict except possibly in development, as otherwise you'll be open to common vulnerabilities. (This is one of the disadvantages of the cookie approach; it's easier to implement wrongly and open up a vulnerability). This assumes your frontend and backend are hosted on the same domain. It becomes a little more complicated if not (CORS, etc.) but it can be done in that case too with a little more work.
On the authentication side, you will just need to look for this cookie in every API other than login. If everything is working right the browser will automatically send something like this with every request to your domain, on any tab, for as long as any browser tab remains open:
cookie: myCookie={jwt}
Again not a Node.js guy but I'm reasonably sure most any library that can verify a token via the Authorization header will support cookie authentication too; you'll need to check the documentation or please post a new question if you can't figure it out.
On the frontend side, besides the cross-tab support, the browser takes care of saving the cookie and sending it when it should, so it's conceptually close to what you're looking for in option 2.
So as with most things there's no single right way (though there is definitely one wrong way - localstorage). For what it's worth, a few years ago I surveyed several commercial sites to see how they handled this, and I found most used the session cookie. This included U.S. banks and financial institutions, which have to follow some of the strictest security standards that exist. While cookies sometimes get a bad rap, when used correctly they are still industry standard for secure authentication. But storing the token in plain old Javascript memory is fine if you don't want to deal with cookies or need the features cookies give you.
Long story short I have just somewhat finished building an application with NextJs, NextAuth and SWR, I use SWR to call multiple Twitch API's, to authenticate the Twitch API I use my Twitch applications Client-Id, and the logged in users Bearer token which is taken from the NextAuth's session object.
My concern is whenever SWR performs and API function to populate the page with data or update it, you can easily view the request headers when capturing the network calls in chrome or firefox dev tools . This is majorly concerning that this is a production environment and it still happens, this also violates Twitch developers documentation and pretty sure there tos "Warning: Treat your token like a password. For example, never use access tokens in any public URL, and never display tokens on any web page without requiring a click to de-obfuscate."
Is there a way or solution to hiding these?
Now my next best question is how can I stop this from happening and is their even a way?
No need to be alarmed.
It's ok to include Bearer tokens in the request's Authorization header - in fact that's the standard way to send them. You can't hide it from Chrome's Dev Tools as that would defeat its purpose.
The advice you read never use access tokens in any public URL might mean don't ever display them in a URL like mywebsite.com/?token=[abcd] because then it would be accessible via browser history, or don't send a token designed for one service to a different website.
This has nothing to do with SWR - it's standard HTTP behaviour.
We're in the process of moving our frontend into a separate project (out of Django). It's a Javascript single page application.
One of the reasons is to make it easier for our frontend developers to do their work, not having to run the entire project -- including the API -- locally. Instead, we'd like them to be able to communicate with a test API we've set up.
We've managed to solve most of the CORS/CSRF issues along the way. But now we've run into something I can't find a solution for anywhere, despite reading lots of documentation and SO answers.
The frontend and the API are served from different domains (during development localhost and test-api.example.com). Until now, while served from the same domain, the frontend has been able to get the CSRF token from the csrftoken cookie set by the API (Django). But when served from different domains, the frontend (localhost) can't access the cookies of the API (api-test.example.com).
I'm trying to figure out a way to work around this, to somehow deliver the CSRF token to the frontend. The Django docs recommend to set a custom X-CSRFToken header for AJAX requests. Would we compromise the CSRF protection if we similarly served the CSRF token in every response as header and (via Access-Control-Expose-Headers) allowed this header to be read by the frontend?
Given that we've set up CORS properly for the API (i.e. only allowing certain domains to do cross origin requests to the API), JS on 3rd party sites should not be able to read this response header, thus not be able to make compromising AJAX requests behind the back of our users, right? Or did I miss something important here?
Or is there another, better way to achieve what we want?
I didn't understand your question at first, so allow me to summarize: you can't get the CSRF token from the cookie on the client because the Same Origin Policy blocks you from accessing cross-domain cookies (even with CORS). So you're suggesting that the server transmit the cookie to the client in a custom header instead, and are wondering if that's secure.
Now, the documentation does make a suggestion for how to transmit the token if you're not using the cookie: put it in the response body. For example, you could use a custom meta tag. When it comes to security I lean towards using recommended solutions rather than trusting my own analysis of something new.
That caveat aside, I don't see any security problem with what you're suggesting. The Same Origin Policy will prevent a third-party site from reading the headers just as it will the body, and you can opt in to reading them from your client domain with the CORS Access-Control-Expose-Headers header.
You might find this answer interesting, as it lays out the advantages and disadvantages of various CSRF token schemes. It includes the use of a custom response header, and—to the point of your question—confirms: "If a malicious user tries to read the user's CSRF token in any of the above methods then this will be prevented by the Same Origin Policy".
(You might want to look into whether you need Django's CSRF protection at all with your SPA. See this analysis, for example. That's outside the scope of this question, though.)
Assume you already have corsheaders installed. Write a Django middleware and include it in your MIDDLEWARE settings:
from django.utils.deprecation import MiddlewareMixin
class CsrfHeaderMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if "CSRF_COOKIE" in request.META:
# csrfviewmiddleware sets response cookie as request.META['CSRF_COOKIE']
response["X-CSRFTOKEN"] = request.META['CSRF_COOKIE']
return response
expose the header in your settings:
CORS_EXPOSE_HEADERS = ["X-CSRFTOKEN"]
When you make a GET API call from you JS, you should get X-CSRFTOKEN from response header, go ahead and include it in the request header when you make POST PUT PATCH DELETE requests.
The Question(s) =]
Based on my research, it seems like storing web tokens in local storage is the preferred method over using cookies in order to prevent CSRF attacks (I am aware that local storage is susceptible to XSS attacks though these seem easier to prevent than CSRF). This being said, I have been unable to locate any relavant guides on utilizing local storage for this means...
I am looking for some help on understanding how the following works...
What is the preferred method/workflow to pass a server-signed token to the browser's local storage.
Once the browser stores the token, how do I then use that stored token.
Do I need to stop the default submission of things like forms via JS and then send AJAX requests with the authorization: Bearer <token> header with every request?
When a user clicks a link to a resource owned by that user, how do I send that token to server to grant access to the protected resource?
The Tools
Front-end
HTML5 (Compiled from Handlebars)
JS
CSS (Compiled from SASS)
Back-end
nodeJS (using Express)
NOTE: I hope to edit this post with code samples in the future (once I get the hang of it lol), to help other confused people like myself in the future
Preferred method is to return token with successful response from /users/login request (wherever you prefer, header or body). Then you put it into local storage.
Most important thing here, is that JWT can NOT be used without HTTPS. Otherwise you will end up passing credentials unprotected.
After acquiring of token, you should MANUALLY add it as a header to each request. Use some js ajax wrapper to simplify this task.
There is no way as far as I know to send custom headers with <a href=""> elements. So I ended up passing additional token (some hash, not JWT) along with link <a href="/some_protectded_page?token=...">. But this is not optimal. If you render pages on server side, it is better to use sessions.
I have a site where user must login to access. I have some web services calls for getting datas stored in another server.
But to access to the server user must login again via a login popup. To avoid this I want to pass the user/password but not in the URL.
Is it possible to do this in Javascript adding user/password in header or something similar ?
Authorization of this type is typically done via request headers. For regular users interacting with a web page, the login credentials may be remembered using the cookies in the request. For API access, however, the standard way to do this is with bearer tokens included in the POST body of the request. See also: OAuth2.
For authenticating API access (but not the user), such authentication is typically done via API keys.
Important footnote: whenever doing any authentication, you should ensure that your requests are encrypted (and, when setting cookies, that cookies used for authentication are properly marked "secure").
Take a look at the W3C specifications for the XMLHttpRequest Object. The five-parameter version of the 'open' method allow you to specify the username and password.
EDITED
Keep in mind: this will make your password publicly accessible.