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:
- User visits your JAMStack app (Gatsby for me)
- User clicks Buy Button
- Gumroad checkout pops up
- User purchases
- Gumroad calls a webhook
- AWS Lambda wakes up, creates account on Auth0
- User is redirected back to JAMStack
- 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:
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.
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.
The webhook is where things get interesting.
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
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 is where that work happens.
A couple things happen here:
- Parse request body with
- Check product against a constant whitelist
- Create or update a user on Auth0 with
- Add authorization role to user
- 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.
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 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.
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.
getAuth0Client is a helper method to instantiate an Auth0 client. Makes your code more readable :)
getAuth0Tokens to get secrets, instantiate a new
ManagementClient for Auth0. This one lets you manage users and stuff.
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.
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 🤔
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
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:
useAuth handles the Auth0 authentication lifecycle and returns an
isAuthenticated method and a
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.
You'll have to add an Auth0 Rule that includes user roles in response data to your Auth0 config.
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.
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 ...
justBackFromGumroad check the URL query :)
And that's how you can paywall parts of your JAMStack app.
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below 👇
Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️