JWT token with AJAX, non-AJAX, JQuery - javascript

I'm a bit frustrated with managing my JWT token during login, submits and redirects. Before I get started here's my technology stack just in case:
JQuery/Html -> Node.Js -> Java Restful Services -> MySQL.
My java Restful services manages creating the JWT Token returning it to the Node.js layer which decides what to do with it and pass it on the the client. This all works wonderfully.
To get the JWT token I'm making an ajax based authentication request to the Node middle tier, which authenticates and returns the token which is summarily crammed into localstorage on the client.
Now I have no desire what so ever to make the entire site load off a single page through ajax, it's a complex site and doing that is just dumb! I need to forward and navigate to sub pages while carrying along the JWT token.
Here's the question (finally)... How do send along the JWT token to the middle tier (node.js) without attaching it as a request or post parameter because that's a big no no? I can't seem to find a way to stuff it in the header associated with Bearer.

You need to store the token at client side using for example a cookie or localStorage
Ajax requests
Cookies: A cookie is sent automatically when making a request to the server, so you do not need to add a specific header
LocalStorage:It is needed to provide the token in each request using an HTTP header.
For example
POST /authenticatedService
Host: example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
This is an example code to show how to execute an ajax POST request using jquery
$.ajax({
type: "POST", //GET, POST, PUT
url: '/authenticatedService' //the url to call
data: yourData, //Data sent to server
contentType: contentType,
beforeSend: function (xhr) { //Include the bearer token in header
xhr.setRequestHeader("Authorization", 'Bearer '+ jwt);
}
}).done(function (response) {
//Response ok. process reuslt
}).fail(function (err) {
//Error during request
});
Form submit
With a form submission you can not control the headers set by browser, so it is not possible to set the Authorization header with a Bearer token. In this case you can
Cookie: store the JWT in a cookie that will be sent with the form data. You will need to add extra security to avoid CSRF attachs
Form param: The JWT is stored in a hidden field of the form.
Use always POST (not GET) to avoid cache of JWT
Link
A link executes a GET request. You could build the link adding the JWT as a query param url?jwt=...
But, consider in this case the security risks. Browser can cache the url and it will be present in logs. An attacker could potentially obtain them if he has access. Also the user could copy the link and use it outside your web application (e.g send it by email...)
If you use cookies, the token will be automatically sent to the server by clicking on the link, but this will only work if the user is authenticated. In this case be aware of CSRF vulnerabilities

Your only option is to store the token in a cookie if you don't want to do anything suggested above. You can't set http headers in links.

Related

How to store basic auth credentials in browser cache after XHR

Context
I have a friend, his name is Bob.
Bob have a server with an application running & accessible only in local. To access this application from the outside world, Bob installed & configured a reverse proxy with nginx and the module auth_basic.
Each request go through the authentication process by the reverse proxy. Two cases :
If a HTTP GET request contain valid HTTP header parameter Authorization: Basic base64credentials, then the reverse proxy access the local application and response accordingly. Each sub-request will not require a new authentication because the browser cache the credentials and send them in every request automatically until we close the browser.
If a HTTP GET request doesn't contain valid HTTP header parameter, the reverse proxy respond directly with the HTTP header WWW-Authenticate: Basic realm="User Visible Realm". Then the browser automatically show a dialog box to enter credentials.
Everything works fine until here. It's like expected from basic auth specification.
Problem
Bob doesn't like the default dialog box from the browser and want a nice html page with a form. He configurate the nginx server to have his own html form.
The problem is that the HTML submit process for a form, by default, doesn't send the Authorization header parameter. Bob need to use a XMLHttpRequest. He implement it and receive a good 200 HTTP response from the server when the credentials are good.
Unlike the default form behavior, with the XMLHttpRequest, the browser doesn't cache the credentials automatically in case of success. So each sub-request display again the login form :'(
Bob can't change the front code of the local application to send by himself the credentials in each request (as with a SPA). Indeed, he doesn't have access to this app. He just have access to nginx conf and his own html login form. So storage is useless here.
Questions
Is it possible for bob to make the browser cache the credentials after receive the XHR response ?
(The goal is to behave like the default behavior even when he use a XMLHttpRequest)
EDIT :
Further explanation
The local app is running on localhost. Bob didn't develop this app and he can't edit it. This app doesn't provide authentication and Bob used the basic_auth module of nginx as a reverse proxy to authenticate people.
It works good but use the default behavior of browsers which implement Basic Auth specification. This behavior display an ugly form and cache the credentials when success. If Bob provide his own form the behavior go, which is normal because the Basic Auth specification require specific header parameter (Authorization: Basic ...) that HTML form can't provide. Bob need to use XHR to provide this parameter.
The question is, how get back the good behavior of the browser with XHR ?
We can only use JS on login.html and not on the local app. Here is the workflow :
HTTP GET request to the server
Server doesn't find Authorization parameter OR credentials are wrong
Server respond login.html
User provide credentials by form. XHR is emitted with Authorization parameter.
Server find Authorization parameter AND credentials are valid
Server give back the local app entry file (for example index.html)
Browser read index.html and want request other files (img, css, js...)
These sub requests will fail because no credentials provide in these requests.
If ugly default form use, the credentials are cached automatically and it works.
I precise also that a solution would be to replace nginx basic auth reverse proxy by a real backend app and another authentication system (with cookie for example which are send automatically) which would work as a reverse proxy but it is not the question asked.
EDIT 2 :
Why Bob can't use storage solution ?
In the ulgy form scenario, he doesn't have HTML login file. When the browser client ask a request to the server, the server only response a HTTP response with the WWW-Authenticate header but without HTML content. The simple fact to have this header parameter display a form. Just putting the good credentials will send back a 200 HTTP Response and the browser will cache the credentials and send it in every request with the HTTP header Authorization: Basic.
In the login.html scenario, after a success login, we need to send back in every request the HTTP header Authorization: Basic (not a cookie, because it's how work Basic Auth spec and Bob doesn't have any backend, just the nginx module). It's possible to send this header from the login.html because we can attach JS on it. But then, the next pages respond by the server will be HTML files from the local app, where Bob doesn't have access to their HTML and can't attach JS on them to provide header Authorization: Basic for the next requests. A cookie could be stored from the login.html file, but this cookie need to be retrieved from the other pages and used to send header Authorization: Basic, which is impossible because Bob doesn't have access to the JS of these pages.
Thank you in advance.
Since you're already using ajax, just have javascript set and read a cookie:
(I use jQuery here, for simplicity, replace the ajax call with the appropriate syntax if you're not using jQuery):
function getCookie(cookiename) {
/* a function to find a cookie based on its name */
var r = document.cookie.match('\\b' + cookiename + "=([^;]*)\\b");
// document.cookie returns all cookies for this url
return r ? r[1] : undefined;
// return the regex capture if it has content, otherwise return undefined
}
function getData(auth_basic) {
$.ajax({
url: 'url_of_nginx...',
headers: {
'Authorization': 'Basic ' + auth_basic
} // send auth header on xmlhttprequest GET
}).next(function ajaxSuccess(data) {
// data from the nginx
document.cookie = '_auth_cookie=' + auth_basic;
// store my auth in a cookie
}, function ajaxFailed(jqXHR) {
// do something on failure, like
showLoginForm()
});
}
function showLoginForm() {
/* function to render your form */
// attach an event handler to form submission
$('#submit_button_id').click(function form_submitted(login_evt) {
// I clicked login
login_evt.preventDefault(); // don't really submit the form
// get my field form values
username = $('#username_input_field').val();
password = $('#password_input_field').val();
// I base64 the auth string
var auth_basic = btoa(username + ':' + password);
// try to auth
getData(auth_basic);
});
}
var auth_cookie = getCookie('_auth_cookie');
if (auth_cookie === undefined) {
// I have no cookie
showLoginForm()
} else {
getData(auth_cookie)
}

Flask JWT Extended set cookie error

I have created an API that is supposed to answer to both mobile devices and web browsers. For example, /web/toys for web and /API/toys for JSON responses. I am using JW Tokens as a means of authentication.
I am displaying forms in HTML and in the background, I call jQuery Ajax's methods to POST to my APIs. I am keeping the access_tokens in the session cookie. To prevent CSRF attacks, I am using Flask-JWT-Extended.
When I decorate a view with #jwt_required and CSRF is set to True, I get missing JWT in headers and cookies, even when the cookies were being set and transferred. I checked the source code and found that it is important to set X-CSRF-TOKEN in the request header. However, since the endpoint answers to both GET and POST calls, how can I set the headers in the GET call without resorting to loading the complete page using jQuery.? Basically, I want to show the form on the webpage, and when the user clicks submit, the form be transferred using jQuery to the existing API. If there is a better way to handle things, I would love to know it.
Thanks!
Author of Flask-JWT-Extended here. As you have discovered, with this extension we are currently doing CSRF protection for every type of request. However, CSRF protection is only really needed on state changing requests: See: https://security.stackexchange.com/questions/115794/should-i-use-csrf-protection-for-get-requests/115800
The benefit of protecting all requests types is that if you have an endpoint that incorrectly changes state in a GET request (there is no technical reason this couldn't happen), it becomes vulnerable to CSRF attacks. Now if the backend is designed more 'up to spec', this is no longer a problem. It sounds like I need to update Flask-JWT-Extended to allow for ignoring CSRF protection on certain types of requests, just like how Flask-WTF operates. I'll try to get this updated today.
Alternately, if your backend is serving JSON instead of html directly (such as a REST api backend and javascript frontend), you can use Ajax to do GET requests with CSRF tokens. In this use case, we could use an Ajax call along these lines.
get (options) {
let self = this
$.ajax({
method: 'GET',
dataType: 'json',
headers: {
'X-CSRF-TOKEN': Cookies.get('csrf_access_token')
},
url: "some_url",
success (result, statusText) {
// Handle success
},
error (jqXHR, textStatus, errorThrown) {
//handle error
}
})
}
EDIT: I also want to preserve the CSRF error messages if the CSRF token isn't present and you are using both headers and cookies for JWTs. Progress on both of these can be tracked here:
https://github.com/vimalloc/flask-jwt-extended/issues/28
https://github.com/vimalloc/flask-jwt-extended/issues/29

Secure ways to implement CSRF free JWT authentification

I have just learned about JWT for authentification. Storing the JWT token in localStorage/sessionStorage is exposed to XSS. Storing it in a cookie is vulnerable to CSRF. I have been researching this and I thought of this solution, but I'm not sure how secure is this and if I should be using it in production.
The soulution is to get the JWT token from server, store it in cookie. Generate a CSRF token (that will be stored in JWT) that will be sent with each HTML page, either in a hidden HTML field of as a global JS variable (). That CSRF token will be sent with every request using JS/AJAX. This way we can rule out CSRF first then verify the JWT token.
I'm not sure whether a new token should be sent with each loaded page, or a single token should be held per session. The 1st case would mean that only the last loaded page/form would be able to submit (which could be problematic if the user is having multiple tabs of other pages open).
Is this a secure solution that might be used in production ?
Also, what other solutions would be viable to reach the same goal ?
Here is my understanding of the issue.
JWT Token - stored in httponly/secure cookie - this ensures any Javascript does not have access to it
CSRF Token - stored by JS - on login, attach the claims as a header (including your CSRF value)
When the JS gets the header, it can store the claim in a cookie, localstorage, or session storage. Because no JS has access to the JWT token in the cookie, by attaching the CSRF with every request and making sure it matches what's in the JWT, you're ensuring that it's your JS sending the request and that it was your backend that issued the token.
A hidden field isn't a great solution because you'll have to add it to each request. Depending on what framework you're using, you should be able to load the CSRF token into whatever is responsible for sending the token as a header on each request when the page is refreshed. The benefit to having all the claims in local storage or a cookie is you can preserve the users front end state. Having the exp claim means you can tell when the user no longer has a valid token in the secure jwt token cookie and you can redirect to a login page.
sessionstorage - is specific to the tab only
localstorage - is specific to the domain across tabs
cookie - is specific to the domain across tabs
Add CSFR to AJAX request:
$.ajax({
type:"POST",
beforeSend: function (request)
{
request.setRequestHeader("CSRF-TOKEN", csrfToken);
},
url: "entities",
data: "json=" + escape(JSON.stringify(createRequestObject)),
processData: false,
success: function(msg) {
$("#results").append("The result =" + StringifyPretty(msg));
}
});
Even though you're not using Angular, this still applies. You should attach the token as a header with the JS and it's fine to get that value from a cookie.
From the Angular JS Documentation
When performing XHR requests, the $http service reads a token from a cookie (by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN). Since only JavaScript that runs on your domain can read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.

Do I have to make a script to make an authorization header for jwt?

I am working on a simple website using jwt. (node.js, koa.js)
Most example codes including expressjs, I cannot find the client-side example
about how to deal with jwt sent from a server.
Only one example (https://github.com/auth0-blog/cookie-jwt-auth) showed me that
[index.html]
... script src="app.js...
[app.js]
$.ajax({
type: 'POST',
url: 'http://localhost:3001/secured/authorize-cookie',
data: {
token: token
},
headers: {
'Authorization' : 'Bearer ' + token
}
After I read this example, I felt that I should have some scripts for users to send an authorization header with jwt. Is it right?
Or are there some front-end frameworks that deal with authorization header?
Thank you for reading newbie'q question.
Yes, you will need to define a mechanism for sending the user's JWT back to the server. It's up to you to decide where the JWT will live in the request -- the most common places are in the Authorization header, or by setting a cookie on the browser (which will be sent along with every HTTP request). You should also consider whether you want the JWT to persist across sessions / page reloads (using for example document.cookie or localStorage).
If you choose not to use the cookie approach, you can configure all $.ajax requests to set your Authorization header "pre-flight" using $.ajaxSetup({...}) (but this is a bit of a sledge-hammer approach). Manually setting the Authorization header on each individual $.ajax request, as you've demonstrated above, is a good option too.
If you want to skip headers all together, you can send the JWT inside the body of your request (as JSON, for example).

JWT (JSON Web Token) with PHP and Angular.js

I have an Angular.js application and I am trying to implement authentication to my PHP backend using a JWT.
I have the app setup to set the token on login and send the token with every request if it exits. I was following the information here, though it is for Node.js not PHP: https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/.
The information there was extremely helpful, but I do not understand why the token should be in the Authorization header with the text 'Bearer ' before the token. Could I just put the token there without 'Bearer '? Is there a recommended method for where the token should go in the request?
My other issue is where to store the token on the front end. The website recommended using $window.sessionStorage which doesn't seem to work well for my case because it seems to prevent someone from using multiple tabs which isn't very intuitive.
My question really comes down to:
Where do I put the token in the request header?
How should I store the token on the front end?
The use of the Bearer keyword is recommended in the RFC6750 - section Authorization Request Header Field:
Clients SHOULD make authenticated requests with a bearer token using
the "Authorization" request header field with the "Bearer" HTTP
authorization scheme. Resource servers MUST support this method
The libraries I've been working with always require it before the token itself. So the request header should be as follows:
Authorization: Bearer your_token
Regarding the storage I have seen it in $window.sessionStorage too

Categories

Resources