Swizec Teller - a geek with a hatswizec.com

Senior Mindset Book

Get promoted, earn a bigger salary, work for top companies

Senior Engineer Mindset cover
Learn more

    Connecting Gumroad to Auth0 for paywalled JAMStack apps

    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
    R1Nx5tx

    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
    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
    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
    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
    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
    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
    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
    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

    HfAc1hu

    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
    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
    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
    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

    Published on February 24th, 2020 in Back End, Front End, Technical

    Did you enjoy this article?

    Continue reading about Connecting Gumroad to Auth0 for paywalled JAMStack apps

    Semantically similar articles hand-picked by GPT-4

    Senior Mindset Book

    Get promoted, earn a bigger salary, work for top companies

    Learn more

    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

    Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections

    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

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created by Swizec with ❤️