How to store authentication token with node and express - node.js

I have two nodejs app for client and server. Client side doesn't rely on any libraries for building interfaces. It uses pure html and express routes to the requested page.
router.get('/', function (req, res) {
res.sendFile(path.join(__dirname + '/index.html'));
});
Client sends an auth request to the server via AJAX call and receive the auth token back. I need to store it somewhere and make sure express checks if token is available or not to reroute user to login page if unauthenticated (token is not available)
I was thinking to use localStorage but it is not available/accessible under express.
$.ajax({
url: 'http://server:8080/login',
type: "POST",
data: {username, password},
dataType: "JSON",
contentType: 'application/json',
success: function (data) {
localStorage.setItem("jwtToken", data.jwtToken);
},
});

Maybe your should consider store it like a cookie and all the new requests to your server will contain the necessary information to verify the auth of the client.

So that developers don't have to deal with tokens and other aspects of an authentication system, I created an npm package. You can check if the user is logged in or not with this package as well. Here is the link:
https://www.npmjs.com/package/authenticatejs

Related

How cookies are handled in react-native?

I have a web app running with node js and passport.js and the authentication flow is working well.
I'm trying to develop a react-native and to make the same authentication flow (with passport.js).
I changed the passport code to redirect back to the react-native app (with Linking) and it worked.
so the flow is:
Open browser with the login url (/auth/google)
User logged in
Redirected back to native app
send a request to verify the user is logged in - but the user is not logged in, I think because the cookies were not sent to the server
I also tried adding to the fetch credentials "same-origin" or "include" but still the user is not logged in.
Some code I used:
Linking.openURL("http://<my ip>:3000/auth/google"); //for log in
app.get('/auth/google/callback', //handle the log in with passport js
passport.authenticate('google', {
failureRedirect: '/login'
}), function(req, res) {
res.redirect('MyApp://login); // redirect back to native app
});
fetch("http://<my ip>:3000/api1", {credentials: /*"same-origin"*/"include"}) //get 401 -> user is not logged in
Am I missing anything?
How cookies are handled in react-native? Is it like in the web? how the cookies should be passed from the browser to the native app after redirect?
React Native is not browser environment. It doesn't automatically handle cookies like browsers.
You have to use some cookie-aware request library such as superagent.
Example usage (from superagent documentation):
const agent = request.agent();
agent
.post('/login')
.then(() => {
return agent.get('/cookied-page');
});
Then all requests from agent will handle cookies automatically (shares the same cookie jar).

Trying to redirect client to Google OAuth, getting 405 error

I'm try to create a Web Service that connects to the Google Calendar API. In attempting to authorize my app, I have generated a url with necessary scopes. The problem is when I try to redirect the client to the generated url, I am getting a 405 error with the following message:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8080' is therefore not allowed
access. The response had HTTP status code 405.
For the most part I have been following this guide: https://developers.google.com/identity/protocols/OAuth2WebServer using the node.js client library.
From what I understand, it seems that Google has not configured their server to accept cross origin requests, what I don't understand is how I am supposed to redirect my users to their authorization page if I cannot send a request from my domain.
Here's the relevant code:
export function authorize(req, res, callback): any {
let auth = new googleAuth();
let oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
if (// Check if we have previously stored a token.) {
oauth2Client.credentials = //get token;
callback(oauth2Client);
} else {
//redirect to google authentication page
let authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
state: '/',
scope: SCOPES
});
res.redirect(authUrl);
}
}
Turns out when AJAX receives a 300 status code, the browser will automatically send another GET request to the returned location on behalf of the client. Even if I had gotten around my CORS issue I would have then had to manually load the html page myself since the request was made on behalf of the client. What I really want is for the browser to make the request to the auth uri.
So my solution to this problem is to detect redirects on the front end and have the browser issue a completely new GET request. Still seems like a work around so if anyone else has a better solution I'd like to hear it.
If some one use React or similar SPA Framework to solve this problem related to OAuth 2 read this:
Do not redirect on the server side. Don not do this (node.js/express as backend example):
router.get('/go', errorHandler(async (req, res, next) => {
res.redirect(authenticationUrl);
})
);
DO THIS:
router.get('/go', errorHandler(async (req, res, next) => {
res.json({
redirectUrl: 'https://google.com'
})
})
);
And then on your frontend client, get the response and make redirect. Example:
getUrl = async () => {
try {
const res = await API.get(`/go`);
window.location = res.data.redirectUrl;
} catch (err) {
console.log(err);
}
}
Work in React. Checked!
As per the flow, it is shown like Browser (APP) -> API (Server) -> Google auth (redirection), It is a very common trail with Identity provider servers like AWS, Google Auth, etc.. as they never send an additional header of ALLOW ORIGIN that's why browser reject the request when it comes to the point of redirection
Now comes the workout solution.
The App domain (http://localhost:3000) should be added as a trusted domain at the IDP server
Request to API server for Auth initiation should be
window.location.href = 'http://localhost:5000/api/v1/auth/login-google';

Reverse Proxy login with credentials from node.js

I currently have a server running Spring in a Tomcat servlet with Shiro Token system for checking if a user is logged in already. It allows cross-domain requests.
In any other domain I can on the client (generally using angular) call...
http.get('https://<my_check_login_service>', {withCredentials: true})
...and as long as I am already logged in (token doesn't expire) return the user info (name, avatar, etc.).
I have another system now that is a node server (also serving up angular for the client side) for which I would like to call the node server and have it proxy over to the above my_check_login_service to get the user, set info on the session object (using express), and then return the user to the client. But also, through the session object, allow me to trust their connection and allow them to perform further api calls depending on the security level of the user returned from the login service.
On the node.js router I can proxy doing this ...
app.get('/checklogin', function(req, res) {
req.pipe(request.get("https://<my_check_login_service>").pipe(res);
}
...but I don't know how to pass the proper credentials to the service. If I do ...
http.get('checkLogin', {withCredentials: true})
...it, of course, doesn't work because the credentials for my login_service are not sent to the local server. How can I pass the correct credentials to make this work?
Cheers.
Credentials are most likely in the HTTP headers, passing all headers (both from request and to response), with the address of original request, should make it work:
app.get('/checklogin', function(req, res) {
console.dir(req.headers)
//You can inspect the headers here and pass only required values
const options = {
url: 'https://<my_check_login_service>',
headers: Object.assign(
//Tell the login service about address of original request
{'X-Forwarded-For': req.connection.remoteAddress}
req.headers)
}
req.pipe(request.get(options))
.on('response', (response) => res.set(response.headers))
.pipe(res)
}
This example passes original address by setting X-Forwarded-For, login_service may recognize it ...or not, depending on the configuration.

redirect to another app with session token (jwt) in AngularJS and NodeJS

I have a startup module in angularjs. This module is just to login and have public information (login, prices, newsletter...). I have many roles and for each role, i have an app (angular module). I made this architecture because i have complex module for each role and it was impossible to put all roles in one module.
So, for login, i use jsonwebtoken in node like this :
var token = jwt.sign(user, config.secureToken, { expiresInMinutes: 20*5});
res.json({ token: token, user: user });
It works perfectly. I can login into my app. After that, i have to propose a list of roles to redirect to the right module.
In angular, I have AuthHttp service that adds security headers (with token) to call rest service with $http.
How can i redirect to 'mydomain:port/anotherModule' with $location or $http ?
With this code in nodejs :
app.get('/secondModule', expressJwt({secret: config.secureToken}), function (req, res) {
res.render('restricted/secondModule/index.html');
});
NodeJs sends an html code in response and does'nt redirect...
And if i do this in my angular controller :
location.href = route;
i have this result on nodejs console :
Error: No Authorization header was found
I am not sure about the libraries you are using, but issue seems that you are loosing the token because you navigate to a altogether new page.
Based on your auth library you need to pass the token that you get after auth from one page to another.
The options here are to either use browser sessionStorage or querystring to pass the token along and at it back to the http header collection on the new page (module)
This is an old post but I recently took a long time to figure this out. I may be wrong but I believe nodeJS/expressJS can't read the token from the session storage. I believe you will need to pass the token via the request header using AngularJS.
This depends on the front end that you are using. For me, I am using AngularJS and I have to do something like this.
angular.module('AngularApp').factory('authFactory',
function($window){ //the window object will be able to access the token
var auth = {};
auth.saveToken = function(token){
$window.localStorage['token_name'] = token; //saving the token
}
auth.getToken = function(){
return $window.localStorage['token_name']; //retrieving the token
}
return auth;
}
.service('authInterceptor, function(authFactory){
return { headers: {Authorization: 'Bearer "+ authFactory.getToken()}
} //the last line gets the retrieved token and put it in req.header
Then, you just need to include 'authInterceptor' in all the http methods when you communicate with the backend. This way, nodeJS will be able to pick up the token.
You can see the Authorization field in req.header if you use the chrome developer tool and look at the Network tab. Hope this helps.

Authenticating Zombie.js Browser against Windows Authentication

I am able to run my mocha tests using Zombie.js on my local project, but am getting a 401 error while attempting to run it on one of our dev servers.
The dev server this has to go on is Windows Server 2003. This is an organizational choice rather than my own.
This server has windows authentication for that domain that the app needs to sit on and is the reason I was getting the 401 error.
I tried setting the authentication as suggested by the zombie docs like so:
browser.authenticate().basic("user", "password");
This did not work.
Any suggestions?
browser.authenticate().basic() in Zombie.js carries out HTTP basic authentication. Your site is protected by Windows authentication, i.e. NTLM. These are different and incompatible protocols. You need to use something like node-http-ntlm, and either figure out how to interpose it in your tests so that Zombie.js can use the session it establishes, or extend the authentication abilities of Zombie.js to include NTLM.
You can try this code
In addition, the library also provides helper methods for encoding and decoding the headers used during NTLM HTTP authentication. This functionality should presently be considered experimental.
source
// npm install ntlm request agentkeepalive
var url = "https://.../ews/exchange.asmx"
, domain = ...
, username = ...
, password = ...
var ntlm = require('ntlm')
, ntlmrequest = require('request').defaults({
agentClass: require('agentkeepalive').HttpsAgent
});
ntlmrequest(url, {
headers: {
'Authorization': ntlm.challengeHeader(hostname, domain),
}
}, function(err, res) {
ntlmrequest(url, {
headers: {
'Authorization': ntlm.responseHeader(res, url, domain, username, password)
}
}, function (err, res, body) {
console.log(body);
});
});

Resources