When you are building a web application with Laravel, authenticating your user is a crucial step because we don’t want an unauthenticated user to do something malicious in our app or do something we don’t expect to. You should always protect your routes with the authentication middleware, or if you want to take a step further, you can protect it with the authorization as well.
It’s easy to authenticate users with the traditional login forms because we have session states between requests that we can maintain. Take a look at my article here if you want to know how we can authenticate users with traditional login forms using laravel/ui package. But, how about authenticating users using the API? As we know, in the API environment, we don’t maintain the session state between requests.
Fortunately, Laravel Passport package comes for help. We can easily authenticate users with the Passport. Of course, we need some preparations to be done before we can use this package in a real-life project. In this article, I’ll show you how to authenticate users using Laravel Passport in our API.
If you want to take a step further for authorizing users, take a look at my article here.
- What is Passport?
- How we can authenticate the users?
- Passport Installation
- API Routes
- API Traits
- Authentication Controller
- Let’s Try it!
- Things to Improve
What is Passport?
Before we begin, I want to make sure we have the same definition of what Passport is.
Laravel Passport is an OAuth2 server and API authentication package that is simple and enjoyable to use. — https://github.com/laravel/passport
With that definition, we know Passport is a package ready for OAuth2 and API Authentication implementation. What’s great about Passport? With Passport, we have a lot of boilerplate codes for authenticating users. The package includes the authentication Middleware, functions for generating the token, the migration table, and so on. What we need to do is just a basic configuration so that our project can implement this package. Interesting right?
How we can authenticate the users?
You can skip this explanation if you’re already know how to authenticate the users with the API. If you don’t know yet, let me explain first so you don’t get confused in the middle of implementation.
Let’s say a user, John, want to log in to our app. The user requests an access token by submitting his credentials into the server. The server will then generate a token if the credentials are matched and return the token to the users. In a real-life project, we must specify the lifetime/expiration time of the token. We don’t want a user to have an unlimited expiration time. At least, we control how users interact with our server by specifying the token’s lifetime.
If John has the access token, then he must always pass the token into the server whenever he asks a resource. Take a look at the image below.
When John asks a resource, he must pass the token that he retrieved before. The server will check if the token the user passed is a valid one and not expired. If the token is valid, the server can get a user represented by this token, and the server found John in this case. So, the server can return the books owned by John, not the books owned by other users.
Of course, you can modify the behavior as you want. Maybe, John can get access to all books, or he can only update a book owned by him, and so on. The most important thing is that every user must pass their token every request they made. So, the server can identify the users by the tokens they passed and properly return a response.
The question is, what does the Passport do in this scenario? Actually, Passport does a lot of things. For example, we don’t need to find a user represented by his/her token explicitly, because the job is already done in the middleware by Passport and we can retrieve the user with Auth::user() function in our controllers. Cool right? It saves so much time for us comparing we made the whole system by ourselves from scratch.
Let’s install Passport inside our project. I assume you have a fresh installation of Laravel project, then in your project, let’s pull the Passport package.
composer require laravel/passport
If the Passport is installed successfully, the next step is to run the migration. The Passport migrations will create some new tables for storing the tokens.
php artisan migrate
Next, we need to create encryption keys. These keys are needed for generating the access token. Install the keys with this command:
php artisan passport:install
After running the command, we must add the Laravel\Passport\HasApiTokens trait into our User model. This trait gives us some helper methods to create the token, inspect the user’s token and scopes.
Next, we must add Passport::routes() in our app/Providers/AuthServiceProvider.php to register necessary routes to issue/revoke the tokens. One more thing, inside the boot() function, we can specify the default lifetime or expiration time of personal access token we want to issue to the user.
Almost done! We have to set the Passport to be the API authentication guard. So that, Passport’s TokenGuard will be used by our application to authenticate the incoming API requests. Let’s modify the config/auth.php on line 44.
That’s it! Next, we need to configure the routes and authentication controller.
Up to this step, we have the authentication Passport middleware to protect our routes. So, we’ll protect the /user and /logout endpoints so that only authenticated users can access these routes. The remaining endpoints, /login and /signup, will be publicly accessible.
Let’s open the route/api.php and modify it.
When creating the API, make sure we have consistent data returns for the API consumer. So, it will make it easy for consumers to process the responses. With this in mind, I’ll make an API trait to ensure that all the responses are always in the same JSON format. Let’s create one in the app\Traits\ApiResponser.php.
You can take a look at my article here about this API trait.
All right, let’s create the authentication controller now.
php artisan make:controller Auth\AuthController
Then open the AuthController.php and modify it:
First, let’s take a look at the signup process. In the signup() function, of course, we must validate the data then create a new user based on the requested data. After that, we can return a response informing the user that a new user has been successfully created, and we pass the access token to the user.
For generating the token, we create a new function called getPersonalAccessToken(). Inside it, we can adjust the token lifetime by getting the remember_me value from the requested data. When a user sets the remember_me value to “true”, then we can make the token lifetime become longer.
We can apply the same logic in the login() function. First, we must validate the data and make sure that those credentials are correct. If so, we create a new token and return it to the user.
One more explanation before we try our application. In the user() function inside AuthController, we only have 1 line of code here. What we do is return the user data based on the requested token by calling the Auth::user() function. If you remember, the /user endpoint is protected by the Passport middleware. Thus, every route wrapped by the middleware will always have access to the Auth::user() or auth()->user() function. Amazing!
Let’s Try it!
Ok, so we have configured all necessary preparations for implementing the Passport authentication. Let’s try to create a new user with email@example.com email.
When we make the signup request, the server will return a response with the access_token information. We can use this token to create a new request to the /user endpoint, but before that, I think we need to try the /login first.
Great! Our /login endpoint is working as expected. Let’s try to request the /user endpoint with the access token we get. Copy the access token value there then paste it into the Authorization Bearer Token.
Very well, the app is working properly until now. The server knows that firstname.lastname@example.org owns the token that we passed it into the server. Lastly, we can easily log out/revoke the user token by requesting the /logout endpoint.
If a user is logged out, the token will be automatically revoked/expired. You can prove it by passing the expired token to the /user endpoint. It’ll be failed for sure.
Things to Improve
There might be some adjustments that we need to do with this Passport middleware. If you try to hit /user endpoint with an invalid token or try to log out a user more than once, you’ll get the message below:
First, a returned response isn’t in JSON format. Second, if the token we provide is invalid, then the Laravel will automatically redirect to the “login” route, but Laravel cannot find it because we don’t have it. There’s nothing wrong with the error message as above, and we can resolve it in several ways, for example:
- Change the redirected route in the app/Http/Middleware/Authenticate.php
- Handle the error in App\Exceptions\Handler.php
Remember that our API consumers never expect a response other than the JSON format. So, I think it’s best to handle the errors by returning them with the JSON format. In this case, I’ll use the second way to handle the error and modify the App\Exceptions\Handler.php.
But again, there are many ways to resolve it. I think I’ll leave the creativity to you. But at least, API consumers now are getting the error message with the JSON Format.
Well done! I hope this article can be your useful reference when developing the API Authentication with Laravel Passport. If you want to take further for authorizing users, take a look at my other article here. Thank you and see you again :)
Postman API Collection: https://www.getpostman.com/collections/22851130ea1101e7e422