If you’ve ever tried to implement authentication in a Laravel application and found yourself stuck trying to manage user sessions and permissions across multiple routes and controllers, you’re not alone. I’ve been there too, and it’s frustrating.
However, what if you could securely authenticate users without having to worry about storing their passwords or managing session tokens? With OAuth 2.0, you can offload this complexity to a central authentication server, freeing up your application to focus on its core functionality. By the end of this tutorial, You’ll build an OAuth-enabled Laravel app that protects sensitive routes with client credentials and uses refresh tokens to handle long-lived sessions.
Configuring Laravel to Use OAuth 2.0
To start implementing OAuth 2.0 in your Laravel application, you’ll need to configure it properly.
First, ensure that the oauth2 package is installed via Composer by running the following command in your terminal:
composer require laravel/passport
After installing the required packages, run the migration to create the OAuth tables in your database using the following Artisan command:
php artisan passport:install
Next, configure Laravel’s oauth2 package by publishing its configuration file and setting up the default settings. Open a terminal and navigate to your project root directory, then use the following command to publish the configuration file:
php artisan vendor:publish --provider="Laravel\Passport\PassportServiceProvider"
After publishing the configuration file, configure it by opening the config/oauth2.php file in your favorite text editor. Update the `tokens’ expiration time and other settings according to your application’s requirements.
Here is a sample configuration:
return [
'driver' => 'passport',
'secret' => env('PASSPORT_SECRET'),
'token_ttl' => 60,
];
Configure the oauth2 driver as the authentication provider by opening the config/auth.php file and updating the providers section. Set 'oauth2' as the default authentication provider.
return [
// ...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'oauth2' => [
'driver' => 'passport',
],
],
];
That’s it for this section. With the oauth2 package installed and configured, you’re ready to move on to implementing OAuth clients in your database.
Next, run the database migrations to create the necessary tables in your database:
php artisan migrate
With these steps complete, you should now have the passport package installed and configured in your Laravel application.
Note that this is just one of many packages available for implementing OAuth 2.0 in Laravel. If you prefer to use a different package or roll your own implementation, feel free to do so – but laravel/passport is a great choice due to its simplicity and ease of use.
Generating API Keys for Clients
In a typical OAuth implementation, clients (e.g., mobile apps, web applications) need credentials to authenticate and obtain an access token. We’ll use the passport package’s built-in functionality to generate API keys for our clients.
First, we need to create a new client in the database using the following command:
php artisan passport:client --name="My Web App" --allowed-grant-types=personal-access-token
This will create a new client with an ID and secret. We’ll use these credentials later to authenticate our client.
Now, let’s modify the clients table to include a column for API keys:
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
Schema::table('oauth_clients', function (Blueprint $table) {
$table->string('api_key')->after('secret');
});
Run the migration:
php artisan migrate
Next, we’ll update our client model to generate an API key:
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Laravel\Passport\HasApiTokens;
class Client extends Model implements Authenticatable
{
use HasApiTokens;
public function getApiKey()
{
if (!$this->api_key) {
$this->api_key = Str::random(32);
$this->save();
}
return $this->api_key;
}
}
With this setup, when we create a new client using the passport:client command, it will automatically generate an API key for us.
Creating an OAuth Client in the Database
To use OAuth 2.0 with Laravel Passport, we need to create a client that will be responsible for authenticating our users. This can be done by creating a new migration and seeding it with some sample data.
First, let’s run the following command to generate a new migration:
php artisan make:migration create_oauth_clients_table
In the generated create_oauth_clients_table file, add the following code:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOAuthClientsTable extends Migration
{
public function up()
{
Schema::create('oauth_clients', function (Blueprint $table) {
$table->id();
$table->string('client_id');
$table->string('client_secret');
$table->string('redirect_uri');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('oauth_clients');
}
}
Run the migration to create the oauth_clients table:
php artisan migrate
Next, we’ll need to seed this table with some sample data. Run the following command to generate a new seeder:
php artisan make:seeder OAuthClientsTableSeeder
In the generated OAuthClientsTableSeeder file, add the following code:
use Illuminate\Database\Seeder;
use App\Models\OAuthClient;
class OAuthClientsTableSeeder extends Seeder
{
public function run()
{
OAuthClient::create([
'client_id' => 'my-client-id',
'client_secret' => 'my-client-secret',
'redirect_uri' => 'http://example.com/callback',
]);
}
}
Run the seeder to populate the oauth_clients table:
php artisan db:seed --class=OAuthClientsTableSeeder
This will create a new OAuth client with the specified ID, secret, and redirect URI. We’ll use this client in the next section when implementing login and registration with OAuth.
Implementing Login and Registration with OAuth
To enable users to log in and register using OAuth, we’ll create two routes that will handle these operations. We’ll use Laravel’s built-in Redirect::intended method to redirect the user after authentication.
// routes/api.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\OAuthController;
Route::post('/login', [OAuthController::class, 'login']);
Route::post('/register', [OAuthController::class, 'register']);
Next, we’ll create a controller to handle the OAuth login and registration logic. This controller will use Laravel’s Auth facade to authenticate users.
// app/Http/Controllers/OAuthController.php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
class OAuthController extends Controller
{
public function login(Request $request)
{
// Validate the request
$validatedData = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
// Attempt to authenticate the user
if (!Auth::attempt($validatedData)) {
return response()->json(['error' => 'Invalid credentials'], 401);
}
// If authenticated, generate an access token for the user
$accessToken = Auth::user()->createToken('oauth-access-token')->plainTextToken;
return response()->json(['access_token' => $accessToken]);
}
public function register(Request $request)
{
// Validate the request
$validatedData = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
// Create a new user
$user = User::create($validatedData);
// Generate an access token for the newly created user
$accessToken = $user->createToken('oauth-access-token')->plainTextToken;
return response()->json(['access_token' => $accessToken]);
}
}
This is a basic implementation of OAuth login and registration using Laravel’s built-in Auth facade.
Protecting Routes with OAuth Authentication
Now that our users can register and log in using OAuth, we need to protect routes that require authentication.
To do this, we’ll use Laravel’s built-in auth:api middleware. This middleware will check if the incoming request has a valid access token for the client making the request.
First, let’s create a new route group to define our protected routes:
// routes/api.php
Route::group(['middleware' => 'auth:api'], function () {
// Protected routes go here...
});
Next, we’ll add the auth:api middleware to the route that requires OAuth authentication. For example, let’s say we have a /users endpoint that returns a user’s information:
// routes/api.php
Route::get('/users', [UserController::class, 'show'])->middleware('auth:api');
When an unauthenticated client makes a request to the /users endpoint, it will return a 401 Unauthorized response. If a valid access token is present in the request, the middleware will authenticate the client and allow the request to proceed.
That’s it! With this setup, any route that uses the auth:api middleware will require OAuth authentication before allowing access.
Handling Refresh Tokens and Revoking Access
When implementing OAuth 2.0, it’s crucial to handle refresh tokens and revoke access properly to prevent unauthorized access and maintain security.
Refresh Tokens
To enable refresh tokens, you need to configure the password_grant in your config/oauth.php file:
'password_grant' => [
'access_token_ttl' => 60, // default is 1 hour
'refresh_token_ttl' => 30 * 24 * 60, // default is 30 days
],
You can also specify a custom token lifetime for each client. This configuration affects how long the access and refresh tokens are valid.
Revoking Access
To revoke an access token or its corresponding user’s refresh token, you need to update the oauth_access_tokens table in your database:
use Illuminate\Support\Facades\DB;
// Revoke an access token by its ID
DB::table('oauth_access_tokens')
->where('id', 1) // replace with actual ID
->update(['revoked' => true]);
// Revoke all tokens for a user by their ID
DB::table('oauth_access_tokens')
->where('user_id', 1) // replace with actual ID
->update(['revoked' => true]);
To handle revocation properly, consider implementing a scheduled task to clean up revoked tokens periodically.
By handling refresh tokens and revoking access correctly, you can ensure your OAuth implementation remains secure.
Testing Your OAuth 2.0 Implementation
Now that you have a working OAuth 2.0 implementation in place, it’s time to test it thoroughly. This will ensure that your application behaves as expected and catches any potential issues before they reach production.
To test the login functionality using OAuth, navigate to http://localhost:8000/oauth/token (adjust the URL according to your Laravel project path) in a REST client like Postman or cURL. You’ll need to provide the required parameters:
curl -X POST \
http://localhost:8000/oauth/token \
-H 'Content-Type: application/json' \
-d '{"grant_type":"password","client_id":1,"client_secret":"test_client","username":"john.doe@example.com","password":"password"}'
Replace the client_id and client_secret with your client’s details from the database. The response should include an access token that you can use to authenticate subsequent requests.
To test protected routes, navigate to a route like http://localhost:8000/profile (assuming this is one of the protected routes) in your browser or REST client. If everything is set up correctly, you’ll be redirected to the login page where you can enter your credentials and obtain an access token. Then, append the access token to the URL as a Bearer token:
curl -X GET \
http://localhost:8000/profile \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Verify that you’re able to access protected routes without any issues. With these tests, you should have confidence in your OAuth 2.0 implementation.
Your application is now more secure than ever, thanks to the added layer of authentication provided by OAuth 2.0.
Frequently Asked Questions
What is OAuth 2.0, and how does it differ from traditional authentication methods?
OAuth 2.0 is an authorization framework that allows users to grant third-party applications limited access to their resources on another service provider without sharing their login credentials. Unlike traditional authentication methods, OAuth 2.0 offloads the complexity of user session management and password storage to a central authentication server.
I’m getting an error ‘Cannot create default store’ when running php artisan passport:install. What’s causing this?
This error typically occurs when you’re trying to install Passport in a Laravel project that already has a custom user model. To resolve the issue, make sure your user model is correctly configured and update the providers section in your config/auth.php file accordingly.
How does OAuth 2.0 handle long-lived sessions using refresh tokens?
OAuth 2.0 uses refresh tokens to handle long-lived sessions by allowing clients to obtain a new access token when the original one expires, without requiring users to re-authenticate.
What’s the difference between OAuth 2.0 and JWT (JSON Web Tokens) for authentication?
While both OAuth 2.0 and JWT provide secure authentication mechanisms, OAuth 2.0 is designed for authorization, allowing clients to access specific resources on behalf of users, whereas JWT is primarily used for authentication, issuing a token that contains user data after successful login.
I’m comparing OAuth 2.0 with another popular authentication package, Sanction. What are the key differences?
Sanctum and Passport (used in this tutorial) both provide robust authentication functionality for Laravel applications. However, Sanctum is designed to be more lightweight and flexible, whereas Passport focuses on providing a simple and secure implementation of OAuth 2.0.
