Lots of folks were curious how I connect Gummroad and Auth0 on my custom course platform so here’s a short tutorial. The same approach works if you swap Gumroad out for Stripe ✌️

Here’s the tl;dr:

  1. User visits your JAMStack app (Gatsby for me)
  2. User clicks Buy Button
  3. Gumroad checkout pops up
  4. User purchases
  5. Gumroad calls a webhook
  6. AWS Lambda wakes up, creates account on Auth0
  7. User is redirected back to JAMStack
  8. User can now login

You can replace the Gumroad popup with a redirect to Stripe Checkout. We use that approach in the example app for my ServerlessReact.Dev course.

If you want to support free trials, you can use the AWS Lambda to add a role for existing users instead of creating new accounts. That way people can login, use your app, and get extra permissions once they buy.

You can watch me figure this out from scratch on a stream:

Click through for source

Start with a buy button

Assuming you’ve got a JAMStack app – Gatsby, NextJS, or even CreateReactApp are all fine – a buy button is just a bit of React. I like to use Rebass buttons.

Click through for source

This renders a button as a link tag, links to Gumroad, and enables the Gumroad popover. Users can purchase without leaving the page and if that doesn’t work (no JS, weird browser, etc), they go to Gumroad’s checkout page.

You’ll need to include Gumroad’s script tag on your page. ReactHelmet works great for that.

AWS Lambda Webhook

The webhook is where things get interesting.

Gumroad supports a Gumroad Ping, which is a POST request sent to a URL of your choice on every purchase. Stripe calls it a Stripe Webhook.

Webhooks let you do stuff on important events in 3rd party services. Creating a user in our case.

I set my hook up using Serverless with AWS Lambda. NextJS’s /api directory would be a good choice too. Or Netlify’s cloud functions. They’re all the same thing – a JavaScript function that runs when a URL gets a POST request.

Click through for source

This config creates a new service, uses Node 12, adds some permissions, and creates a gumroadping AWS Lambda function.

A benefit of using AWS directly is that you can use AWS Secrets Manager to securely store API keys and keep your infrastructure config in this one file.

sls deploy spits out a URL. Gumroad sends a POST request and AWS runs your code.

pingHandler

pingHandler is where that work happens.

Click through for source

A couple things happen here:

  1. Parse request body with querystring
  2. Check product against a constant whitelist
  3. Create or update a user on Auth0 with upsertUser
  4. Add authorization role to user
  5. Say all went well

We always return success and assume errors will throw. When your code throws AWS responds with a 500. That’s fine.

We’re also hardcoding the Student role because Auth0 doesn’t work with role names and it’s too much work to read the list, find the ID, and add that.

this_is_fine giphy

Step 2 is important because Gumroad pings for all purchases. Wouldn’t wanna give access to someone who bought a different product. 🙃

As you can see this code is very scalable and generic. But it doesn’t have to be. That’s the beauty of cloud functions – they do one thing and one thing only.

upsertUser

upsertUser is where users get created or updated.

Using upserts lets you support free trials and people who repurchase after a refund or whatever. Folks who have an account already.

Click through for source

Get the Auth0 client, find user by email. If user exists, return, otherwise create a new user.

For better security we generate a random secure password. Users have to go through the password reset flow on their first login.

Makes the UX more cumbersome and the whole system more secure. You don’t want to store these passwords and you don’t want to send them by email. You also don’t want them to be guessable.

Infosec matters.

getAuth0Client

getAuth0Client is a helper method to instantiate an Auth0 client. Makes your code more readable 🙂

Click through for source

Call getAuth0Tokens to get secrets, instantiate a new ManagementClient for Auth0. This one lets you manage users and stuff.

auth0Tokens

auth0Tokens is a helper method to read API tokens and secrets from AWS Secrets Manager. This is important to avoid hardcoding secrets in your code, sharing them with other developers, and making sure they’re secure at rest.

You get a sort of triple-blind secret.

Public can’t see it. Developers can’t see it. Servers can’t see it. Secret only exists in memory at runtime. 🔐

Attacking that would require an OS-level hack.

Click through for source

Reads secret from AWS Secrets Manager, stores it in local variable for next time so you don’t have to ping ASM every time you need a secret while your code runs.

I should open source this 🤔

An important sidenote

You’ll need 2 Auth0 apps. This one took me for a spin. 😅

1 app for your JAMStack frontend
1 app for your Lambda backend

Authenticate and Authorize users

You’re creating users for every purchase. Now what?

Use useAuth to handle authentication and authorization in your app.

Assuming standard configuration from useAuth docs, you can paywall any component in your JAMStack app like this:

Click through for source

useAuth handles the Auth0 authentication lifecycle and returns an isAuthenticated method and a user object.

Call isAuthenticated to see if user is logged in with a valid session, use isAuthorized to verify they’ve got the right role. Then let them through.

Otherwise show them the buy button.

Tell Auth0 to include role metadata

You’ll have to add an Auth0 Rule that includes user roles in response data to your Auth0 config.

Click through for source

This was the smallest code that works for me. Cobbled together from online discussions, a few blogs, and some trial and error.

I didn’t dive too much into details and I don’t know why Auth0 doesn’t include meta_data by default.

shrug giphy

Bonus UX points

For bonus UX points, you should give people temporary access when they come back from that Gumroad purchase.

Gumroad gives you specific URL params that mean “This user just bought your course”. Would be great if they didn’t have to then login, change their password, and go through a bazillion steps just to get access.

It’s the approach I use on ServerlessHandbook.dev where there’s no logins. Just local storage after a Gumroad redirect.

Just add a check …

Click through for source

Make justBackFromGumroad check the URL query 🙂

✌️

And that’s how you can paywall parts of your JAMStack app.

To learn more about these techniques, check out ServerlessHandbook.Dev and my new course ServerlessReact.Dev

Cheers,
~Swizec

Learned something new? Want to improve your skills?

Join over 10,000 engineers just like you already improving their skills!

Here's how it works 👇

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet 📖right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

PS: You should also follow me on twitter 👉 here.
It's where I go to shoot the shit about programming.