Sanctum API Tokens Overview0:00
Let's take a look at how we can provide an API for other apps to access our data. We can make use of Sanctum for this, which already comes with new Laravel installations. So if you take a look at the Sanctum docs, you'll see that we can use it in three different ways depending on our needs. So in this video, I want to focus on API tokens, but you can also use it for SPA authentication. So if you're building a single page app with a separate front end and back end, you can make use of Sanctum to communicate between the two projects and authenticate securely. So we already have a few videos on that. This video makes use of Breeze and the official Next.js scaffolding. And in this video, we make use of Nuxt as our front end and use Sanctum to authenticate.
This video makes use of Breeze and the official Next.js scaffolding. And in this video, we make use of Nuxt as our front end and use Sanctum to authenticate. You can also do something similar with mobile app authentication. So when we log in, we obtain a bearer token, which we can use on other requests to access our data. And we do have an example of this in our React Native series. But like I said, in this video, we want to make use of API token authentication. So the idea here is you're already logged in in a main application. And from within there, you create a new token, which you can make use of to perform actions on your application.
GitHub Token Example2:03
So we can create repos and also delete repos should be one down here for deleting repos. Okay. And let's create that token. Make sure to copy this. And now we can make use of the GitHub API to create and delete repos using this token. And then we can make use of the API in any other application, for example, this CLI here. So I'm going to go into my REST client and make use of the GitHub API. And you can see I already have the endpoints for creating and deleting repos here. So the endpoint for creating is /user/repos. It's a POST request, we have to pass in a JSON body for the repo we want to create.
You can see test repo here, if I go into my actual GitHub. So let's just go to my profile here, or my repositories, you'll see it's right there. And we can do the same for deleting as well. The endpoint is this. So we have the username and the repo name in the URL. It's a DELETE request. And we want to make sure we pass in the correct token. So let me just paste in my token here. Let's hit that endpoint. So it returns a 204, no content, but it should have deleted that repo.
Let's hit that endpoint. So it returns a 204, no content, but it should have deleted that repo. So if I refresh, it's gone. So let's see if we can create something similar. So within our main application, we want to be able to generate tokens. And we also want a few API endpoints to perform actions on our data by creating or deleting repos. And also note that this functionality is already available if you're using Jetstream. So I have a Jetstream application here, or not here, here. If you go to your settings, you have to enable it first.
Starter App Setup3:54
So I have a Jetstream application here, or not here, here. If you go to your settings, you have to enable it first. I have it enabled here, it's called API tokens. And we have the same functionality that you saw within GitHub that allows us to create tokens and manage the permissions. So let's see if we can recreate something similar. So I already have a starter application here, similar to GitHub. So we have a Repo model, we have a relationship between Users and Repos. So a User has many Repos, and a Repo belongs to a User. I'm making use of Breeze for authentication here.
So a User has many Repos, and a Repo belongs to a User. I'm making use of Breeze for authentication here. And my Repos shows the logged in User's Repos. Right now we can see mine here. And we can also create one. So let's say new Repo from main app test. Let's create that Repo. There it is. And we can also delete Repos from within here. So if we were to go into the actual Repo, we can see details about the Repo here.
Build Token Creation UI4:40
And we can also delete repos from within here. So if we were to go into the actual repo, we can see details about the repo here. And we should be able to delete the repo here. Let's start off with a new form that allows us to create tokens from within this main application. And that will provide a few API endpoints that allow us to create and delete repos. So let's make a new menu item here for personal access tokens or API tokens. So that's within my app layout. Let's create one for API tokens. Actually, I'll name it personal access tokens.
Let's create one for API tokens. Actually, I'll name it personalAccessTokens. That's what I have in my reference code here. So the endpoint is going to be personalAccessTokens. And same for the name here. Okay, let's go into our routes file and create an endpoint for that. So I'll just duplicate this one here. It's going to be for personalAccessTokens. And we'll make a new view called personalAccessTokens. And for now, we don't need to pass it any data.
And we'll make a new view called personal_access_tokens. And for now, we don't need to pass it any data. So let's save this. And I'm just going to duplicate one of my views here. The home page is pretty bare bones. So let's duplicate this. Let's name it personal_access_tokens. And let's just change the title here. Okay, let's see if this works. So back here, there's our menu item.
Okay, let's see if this works. So back here, there's our menu item. And I didn't name it blade.php. So let's rename this. And now it should render correctly. Okay, let's make a new form here. So we can generate new tokens. We'll also list out our tokens on the same page later on. But for now, let's create a form. So let's make an H3 here.
But for now, let's create a form. So let's make an h3 here. Let's just make it a bit bigger and make it bold and say create token. And this will be a form. Let's post to the same endpoint, but it'll be a POST request now. So personal access tokens. And the method is POST. Let's make sure to add the CSRF field. And we only need one field here for now. It's just the tokenName.
And we only need one field here for now. It's just the token name. So I'm going to paste in that field. Let me just save that. So it's just a token name. Input type equals text. We have some classes, so it looks decent. And we also have the error message if there is one. And of course, we also need a submit button, which I will paste in as well. So button type equals submit, and this will submit the form to our endpoint.
And of course, we also need a submit button, which I will paste in as well. So button type equals submit, and this will submit the form to our endpoint. So let's just see if that renders correctly. Okay, that's fine for now. Now let's create that endpoint and generate a new token. So back to our routes file. Actually, I'm going to grab all of this and put it down here. Okay, let's make another one for our POST request. So it's POST, same endpoint. We need a request here.
So it's Post, same endpoint. We need a Request here. So let's accept that. Let's do some validation to make sure the token_name is passed through. So $request->validate. The field name is token_name, and required is fine. And now to generate a token, if we go back to the documentation for Sanctum, we should be able to use the createToken method. And we also want to show the User the actual value of the token so they can copy it and make use of it in their applications.
And we also want to show the User the actual value of the token so they can copy it and make use of it in their applications. So we only want to show it once. So what we can do is put it in a flash message. So let's grab all of this. Let's go back to our code. Let's paste that in after our validation. So we're creating the token here for the logged in User using request()->user(). And instead of returning the token, let's just return or let's just redirect back and show it in the flash message.
And instead of returning the token, let's just return or let's just redirect back and show it in the flash message. So let's grab this. Let's remove this. Let's return back and let's use a flash message here. So with, I'll name it successMessage. And we can say token was created. Make sure to copy this. And we can output the token plain text value or plain text token, okay. And we have to make sure to show this flash message within our view.
And we can output the token plain text value or plain text token, okay. And we have to make sure to show this flash message within our view. So back here, I'll just put it here underneath the title. And I do have a snippet here. So success message. So if it exists in the session, then go ahead and output it. And I'm just going to change the classes here. Okay. So now, if I did this correctly, we should be able to generate a token and it should show within the flash message.
So now, if I did this correctly, we should be able to generate a token and it should show within the flash message. So let me just refresh this. Let's say newToken, let's create that token. And it does work. And it is showing here. Let's also list all of the tokens for the currently logged in User. So right underneath here, I'm going to make an unordered list. Let's say listDisk, give it some space, y2, and give it a margin top. And I'll use forEach here.
Let's say list disk, give it some space, Ytwo, and give it a marginTop. And I'll use forElse here. And we can list out the logged in users tokens. So to grab the user's tokens, we can say auth()->user(). For the logged in user, we can say tokens as token. And within here, we can just put it within a listItem. And of course, you can put other details here too, like the tokenExpiry. But for us, we'll just put the tokenName. And for the empty case, sorry, forgot to close that out. For the empty case, we'll just say you have no personal access tokens.
Okay. Let me just make another token here because I didn't copy the other one. So another token, create token, let's copy this one. And now we can work on our API endpoints. So whenever we make use of this token, it should be on behalf of the currently logged in User. So right now I'm logged in as a User named Andre. If I were to use that token to create a repo using the API endpoint we're about to create, then it should show here. So let's go ahead and do that.
Create Sanctum-Protected API11:11
then it should show here. So let's go ahead and do that. So let's go into our routes/api.php file. And here's where you can define all of your API routes. So if the API endpoint is public, then we do not have to attach the auth:sanctum middleware. So for example, if we wanted a list of repos, then we can just go ahead and create that route. So let's say repos for the endpoint. And this will be a public repo, which just lists out all of the repos within the application. So let's just return that here.
And this will be a public repo, which just lists out all of the repos within the application. So let's just return that here. So let's say return, let me just move this up. And how about we also eager load the User associated with this Repo. So we can say a Repo with the userId and name, and let's grab the latest Repos and get that. So again, this is public, so we don't need to attach the sanctum middleware and anyone can access this route. Okay, let's try that within our REST client. Let's create a new endpoint.
Okay, let's try that within our REST client. Let's create a new endpoint. Let's say, get all repos, it's a GET request. The endpoint is, what is it, lc cookbook API tokens, lc cookbook API tokens.test. And it's within the API routes file, so /api, and I named it repos. Again, this is public, so this should work. And there are all of our repos. So again, this is everyone's repos. You can see we have this user here and another user here. And I think I see that maybe 100 repos, not sure.
You can see we have this User here and another User here. And I think I see that maybe 100 repos, not sure. Let's double check the database. Let's go to the repos table. And I have 99 because I deleted one earlier. Also note we have this table for personal access tokens, and we did create two. And you can see they're both associated with userID one. Now let's make some endpoints for creating and deleting repos, just like we did in GitHub. So let's duplicate this one. It's now going to be a POST request for the endpoint.
So let's duplicate this one. It's now going to be a POST request for the endpoint. How about we copy the endpoint for GitHub, and I believe that was /user/repos to create one. And it was. So let's do the same /user/repos. We need to accept the request here. Get rid of this here. We do need some validation, and I'm just going to take that from our routes file, where we create it in here as well.
We do need some validation, and I'm just going to take that from our routes file, where we create it in here as well. So where's the create endpoint here? It's right here. So I'm actually going to grab all of this. If we used a form request, we could avoid the duplicate code for our validation, but this is fine for this demo. Space that in. Let's save that. And the indentation didn't work, so let me just fix that.
Let's save that. And the indentation didn't work, so let me just fix that. And how about we return the created repo here? Return that. And now we have to make sure to use the auth:sanctum middleware. So let's go ahead and do that. I'm going to grab it here. And I'll just put it at the end here. Okay, so now when we hit this endpoint, we have to pass in a Bearer token, and that token should have information about the logged in user, which will allow us to use methods like
Okay, so now when we hit this endpoint, we have to pass in a bearer token, and that token should have information about the logged in user, which will allow us to use methods like auth()->id() to grab the ID of the logged in user. So let's try that out. So the endpoint is /user/repos. So back to Insomnia. Let's say createRepo. So let me duplicate this. createRepo. The endpoint is /user/repos.
Create repo. The endpoint is /user/repos. And it's a POST request. Let's try hitting the endpoint. And it says not found, we might have to add Accept: application/json in our headers. So Accept: application/json. Okay, try that again. Oh, it's because I forgot the API. Okay, let's try that one more time. Okay, so we do get an authenticated.
Okay, let's try that one more time. Okay, so we do get an authenticated. So now if you pass in that token that we generated earlier, so we can say auth Bearer token. So right here, let's pass in that token, they can have a clipboard somewhere. There it is. And let's try running that now. Okay, so now we're getting a different error. And that's just because we didn't pass in the name and the description of the repo. So we can do that within the body, we can just make use of normal fields here. So we'll say multipart/form-data, name is repo from API, and description is testing from.
So we can do that within the body, we can just make use of normal fields here. So we'll say multiPartForm, name is repo from API, and description is testing from API. Okay, let's run that. And it does work. So it turns the newly created repo, we passed in our bearToken, and that token is associated with User1. And if you go back to our application, let's refresh this page. And there it is. Cool.
And there it is. Cool. Let's provide an endpoint for deleting repos as well. So I'll just paste that in since it's pretty much the same as creating a repo. So I'll just paste that in here. This is the endpoint, it's taking in a repo in the URL, and instead of the ID, it's making use of the name here. We're accepting that repo here, we have some validation to make sure that only the User who created the repo can delete it. And then we actually delete it.
who created the repo can delete it. And then we actually delete it. And then we return a 204, which is similar to what GitHub does. And for the middleware, let me just delete the second one, which has to do with permissions, which we'll take a look at in a second. So let's save this. And let's try that out in Insomnia again. So let's duplicate this. It's for deleting repos. It's a DELETE request.
It's for deleting repos. It's a delete request. The endpoint is repos/repo-name, the repo name is repo from API, we don't need any fields here. Our bearer token should be within here already. And that should work, hopefully. So let's try that out. Okay, we do get 204. And if I were to refresh my app, should be gone. And it is cool.
Add Token Abilities Scopes17:16
And if I were to refresh my app, should be gone. And it is cool. Now if you want, you can also provide abilities to your tokens, it's also known as scopes. So for example, within GitHub, we had all of these abilities when we created a new token. So you can easily do that within sanctum by providing an abilities array as the second param to createToken. And if you don't provide one, the default ability is a *, which gives you the ability to do anything. So if you take a look at the database here, you'll see the abilities column here. And it's just a string array with * as default.
So if you take a look at the database here, you'll see the abilities column here. And it's just a string array with star as default. So let's see if we can create a few abilities for our tokens. So in our case, let's create a create and delete ability. So we have to update our form here for creating a token. And let's add checkboxes for creating and deleting. So back to our personal access tokens blade, I'll put it underneath the form here, or not underneath the form underneath our text input. So I'm just gonna paste that in. It's just two checkboxes, one for creating and one for deleting.
So I'm just gonna paste that in. It's just two checkboxes, one for creating and one for deleting. So let's see how that looks. Okay. And now if these are checked, they will pass through to the request. And we can add that as an array to our createToken method. So back to our routes file, our endpoint for creating tokens is down here. So we want to pass in an abilities array as the second param. So let's do that abilities. And to start it's empty.
So let's do that abilities. And to start it's empty. So empty array, and we can push to it if it exists in the request. So that just means if it's checked. So if request, start with create, and go ahead and push to the array, we'll use array_push here. abilities, let's just say create for the actual ability name. And we can do the same thing for delete. Let's just update this. Okay.
Let's just update this. Okay. And let's go ahead and try that out. So let's save this. Let's create a new token. Actually, let me just delete the other ones. So we're not confused. So let's just do that within the database. And of course, if this were a real app, you'd want to provide the User with the ability to delete their tokens from within here.
And of course, if this were a real app, you'd want to provide the User with the ability to delete their tokens from within here. Again, the default is * for everything. But now we have create and delete abilities. Let me just delete these ones. Okay. Let's create a new one. Actually, let's refresh this first. Okay. It's a new token.
Okay. It's a new token. Let's give it both abilities. create and delete. Create that token. Okay. Let's copy this. And let's check that out in our database. You can see we now have create and delete as abilities. So now to enforce these abilities, we can go back to the documentation, we can make
You can see we now have create and delete as abilities. So now to enforce these abilities, we can go back to the documentation, we can make use of the tokenCan method if you want to do it programmatically, and then provide the ability name. Or you can make use of middleware here. So there are two, one called abilities and one called ability. And the difference is abilities checks for all abilities. So in this case, checkStatus and placeOrders would have to be in the array for this code to execute. And ability is the same thing, but only one of those abilities has to be present.
to execute. And ability is the same thing, but only one of those abilities has to be present. So this is and and this is or. But if you only have one ability, then it shouldn't matter too much. So let's try this out. We have to add this to our app/Http/Kernel.php. So app/Http/Kernel.php, okay. And this goes within the routeMiddleware. So right here, let me just paste that in. Okay.
So right here, let me just paste that in. Okay. And we can make use of it within our middleware on our routes. So we can say ability or abilities, and then the ability name. So let's grab this, go back to our routes/api.php file, and we can add them here. So let's try for creating first. So currently, we do have access to create. So this should work. So the name of our ability is just create. So let me just change this.
So the name of our ability is just create. So let me just change this. So let's try this out again, back to our rest client. So let's go back to create repo. This token is now deleted. So let's try that out. It should be unauthenticated. Okay. Let's paste in our new token here. And this should work.
Let's paste in our new token here. And this should work. And it does. Cool. But if I were to remove that ability, we should no longer be able to create a repo. So let me just change the name here. Because we need to have unique names, so I'll say change. And let's go ahead and remove the create ability manually within the database. So let's just update this. Let's remove create.
So let's just update this. Let's remove create. Let's save this. Okay. And now if we try this again, we should get some sort of error. And we do invalid ability provided. Cool. And we can do the same for delete. So let's just add the delete ability on deleting our repos right here. Okay.
So let's just add the delete ability on deleting our repos right here. Okay. delete. Let's actually remove that first. Actually, let me add the correct bear token first. So this, delete. Let's remove that ability first to make sure we get that error message. So let's remove delete here. Okay, let's try that out. Let's try deleting this repo.
Okay, let's try that out. Let's try deleting this repo. And we do get invalid ability provided. But if we have the correct ability, let's put that back. So delete, then it should work. Try that one more time. And it is deleted. Cool. So yeah, if you wanted to provide personal access tokens for other applications to have access to your data, then definitely make use of this feature within Sanctum.
So yeah, if you wanted to provide personal access tokens for other applications to have access to your data, then definitely make use of this feature within Sanctum. So as always, let's make a commit here. This is a separate repo from what we've been using so far, but we can still make a commit message here. git add, git commit. This is episode nine. Let's name it API tokens with Sanctum.
