How to clear http basic Authorization credentials saved in the browser - javascript

In an angular application, I send an XHR login request to a stateless rest server with http auth header:
http
.get(`${site.url}/login`, {
headers: {
Authorization: `Basic ${btoa(`${username}:${password}`)}`,
},
withCredentials: true,
})
Anytime I request the same server then, I don't need to send the credentials again: they are saved automatically by the browser. This is nice, this way I don't have to save the credentials on the browser (the server is stateless, so there is no session nor token available).
However this introduces a security issue: when the user logouts (this is a simple boolean stored in the browser), his browser is still able to connect to the server: in case of XSS someone could connect again.
Is there any way, from Javascript, to clear these credentials? I saw some hacks (request the server with bad credentials...) but I am looking for a consistent & safe way.

Headers are immutable - they can't be changed. However you can use a technology called JWT (JSON Web Token).Here a link for JWT's website.

Related

Cookie token sending to server via header

This is my first attempt to dealing with server
Using React and express
Saving token to browser cookie from server
//After login request
res.cookie('token', token, {maxAge: 3600000} ).json({ user: userDoc, message: 'message!', status: 200 });
Question is here
How can I send token value from client along with each API request for further app process?
After research I found two options!.
Option A
//Collecting data from cookie
let token = Cookies.get('token');
axios.get("/api/, { headers: {"Authorization" : `Bearer ${token}`} });
Option B
Here is my confusion that I have read ,that browser send cookie along with each api automatically without manually adding in header
I have tested in server let { cookies } = req; and its working
Question
Which option is the correct way ?
If yes A or B - why? and why not?
Considering Option B is relying on server for dealing with cookies.
When dealing with sensitive data such as authorisation tokens, you must choose option B.
This is because the client side scripting should not get access to the sensitive data by any means.
To prevent client from accessing the cookie, use HttpOnly flag in your call to res.cookie.
HttpOnly cookies cannot be accessed by client side scripting. So it is pretty safe to use.
Even if data is not sensitive, you should go with option B. Since your browser is automatically sending the cookie payload, you don't have to deal with it manually.

How to stop React-Native-Windows from asking for HTTP Basic Auth credentials

I'm using React Native with the plugin for the Universal Windows Platform to access remote resources on a REST server.
When doing a fetch request for a resource that requires authorization via HTTP Basic Auth, I can provide the request with an additional "Authorization" header and everything works fine as long as the credentials are correct.
If the credentials are wrong I'm presented with a Windows-native login prompt (similar to the one when connecting to a remote computer). This prompt is not managed by my app, but automatically seems to pop up when the underlying network connection detects a 401 Unauthorized server response.
Here is what I do inside React Native:
let encodedCredentials = new Buffer(this.state.username + ":" + this.state.password).toString("base64");
let response = await fetch(this.state.serverUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': "Basic " + encodedCredentials,
}
});
let responseJson = await response.text();
alert(responseJson);
The server response, when provided with incorrect credentials, includes:
Status Code: 401 Unauthorized
WWW-Authenticate: Basic realm="iOSResource"
Note that the native login prompt seems to delay the fetch request as a whole. I can enter wrong credentials and confirm the prompt multiple times without the alert firing once. Only when the prompt is explicitly cancelled, the correct credentials are entered or wrong credentials have been tried a couple of times the fetch await continues.
Unfortunately this brings up another issue: When entering wrong credentials in the popup prompt a couple of times and it finally "gives up", I can supply whatever credentials I want to the fetch request, it will not supply my own authorization header to the server in any future requests. The data sent will be stuck to whatever I entered in the prompt before it closed. In this case it does not bring up the prompt again and the request just immediately fails. That leaves me unable to correct the credentials in my own app, because they are simply not sent within the request. I have confirmed this by inspecting the outgoing data in Wireshark.
I guess Windows seems to transparently tamper with the network request to intercept special response codes and re-prompt credentials if necessary before returning the request result to the actual caller for the first time.
I want to deal with incorrect credentials in the app, instead of causing Windows to intercept requests. Is there a way to suppress this native prompt and immediately proceed with my own code in case the Basic Auth fails?
Edit: The behavior is exactly the same when using Axios instead of plain fetch. Seems like both ultimately do a XMLHttpRequest, which is filtered the same way.

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)
}

What is the best way to access the JWT from node?

I have implemented JWT authentication using Node.js. When the user signs in, Node.js signs/creates a JWT and sends it back. Thereafter, it is stored in the localStorage. Now, this is probably where I am going wrong, but... to move forward, I make use of the express router, and within the router code (which is obviously at the node level) I want to be able to access the token (which is in localStorage) so that I can make a call to the API for further data. However, I just realised that localStorage is at the client-end and that node/express/router doesn't recognise localStorage. So I am stuck. Obviously, I am doing something fundamentally wrong... I should not need to access localStorage from the express router file. Perhaps, I should really be making the API calls not from the express router file, but from client side.
Any hints/directions?
localstorage is bad way to save token. you should save token in cookies and use then where you want.
EXAMPLE:
new Cookies(req,res).set('access_token',token,{
httpOnly: true,
secure: true // for your production environment
});
and then read:
var token = new Cookies(req,res).get('access_token');
You need to send the JWT that is stored on the client side every time you make an API request to the server side.
https://jwt.io/introduction/
Scroll down to the section How do JSON Web Tokens work? The JWT should be sent in the header of the API calls in the form:
Authorization: Bearer <token>
How you do this depends on how exactly you'll send the HTTP requests to the API, but it should be pretty simple in any respects. You can find out about how to add Headers to an angular $http request at this link:
https://docs.angularjs.org/api/ng/service/$http
Then it's up for each of your authenticated express routes to check the headers, pull the JWT out, ensure that it's valid, and then proceed with the request (or halt it if the JWT is invalid).

How to manage JWT when Cookies are disabled

I've been reading articles about JSON Web Token (which is completely new to me) and its safe mechanism to transmit information between parties in order to avoid server Sessions.
I'm building a web app from scratch using Java, Tomcat, Jersey framework for Web Services and JOSE4J for the JWT.
Many articles advice to use Cookies httpOnly instead of localStorage
I've already created a restful method like this with a cookie and the jwt
#GET
#Path("/authenticate")
#Produces(MediaType.APPLICATION_JSON)
public Response authenticate(
#HeaderParam("username") String username,
#HeaderParam("password") String password) throws JSONException,
IOException, JoseException {
Service service = Service.getInstance();
EmployeeProfile employeeProfile = service.authenticate(username, password);
// Temporarily httponly and secure as false to test
NewCookie cookie = new NewCookie("jwt", service.getToken(), null, null, null, 900, false, false);
return Response.status(200).cookie(cookie).entity(employeeProfile).build();
}
return Response.status(204).entity(null).build();
}
When I run my webapp in Chrome I can see that the cookie was saved correctly.
Now I can use this token to call further restful methods with no need to authenticate again, but what if Cookies are disabled? I cannot retrieve the cookie as I tested in incognito mode. In that case I can verify if cookies are enabled and warn the user to enable them in order to proceed with the login process.
To check cookies I do this:
$.cookie('test_cookie', 'cookie_value', { path: '/' });
if ($.cookie('test_cookie') !== 'cookie_value') {
//Cookies are disabled. Show a modal.
}
But this is very restrictive. So I wonder what would be my alternative to retrieve the jwt from server? I am not very sure about this, but should I change the controller to send the jwt as a part of the response in json and keep it in the localStorage even if this can expose my token to XSS attacks? However, using cookies can be also susceptible to CRSF attacks but not if I set httpOnly and secure properties to true, but in that case I won't be able to read the cookie with javascript. I am confused about this.
Thanks in advance.
You are right , You need to change your controller and send the JWT is part of the response as well as the cookies with flag httpOnly due securities, but the question is are decrypting JWT in client side and using some value from there. if "No", then no need to send JWT as part of the response.. if "Yes", better take out all that values from the token and send a separate json object in response header.

Categories

Resources