 Hey folks, I've been playing with Auth0 actions recently and I'm loving how it allows me to extend my Auth0 tenant and run my own customer code. So as a proof of concept, I wanted to create a web store that uses nothing but Auth0 and Stripe. No database as a service, just really simple continuous deployment and it turns out that it's absolutely possible. Let's jump right into it and find out how. Basically if you want to create a simple web store, you would create a custom web application using something like React or Python or whatever your stack of choices and create a database to store the products and customers and orders and so on. You'd want to use a payments provider and an identity provider so you're not reinventing the wheel and keeping yourself safe. But I wondered, can I do this without a custom database? Some payment providers allow you to configure your catalog of products in their system and will store orders rather than just the payment details and of course identity providers are by their very nature a database of users so why couldn't I simply piggyback of them? So I created this simple React app that uses Auth0 for authentication. When a customer logs into Auth0, an Auth0 action runs my custom code on the Auth0 server to make sure that the user is a customer in my Stripe tenant. And when the user is redirected back to my React app, Auth0 sends through an access token that includes the user's Stripe customer ID so that the React app can tell Stripe who's making the purchase. Now the beauty of the access token is that it's a JSON web token, which means that as it's signed by Auth0, we can trust that the data inside the JSON web token hasn't been modified or even fabricated by somebody else. And behind the scenes I use two Lambda functions for talking to Stripe, one to get a list of all the products and the other to start a checkout process, both of which require me to send through the Stripe secret key. Now if I was doing this with the stack that has server-side execution options like PHP or Next.js, this might be a little less hassle, but using Netlify's command line tool, I'm able to run Netlify functions locally during development so easily that this is now my preferred way of creating web apps nowadays. If you prefer other stacks, the key here is to make sure that you're talking to Stripe from server-side code and not storing any of the secrets in code that get sent to the browser. OK, enough of the design concepts, let's take a look at some of the code and then we'll create the actions that make the magic happen. So here's my web store. It's a simple application that just lists a couple of products and the products, as I mentioned, are stored in Stripe. So I've configured these in Stripe and I've got a Netlify function that pulls those out. Now when I want to purchase, I need to log in. So that's something I've configured my React application to require for a user to be logged in first. And if I click on log in to purchase, it'll take me to the login page. When I've logged in, then the button changes to a buy now button. So let's have a look at some of the code to see how that all ties together before we write all zero actions. So we'll jump over to Visual Studio code here. We'll see on the home page down at the bottom, this is the main template output here and we've got a point here where we display the buy now button. We look at the code for the buy now button. We're pulling out of the use Auth0 hook of various things. Is loading? Is Auth0 still trying to work out whether or not the user is logged in or not? Once we know that, are they authenticated? If they're not, we have a link here to a function that allows us to redirect the user to log in. And then we've also got the get access token silently method that allows us to get the access token that Auth0 has created for the user that's just logged in. And down at the bottom, we can see here that we don't output anything while we're still loading. If we know that the user is authenticated, then we show a button that says buy now. Otherwise, we show a button that says log in to purchase, which uses that log in with redirect method that we got from the use Auth0 hook. But the thing that we want to look at at the moment is what happens when you click on the buy now button. So that calls this buy function here and in the top here, we've been passed the product with the buy now button. And if somebody clicks on the buy button, this function is called. We get the access token, and then we make a call to our Netlify function, which we'll jump over to in a second. And into that, we pass a bearer token from the access token, so that our Netlify function knows the information about the user, specifically in this case, the Stripe customer ID. And we pass in the item that wants to be purchased. The way the Stripe works is that once this completes successfully, we'll be given a URL to redirect the user to. So that's what we do on the next line here. But again, let's jump in one step at a time. So we've got the button. We know what happens when you click on the buy now button. So let's have a look at the buy function to see what's going on behind the scenes on the server side when we're talking to Stripe. So at the top here, we're using this verify JWT function that makes sure that the JSON web token that we're receiving is valid. So we make sure that it's issued by the all zero issue that we're expecting and that it's intended for the audience that's reading it, in this case, our Netlify function. So assuming the JSON web token is verified, we can continue knowing that the Stripe customer ID inside that JSON web token has not been modified since all zero said it to us. Now, when we add a custom claim to our JSON web token, where a claim is basically an assertion of a truth, in this case, we want to add a claim in that the Stripe customer ID is equal to a certain value. We will prefix the name of that claim with the domain of the web application that we're writing. It's a good way of making sure that the name is unique and is not going to be used by any other systems. Once we have that Stripe customer ID, we're going to be able to make a call to Stripe checkout session, which is going to create a new session for checking out through Stripe to make a purchase. And the key information that we're passing through here is the customer ID. So this is required by Stripe in order to be able to create the checkout process. This is why it's key for us to have a Stripe customer ID for each user so that we can associate those IDs together, the user ID within all zero and the Stripe customer ID within Stripe. We're also passing through the items that are being purchased and a few other things like what kind of payment methods are accepted. But once this is all done, we get a response back and we return the session back to our web application. This is the point that we come back in over here. Here's the response. And then we're going to do a redirect to the URL for the checkout process. So the things we need to do are to make sure that the Auth0 user, each Auth0 user has a Stripe customer ID and also to provide that Stripe customer ID to our web application via the access token. So let's jump over into the Auth0 dashboard here and work out how we're going to do that. So we jump under user management. We can see here that I already have an account. This I created earlier when I was looking into the system. And there are two areas that we can store bits of information for each user. You might have seen these before. We have the user metadata and the app metadata. User metadata is essentially any data associated with the user that the user is able to modify. And app metadata is any information about the user that only the application with the use of a secret key can modify. So we're going to want to store this into the app metadata. We don't want users being able to modify their own Stripe customer ID. So into the app metadata, we will store the Stripe customer ID once we've made sure that the user has a Stripe customer. So we'll jump down to actions here and into library because we're going to create a couple of actions. First to be able to add into our flows and we'll see how that all works. So we're going to build a custom action over here. And we're going to call this link user to Stripe. What we want to do is link our all-serial user to the Stripe customer ID. And we have a number of different triggers that we can look out for. It might make sense to use the post user registration after a user is registered. Then we want to make sure that they have a Stripe customer ID. And that would be a very good choice. However, there's going to be one problem in the scenario where I log in with the account that already exists that I've created for myself. Then this is not going to run because I already have an account. So I'm going to choose to do it on post login. So every time a user logs in, we're going to make sure they have a Stripe customer ID. If you're starting with a blank database without any users in there, then the post registration would still work for you. But because I want existing accounts to be verified that they already have a Stripe customer ID, I'm going to go with the post login. So I'll create this action. And here we now have a simple in-page editor that allows us to add some code. So the first thing I want to do is just draw your attention to the options we have over on the left-hand side here. We've got a little test suite that allows you to modify the JSON that gets sent into the test, run your code against the data that's in there and see what the output is. This is a great improvement over rules and hooks in that you're actually able to test this before the code goes into a deployed state. The two that we're going to be looking actually using today, though, are secrets and modules. Because we're going to want to talk to Stripe, I want to install the Stripe NPM module. So I'm going to add this in here. And now we can see that whenever we run our code, we're going to have access to the latest version of the Stripe module. And I also want to add a secret in rather than putting the Stripe secret key into the code itself, I'm going to add a secret to store that more securely that we can then access from our code. This is very similar to the way that environment variables work, except it's a way of doing that within all zero actions. Okay, we've got our Stripe secret key over on the left-hand side here now. So the first thing we want to do, as I mentioned, is to pull in this. Let's make this small so we can see the code. Pull in the Stripe module. And we're going to pass into it the Stripe secret key. One of the other things I like about actions over rules and hooks is that the new editor we're using has a lot of code completion built into it. So we can dig in and find out exactly what options there are for us as we're coding through rather than having to refer to the documentation all the time, which I think is a great touch. So we're going to require the Stripe module. And we've now got our Stripe object that we're able to use. So let's go ahead and create a new Stripe customer ID. So we're going to use the Stripe object here to create a customer. We're going to pass through the user's email, the user who's just logged in to Auth0. Little description here just so you can see at the other end extra information about the user. And we're also going to pass in some metadata. So in the same way as Auth0 can store app metadata and user metadata, Stripe allows you to store metadata against each customer. So I want to grab the user ID from the Auth0 user. And I want to store that in my Stripe customer so there's a one way relationship there. And then we can use the result of this to take the customer ID, which has come out of the Stripe customer. So we're going to take the customer ID and we're going to create a key value pair in the app metadata for the user called Stripe customer ID and store that in there. And there's one thing we need to do before we save and run this. We want to make sure that this only runs once per user. At the end of this, the user is going to have a Stripe customer ID. So let's just put a check in here at the top to say if the customer already or if the user already has a Stripe customer ID, we're not going to do anything. So just to recap, when the user's logged in, we're going to load the Stripe module. We're going to check to see if they already have a Stripe customer ID. And if they do, we're just going to return. If they don't have a Stripe customer ID, we'll create one for them. We will also allow Stripe to know what the user's ID within Auth0 is. And then we're going to take the Stripe customer ID and we're going to store that against the user in their app metadata. So I'm going to come across here and click Straight on Deploy. You could save as draft and do some testing first, but we're just going to deploy it straight away. And once that's done, you can see that the action is up to date within the system. It's not actually been put into any of our authentication flows, though. So we have a library of actions and we're able to define when and how these get executed. So we jump to the flows tab here. We can see that we've got the six main triggers that we saw before. Because we created a post-login action, we're going to jump into a login flow. And if we look on the right-hand side under the Custom tab here, we'll see the link to Stripe action that I created. So I'm going to drag that over and then hit Apply. It's at this point now that any subsequent login is going to cause the user to have a Stripe customer ID created. So let's just double check that this is working. We'll jump into my user and we'll make sure that I don't have a Stripe customer ID. I shouldn't yet. And my app metadata is still blank. So if I jump over to the Web Store, I'm going to log out and then log back in again. OK, we've come back to the Web Store. If I jump back into Auth0 and hit Refresh on here, we should now see that app metadata has some information in it. And we have our Stripe customer ID. And we should be able to find a Stripe customer with this ID. If we jump over to Customers here, we can see that this was created just a minute ago. And the descriptions in there, my email address is in there. If we open it up, we can see that the ID is the same. But also, we can come down under metadata and see that my Auth0 user ID is stored against my Stripe user. So that first part is working. But we don't have an access token yet or we don't have the claim inside the access token for our buy function to work. So let's create a second action in order to add that into the access token after the user logs in. So we'll jump down to Actions and Libraries again. I'm going to create another custom function. We'll call this one Add Stripe Customer ID to Access Token. This one's going to be very simple. We're going to do a very similar check to what we did at the start of the other action. We're going to make sure that they have a Stripe customer ID. And if they do, we're going to use the API parameter over here, which allows us to affect changes or consequences to running the action. What we want to do is access the access token and set a custom claim on there. As I mentioned, we prefix it with the namespace of the URL of the web application we're using. This string here will match what we're looking for in the buyJS function that we saw before. And the value that we're going to pass in is that Stripe customer ID from the app match data. So if I deploy this now, once that's done, we can jump over into Flows and add this into the flow as well. We'll see that under Custom here, we now have our Add Stripe Customer ID to Access Token. We want to make sure that we drop this one after the first one. This is obviously the one that's going to create or ensure that the user has a Stripe customer ID. And then we're going to add that Stripe customer ID to the Access Token. So if we apply this again and we log out and log back in, now a new Access Token has been sent during that login process to our React application, which means this time when I click on the Buy Now button, we're actually going to be able to see that Access Token get passed through to our function. So I'm just going to jump into the Network tab over here and filter by XHR requests just so we can see what's going on. So when I click on the Buy Now button, we can see that the buy function is called. And in here, we've got this authorization bearer token. We can actually go in and have a look at what's going on inside here using jdwt.io. So a great little tool for looking at what's going on inside a JSON web token. And if I paste this, just get rid of the cruft to the beginning, we can see down here that the various claims that came through in the Access Token include this Stripe customer ID. So because JSON web tokens are signed using the signature down at the bottom here, our application has been able to verify that this has not been modified. We can trust that information and send that through to Stripe. Meanwhile, on the left-hand side here, let's just close this up, you can see that because I clicked on the headphones to purchase, it's now asking me to complete information to make payment. Once I make payment, I'm sent to the Success page. And if we jump over into our payments over here, we'll be able to see that a payment has just come through for the headphones. If I decided that I wanted to buy the running shoes instead, then Stripe would be given that information and that's the item that I'd be buying. That's Auth0 actions in a nutshell. If you found this useful and you would like to see a longer video that goes into the entire creation of this web store, let me know in the comments down below. That's something I can put together if that's of interest to you. Also, check out some of the other videos. There are a couple of other actions videos on the Auth0 channel at the moment and they might give you ideas of other things you can do with actions as well. I hope you found this information useful. If you have, then please hit that like button. It's going to help other people find this content too. And if you want to see other videos that we're coming out with, subscribe, hit the notification bell and until next time, happy coding.