How do I track Stormpath authentication session with Stripe status? - node.js

I'm currently using express-stormpath for authentication with stormpath in my node.js application. I'm also using stripe. I'm attempting to figure out how to store sessions correctly. Here's what I plan to store in a user session:
session: {
sp_user: { //stormpath user details }
db_user: { //details from my DB }
stripe_expiraton: '234253843923' // unix timestamp
}
So on login I'm planning to create a session for the user with a 7-day expiration. On every API call express middleware will check to see if the stripe expiration date has arrived. If it has, or if there is no expiration date that means we need to redirect the user to the payment page. Otherwise will assume everything is normal and paid up and carry on.
Should I be storing session information this way? I'm not super experienced with session management.

What I'd recommend you do is store this information in your User Account's custom data store. Each Account in Stormpath lets you store any JSON information you want.
This is going to be a much better solution that storing your data in a session, as it might get wiped by the user at any time if it's only client-side.
Also: for Stripe, it doesn't really make sense to store a timestamp in most cases. The way you typically do billing is:
Collect the user's billing information.
Send it to Stripe using their JS APIs, you'll then get back a token.
Send the token to your Express app.
Use that token to create a Stripe Customer Object. This way you can bill the user in the future.
Store the Stripe Customer ID in your Stormpath Custom Data, this way you know how to bill the user later on!
With the above flow, you can automatically bill the user when you need to, instead of redirecting them to the payment page all the time. This also means you don't need to worry about session expiration, etc., and only need to redirect the user to the payment page if their card is expired or no longer valid.
Here's how to do it inside of a route:
app.post('/blah', stormpath.loginRequired, function(req, res, next) {
req.user.getCustomData(function(err, data) {
if (err) return next(err);
data.stripeCustomerId: 'xxx'
data.save(function(err) {
if (err) return next(err);
});
});
});
The above code will save your Stripe Customer ID inside your Stormpath account.
You can then retrieve this data at any time by saying:
req.user.getCustomData(function(err, data) {
if (err) return next(err);
data.stripeCustomerId; // this will be the value you've previously stored
});
Hope that helps =)

Related

Node.js - Handling a webhook and updating an express-session

I'm using Stripe to let my customers change their plans, and we send them to an external Stripe-hosted page to complete the checkout. After they checkout, Stripe sends them back to a thank-you page and initiates a webhook.
The webhooks are great, because it doesn't update any database information or changes their plan until after the payment has succeeded and their card has been charged.
I also use webhooks to "reset" their plan limits on a successful card charge. So each month, on each successful charge webhook, the limits update. But if the user is in an active session when they update, I should update that session.
BUT what I'm struggling with is, in a webhook event how do I update an active express session?
There's no req.session object, because the "event" is coming from Stripe's webhoook.
app.post('/webhook', async (req, res) => {
/* <-- Update the database, which is good --> */
database.updateUser()
/* <-- Update their session if it's active --> */
// This won't work, because there is no req.session and no cookie being sent!
req.session.user.plan = parseWebhookData()
// I can't access the store, because I don't know the user's session id
// Plus, all my store is encrypted so I can't do a MongoDb query search for their session.
store.get(sid, callback)
// How do I update their session??
})
The only solution I have so far is to have the database re-update their information on every page they visit. But that slows down my response time by 1s per page!
I solved this myself.
Solution #1: Recommended by Stripe: Update user on login, not with the webhook
Stripe recommends checking a user's subscription on login, and resetting their "plan usage" on login INSTEAD of resetting their plan usage on a webhook.
I thought this was very interesting and after implementing I've found this is a much better solution than trying to deal with Webhooks and session objects!
Solution #2: Get the session ID and update the express-session database!
Express-session gives you the sessionID in "req.sessionID" so you can search your database for the sessionID and update what you need to.
app.get('/getID', (req, res) => {
console.log("sessionID: " + req.sessionID);
res.send(req.sessionID);
});
It also helps to do MongoDB's atomic updates if you turn off the stringify feature in your express-session.
const store = MongoStore.create({
stringify:false
});

firebase.auth().currentUser returning null

In the html file that I have for the sign-in page, I perform the authentication using Firebase and on successful authentication, I redirect the given user to the homepage. When I call firebase.auth().currentUser in the express file, I use for rendering and routing pages, I get undefined or null for the current user.
Can anyone help me understand what the issue might be?
This is how I perform the authentication:
firebase
.auth()
.signInWithEmailAndPassword(temail, tpass)
.then(function(firebaseUser) {
window.location.href = "http://localhost:5000/homepage";
})
.catch(function(error) {
window.alert("incorrect pass");
});
This is the code that I have in my express file:
app.get("/homepage", (req, res) => {
var user = firebase.auth().currentUser;
console.log("USER IS " + user);
res.render("menu", { title: "Welcome" });
});
Backend code doesn't have a sense of "current user". When you sign in on the frontend, the current user is only known on that client. It isn't known on the backend. If you want the backend to know which user is signed in, the client will have to send an ID token to the backend for it to verify. The documentation for the Firebase Admin SDK is used for that on the backend. The client must send the ID token to in the request to your route, and the code handling that route must verify the token in order to know the user that made the request. From the documentation:
If your Firebase client app communicates with a custom backend server, you might need to identify the currently signed-in user on that server. To do so securely, after a successful sign-in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity and authenticity of the ID token and retrieve the uid from it. You can use the uid transmitted in this way to securely identify the currently signed-in user on your server.
When the user lands on a new page, Firebase automatically restores their previous authentication state. But to do so, it may have to contact the server, which means that it may take a few moments. While Firebase is restoring the state, auth().currentUser will be null.
To ensure you get the correct authentication state, you should use an authentication state listener, as shown in the documentation on getting the current user:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});

Should I verify a JWT token information from a Database?

I'm currently trying to build a REST API using express, node, and MongoDB. Now for authentication, I'm using JWT.
Here is the code for checking JWT token
const token = req.headers['authorization'];
if (token){
const tokens = token.split(' ');
const key = tokens[1];
jwt.verify(key, config.jwtKey, (err, authData) => {
if (err){
res.status(403).json({
success: false,
message: "Authentication2 failed"
});
}
// User authenticated
// Do something
next();
});
} else {
res.status(403).json({
success: false,
message: "Authentication failed"
});
}
Now, this code is working perfectly.
For making the JWT, here is the code
........
........
const token = jwt.sign(
{
email: user[0]._email,
userId: user[0]._id
}, config.jwtKey,{ expiresIn: "1d" });
........
........
Now my question is should I also verify the user by checking the existence of the user's information in the database to make it more secure?
For example, searching the email and userId to my database.
I'm using node, express, MongoDB, Mongoose, and JWT for this project.
It's an old question but I want to leave an answer:
Yes! Everytime your client make an api request, backend should verify both the validity of the token and the presence somewhere in your backend (for example a db table).
Consider always the most dangerous scenario: bank account
What happens if someone steal your device?
You should be able to invalidate the token from another device and change the password.
The advantage of using a token is that the server can
verify it quickly without calling out to an external data store like MongoDB.
But if you're going to add a business login to your API authentication like a blacklist/whitelist of revoked tokens then you have to use a store to verify the token and user details, (will be slower than not doing a remote call for each token but you have to do it with low latency).
For low latency you have to use DB like Redis, Dynamodb would probably be fine and more secure without major latency between your DB and your API server.
Is not required to verify the signature with DB, and you can settle for JWT algorithm
Verifying (the signature of) the token using the selected algorithm is enough to ensure that this user exists (or existed) on the system because it was the system who generated the token in the first place.
But there are cases when that's not enough, for example, blacklists as mentioned in Roy G's answer, or if the users' claims have been changed or completely deleted from the system but they are still using an old token (not expired yet), they could still have access to the system, so checking against DB would prevent that access.
Setting a small expiry date in combination with refresh tokens is generally a good practice to prevent those kinds of leaks.

How implement an access token, so I can bypass login page (sailsjs)

I have a SailsJS website for which I implemented authentication through a form where user needs to fill in email and password. copied from ActivityOverloard 2.0 example code
Login
login: function(req, res) {
console.log("Login hehe!!");
// Try to look up user using the provided email address
User.findOne({
email: req.param('email')
}, function foundUser(err, user) {
if (err) return res.negotiate(err);
if (!user) return res.notFound();
console.log("found email");
// Compare password attempt from the form params to the encrypted password
// from the database (`user.password`)
require('machinepack-passwords').checkPassword({
passwordAttempt: req.param('password'),
encryptedPassword: user.encryptedPassword
}).exec({
error: function(err) {
console.log("There was an error with password");
return res.negotiate(err);
},
// If the password from the form params doesn't checkout w/ the encrypted
// password from the database...
incorrect: function() {
console.log("Password doesn't checkout w/ the encrypted");
return res.notFound();
},
success: function() {
console.log("Good password");
var now = new Date();
User.update(user.id, { online: true, lastLoggedIn: now }, function() {
// Store user id in the user session
req.session.me = user.id;
User.publishUpdate(user.id, {
online: true,
id: user.id,
name: user.name,
lastLoggedIn: now,
action: ' has logged in.'
});
// All done- let the client know that everything worked.
return res.ok();
});
}
});
});
my page is protected with login
myPage: function(req, res) {
if (!req.session.me) {
return res.view('login'); // not authenticated will take you to the login page
}
// It's authenticated, it runs the code below
// DO SOMETHING
Now a very special use case, I need to open my page without user interaction (It can't be through a form) but I still need it to be protected. I'd need to pass some kind of access token.
I understand that passing an "access token" as query param is most probably not a good idea isn't it?
In fact, I don't know how to resolve my problem and allow to access myPage other than a session based authentication through a user interaction via a form ...
It seems to me that I'd need to first get a token programmatically and then open a browse to my page ... I bet there is some best practices to address my problem out there.
Any pointers? may be someone can fill the knowledge gap.
Realisticly speaking, you have multiple options with regards to passwordless or formless logins in node.js/express.js and therefore sails.js, as sails is built on top of both.
How you would approach the solution, really depends on the scale and use of your application/applications. For example; will the same login credentials be used to access multiple applications or a single web application, will the application be available only in an intranet or across the whole WWW.
Regardless of the scenario above, there will next to always be some initial setup required by the user, whether that is an initial sign up with an identity provider or an initial sign up with your application. The sign up form, will not dissappear entirely, rather it will become a one time event.
So let's look at some options and how we might introduce them into an express/sails application/s, I will start with the most basic and work down in difficulty.
Option 1:
Make use of the sails session store. From your code, you have already started doing this. The logic works something like this:
Your user signs up or logs in for the first time. At this stage you set the users session to be authenticated.
// Store user id in the user session
req.session.me = user.id;
req.session.authenticated;
You set a policy on all the pages which require authentication. Luckily, sails has already done some of the heavy lifting here by creating a sessionAuth policy in the folder api/policies. To make use of them open the config/policies.js file and add this policy to your protected pages
'my_app' : {
'route_to_protect' : 'sessionAuth'
},
Finally, you will want to make this session cookie last a really long time, to do this open config/session.js and edit the cookie maxAge to suit your needs. For example, if you want to force the user to login every 365 days, you might do some like this:
// milliseconds * seconds * minutes * hours * days
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365
},
The draw back to this option is that your sessions will be lost if the application is restarted and all users will have to log in again.
Option 2:
Use a simple third party library like passwordless. Passwordless offers token-based authentication for express web applications and as sails is built on top of express...
The general jist of passwordless is when a user signs up, you deliver them a link to your application via email, this will log them in and in turn set up there session. Passwordless makes use of mongo as a session store, so you can either install mongo or use something like mLab which is a Mongo Database-as-a-Service provider. For a complete run through on using passwordless, take a look at their getting start page here.
Now for the more featureful based options.
Option 3:
If you are developing an application that is public facing, making use of Passport.js with sails is a great option.
Passport is authentication middleware for Node.js. Extremely flexible and modular, Passport can be unobtrusively dropped in to any Express-based web application. A comprehensive set of strategies support authentication using a username and password, Facebook, Twitter, and more.
Passport works with Sails just like it does with Express.
There are already a shed load of guides on setting up passport out in the ether. But a great step-by-step is available here and is also the one referenced by sails in there official documentation here.
Passport is in all essence an authentication middleware. It allows users to identify themselves based on this authentication, you can develop the correct authorization functionality in your application.
Option 4:
Make use of SAML or OAuth. From a development and implementation perspective, these are by far the biggest undertaking out of the options provided.
SAML and OAuth are authorization middleware which refers to rules that determine who is allowed to do what. Both have a very similar setup and make use of an Identity Provider(IdP) and Service Provider(SP), where the IdP represents an online service that authenticates users in the flow and the SP represents an application that relies on a trusted IdP for authentication and authorization.
I am more familiar with SAML, so what follows is with reference to considerations when implementing SAML in a project.
You will first need to register your application (SP) with an IdP. With regards to IdP's, what you choose is based on the scale and requirements of your application, there are free online IdP's like ZXIDP and SSOCircle or if your application required a dedicated IdP you could look at something like OpenSSO. You could also consider creating your own Node.js IdP using the saml-idp package.
Integrating SAML into a sails application is not overly difficult. Make use of the saml2-js package.
Once all configured, the logic works something like this.
User opens their web-browser and goes to yoururl.
To authenticate the user yoururl constructs a SAML Authnrequest, signs, encrypts and encodes it.
Then yoururl redirects the user's to the IdP to authenticate.
The IdP validates the request, in the first signup/login, the IdP will ask the user to enter their username and password, after that it will use the sessioning and other than the address change in the browser address bar the user will not see much.
If the user is successfully authenticated, the IdP generates a SAML token that includes information about the user (username, etc) and redirects them with this token back to yoururl.
Finally yoururl verifies the SAML token, extracts the identity information about the user including authorisations and logs them in.

How do I make Instagram OAuth (or any OAuth) always force the user to sign in, even if they are signed into the application's website?

I'm building a tool where a user would want to authenticate multiple Instagram accounts into the application. The problem I run into is if the user has already authenticated one and I initiate the OAuth dialogue again, the OAuth assumes that I want the access token of the user already logged in.
I have an iOS app that is similar and the way to avoid this is clear all the cookies of the Safari browser.
I'm using the instagram-node module right now.
app.get('/authenticateInstagram', function(req, res) {
res.redirect(ig.get_authorization_url(redirectURI, {
scope: ['basic', 'public_content', 'likes', 'follower_list', 'relationships'],
state: 'a state'
}));
});
app.get('/handleInstagramAuth', function(req, res) {
ig.authorize_user(req.query.code, redirectURI, function(err, result) {
if (err) {
console.log(err.body);
res.send('Didn\'t work');
} else {
console.log('Yay! Access token is ' + result.access_token);
res.send('You made it!!');
}
});
});
So when I try to add another IG account (now that I've signed into an Instagram account already), I don't get prompted to log in by the OAuth sequence. It assumes I'm the previously signed in user.
I can think of the following approaches
You can make a request to logout end point if Instagram supports it. Once you logout you can login again
I am not sure if instagram supports prompt=login parameter. If so it should take you to login page for each call.
I checked Instagram documentation and I think you can use Client side (Implicit) flow to login every time to get access token.

Resources