Handle "Cancel" subscription on Shopify node/react app - node.js

I followed the Shopify tutorial to create an embedded app using node.js and react. However, I am facing a problem. If the user "Cancel" the subscription, I need to prevent the user to use the app. I have successfully retrieved the subscription status using a webhook, but I am not able to use it on my index.js to determine if the user has approved or not. The problem is that I cannot use cookies inside a POST request.
ctx.res.statusCode = 200;
//COOKIES NOT WORKING INSIDE POST - how to retrieve the subscription status in my index.js so
//I can redirect the user to the subcription url if he didn't accept the subcription ?
ctx.cookies.set('subscriptionStatus', ctx.state.webhook.payload.app_subscription.status, {
httpOnly: false,
sameSite: 'none',
secure: true
});
console.log('received webhook subcription: ', ctx.state.webhook.payload.app_subscription.status);
});

When you receive a cancel webhook in your App, Shopify is giving you the myshopify.com domain name of the store cancelling use of your App. Since your App is receiving the webhook, and since you are storing the name of the shop in your App database, you can simply delete the entry for the shop from your database. That will erase the access token you stored, and hence the person can now not use your App anymore.
Has nothing to do with POST, cookies or anything else really, it is simply you, taking a string, and removing a line from your DB.

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

Authorize a WordPress site and a React app with a web hook and token?

Problem: The client is using MemberPress/Wordpress as their main platform. The users are being managed there. I built a React app to coexist with the Wordpress platform.
What I have done: Memberpress has webhooks. So when a user logs into WordPress I hook into that action with a node server that inserts the user into a custom database and I generate a token so that the user can access the nodes the user owns within my infrastructure.
module.exports = (req, res) => {
const { id, email } = req.body.data;
request(
"https://api.graph.cool/simple/v1/73289237283967",
mutation,
{
wpId: id,
email
}
).then(data => {
res.json({ status: "success" });
});
};
The above gets triggered every time a user logs in. This executes a graphQl mutation that has a custom resolver that checks if the user with that wpId exists. If not it will create a new user. Then it generates a node token https://github.com/prisma-archive/graphcool-templates/blob/master/auth/email-password/src/signup.ts and sends back in the response.
Once I obtain the token I can make requests on the user.
Problem: I am not sure how I can connect the two. I have to redirect the user to the wordpress login page. The login action triggers the event, and a token will be generated. I am not sure how I can store that token in an efficient way that can then be sent back to my app.
My thought would be to write a small plugin inside WordPress to handle the response of the hook and store the token in local storage within WordPress. Then whenever the user clicks to go to the app I can put the token in the URL (feels slightly weird and cant directly go to the URL). I am out of any other thoughts and would appreciate a nudge in the right direction.

Running express-stormpath locally (register doesn't work?)

I'm trying to run express-stormpath on my local NodeJS project, but I cannot do anything to sign up to get my API key. When I simply do:
var stormpath = require("express-stormpath");
app.use(stormpath.init(app, {
web: {
register: {
enabled: false
}
}
}));
I was told by all the documentation, that I should be able to visit http://localhost:3000/login and http://localhost:3000/register to verify the "installation" works, but I'm just getting timed out. My console reads:
Error: API key ID and secret is required.
When I go to the website, I can ONLY log in and when I go to https://api.stormpath.com/register to register an account, it just redirects me to the login page (got the link from here).
What do I do to register an account? This is one of the most frustrating things I have ever dealt with.
Sorry for the frustration.
Stormpath isn't accepting new tenant registrations, because the service is shutting down after the team joined Okta. The functionality of Stormpath will make its way into the Okta developer API.
Source: previously at Stormpath, now at Okta.

RESTful Authentication using Ember and Node/Express and json web tokens, how can I verify users' email addresses?

Here's my workflow:
Ember action on new user signup is to send Express the user data.
Express then creates a web token, encrypts the contents, and puts a link in an email that it sends with Nodemailer.
The email is sent successfully.
User goes to their email and clicks on the link.
On clicking the link, Express gets the token from the query params decrypts and decodes the token, and creates a New User.
All of the above works ok, but here is where I'm stuck. I'd like for the user to be redirected back to the Ember frontend, and automatically logged in. This is the bit I'm stuck on. Here is the Server code:
<!-- language: lang-js -->
signUpUser.save().then(function(model) {
res.set('location', 'http://localhost:4200/login');
res.status(302).json({user:model})
});
I'm able to successfully redirect back but I'm not able to capture the json data in my ember code, and I'm not sure where or how in Ember I can call a login action in the given scenario.
I have a feeling my approach may be wrong? Because email verification is a common thing. Also, I'd rather not have to make users input their form information more than once.
Here's how I'm doing this:
In Express, add query params to the response url after saving user:
signUpUser.save().then(function(model) {
res.set('location', 'http://localhost:4200/login?token=' + token + 'id=' + id);
res.status(302).json({user:model})
});
In Ember, in the /login route beforeModel hook, grab the query params:
beforeModel: function(transition) {
console.log(transition.queryParams.token);
if (transition.queryParams.token) {
this.controllerFor('login').send('assignTokenToUser', transition.queryParams.token, transition.queryParams.id);
};
if (!Ember.isEmpty(this.controllerFor('login').get('token'))) {
return this.transitionTo('courses');
}
}
I'm not sure this is the Ember Way, but the key here is being able to grab queryParams of the transition object.
Can you provide some more information about the authentication system you are using? It sounds like you are using a JWT to convey some information about email verification, but how do you authenticate API requests? Do you use another JWT that is stored in a cookie? If so you want to create this cookie when they arrive with their verification JWT.
Disclaimer: I work at Stormpath and we have a fully-featured email verification workflow in our service. While we don’t have an integration for Ember.js, we do have a good overview of JWTs and Single Page Applications, it may be useful at a high level: Token Based Authentication for Single Page Apps
We do have an Angular integration, if you have the option to switch frameworks: Stormpath AngularJS SDK

Polling Google sites for changes

I need an event that is being fired whenever something changes on my google sites site.
There is an XML feed with all the latest changes. I am planning on polling this feed with Zapier and when something changes Zapier will make a http request to a url that I provide so I get my event.
This works fine as long as the site is public, but in my case it is not.
So I think about building a proxy for the feed that google provides. The proxy will call the feed with the proper authentication and pass the contents. Zapier will call the proxy with a Zapier friendly authentication mechanism.
I figured that I need to call the google feed with a service account. So how do I do that with node.js?
I have been looking here:
https://developers.google.com/google-apps/sites/docs/1.0/developers_guide_protocol#ActivityFeed
Figured it out!
I have made a proxy with basic authentication that is accessed via https. This proxy will pass through the activity feed of the Google site that I am interested in.
I built the proxy in node.js with the googleapis module. Here is a piece of the code I use:
var jwtClient = new googleapis.auth.JWT(
client_id_email,
__dirname + 'key.pem',
null, ['https://sites.google.com/feeds/'],
'user#domain.org'
);
jwtClient.authorize(function(err, token) {
if (err) return next(err);
return rest({
path: 'https://sites.google.com/feeds/activity/{domainName}/{siteName}',
headers: {
"GData-Version": "1.4",
Authorization: token.token_type + ' ' + token.access_token
},
params: {
domainName: 'domain.org',
siteName: 'site',
},
}).done(function(result) {
res.set(result.headers);
res.send(result.entity);
}, next);
});
The username (user#domain.org) in the example is a user that the service account impersonates. This user must have acces to the site.
I put the service account key in a file name 'key.pem' in this example.
Now you must allow accesd to the service account for the domain you want to access. You do this on the admin site of the domain (admin.google.com).
Go to security
Go to advanced settings
Go to API client access
There you have to add the client_id of the service client
And now... it works :-) !!! \o/

Resources