codeburst

Bursts of code to power through your day. Web Development articles, tutorials, and news.

Follow publication

Laravel Passport: Assigning a UUID to your OAuth Clients

Richard Toms
codeburst
Published in
5 min readApr 12, 2019

Photo by JESHOOTS.COM on Unsplash

The base Laravel Passport package comes with some amazing functionality out of the box. My only criticism is the design of the OAuth Clients; having incremental IDs over something like a UUID. Some see this as a security concern and personally it didn’t sit right with me.

So I’ve taken a little bit of time to see how I can change this and what bits need tweaking in order to make this work, and this experiment turned out to be a success!

Overriding the source

Updating the migrations

Looking through the Laravel Passport docs, it appears that you can publish the migrations that it uses. This means that we can override them and update the migrations to use a UUID field instead of an incremental one.

So first we will publish the Laravel Passport migrations:

php artisan vendor:publish --tag=passport-migrations

Great! Now we should have a number of migrations added to our database/migrations directory:

./2016_06_01_000001_create_oauth_auth_codes_table.php ./2016_06_01_000002_create_oauth_access_tokens_table.php ./2016_06_01_000003_create_oauth_refresh_tokens_table.php ./2016_06_01_000004_create_oauth_clients_table.php ./2016_06_01_000005_create_oauth_personal_access_clients_table.php

Now we will go through these in order and update the database structure as necessary to accomodate for our UUIDs.

First up is the oauth_auth_codes table, auth_codes are connected to oauth_clients so we will need to update the migration to look a little like this:

/** database/migrations/2016_06_01_000001_create_oauth_auth_codes_table.php */
public function up()
{
Schema::create('oauth_auth_codes', function (Blueprint $table) {
$table->string('id', 100)->primary();
$table->integer('user_id');
$table->uuid('client_id');
$table->text('scopes')->nullable();
$table->boolean('revoked');
$table->dateTime('expires_at')->nullable();
});
}

Next up is the oauth_access_tokens table, access_tokens are also linked to specific clients so we can update that column:

/** database/migrations/2016_06_01_000002_create_oauth_access_tokens_table.php */ 
public function up()
{
Schema::create('oauth_access_tokens', function (Blueprint $table) {
$table->string('id', 100)->primary();
$table->integer('user_id')->index()->nullable();
$table->uuid('client_id');
$table->string('name')->nullable();
$table->text('scopes')->nullable();
$table->boolean('revoked');
$table->timestamps();
$table->dateTime('expires_at')->nullable();
});
}

The oauth_clients table is after that, this will be the time we override the id column to be a UUID and primary rather than incrementing:

/** database/migrations/2016_06_01_000004_create_oauth_clients_table.php */ 
public function up()
{
Schema::create('oauth_clients', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->integer('user_id')->index()->nullable();
$table->string('name');
$table->string('secret', 100);
$table->text('redirect');
$table->boolean('personal_access_client');
$table->boolean('password_client');
$table->boolean('revoked');
$table->timestamps();
});
}

Finally, the ouath_personal_access_clients table needs the client_id column changed:

/** database/migrations/2016_06_01_000005_create_oauth_personal_access_clients_table.php */
public function up()
{
Schema::create('oauth_personal_access_clients', function (Blueprint $table) {
$table->increments('id');
$table->uuid('client_id')->index();
$table->timestamps();
});
}

Now that all of our custom migrations are done, we need to tell Laravel to not run the Passport migrations that are in our vendor/ directory. To do this we need to open our AppServiceProvider where we can add this line to the register method:

use Laravel\Passport\Passport;public function register()
{
Passport::ignoreMigrations();
}

The first step of this problem has now been overcome, once we migrate our tables we will have UUID type columns in place of the integer ones that used to be there.

Trying to install Passport

Our next step is to try to install Passport so that it can create our Personal Access client and Password client for us. To do this we run the artisan command php artisan passport:install.

Here is where we see our first issue with this change crop up. As the default Passport configuration is expecting to not need to set an id field, it doesn't set one and we end up with a duplicated Primary column id error. So let's take a look at the source and see what we can do.

Making a custom OAuth Client Model

The OAuth Client model isn’t expecting to have to set it’s own ID, by default it allows the database assign it an auto-incrementing ID. So how do we change this?

Well, because Eloquent Models defaults to having an integer identifier, the $incrementing property is set to true and will therefore cast the id field as an integer when it is accessed. This will become an issue when going through the Authorization Code Flow in production.

This means that we need to have our own Client model that interacts with the oauth_clients table in the same way that the Laravel Passport model does. We then need to tell Laravel to use our version instead of its.

After looking through the Laravel Passport source code, I can see that the OAuth Client model is retrieved by a static method from the Laravel\Passport\Passport class. This means that we can overwrite this with relative ease if you know how static properties on classes work (If you don't, time for a quick lesson).

A static property defined on a class cannot be directly overwritten by a simple assignment, but it can be overwritten by a static method on the same class. All instances of this class will share this static property, so only update them when absolutely necessary.

Looking at the Laravel\Passport\Passport class, we can find the following static property and method that will allow us to take over:

/**
* The client model class name.
*
* @var string
*/
public static $clientModel = 'Laravel\Passport\Client';
/**
* Set the client model class name.
*
* @param string $clientModel
* @return void
*/
public static function useClientModel($clientModel)
{
static::$clientModel = $clientModel;
}

This method allows us to provide a path to the model that Passport will use when trying to query OAuth Clients.

So, first we will need to make a new Client model in our app/Models/Passport directory that extends the Laravel version to not interfere with other functionality.

We need to add two things to it, tell the Model not to increment IDs and in turn to not treat IDs as integers, and a way to attach a UUID on creation of a Client.

To do this we can set the $incrementing property to false which takes care of the first point.

To intercept a model that is about to be created and have the ability to modify its attributes we need to create a boot method on the model. The boot method allows us to hook into the Eloquent Model events that get dispatched when an action is about to take place or has taken place. In this instance we will hook into the creating event and set a UUID on the model before it gets saved to the database.

Our model class should look a little like the below:

namespace App\Models\Passport;use Webpatser\Uuid\Uuid;
use Laravel\Passport\Client as OAuthClient;
class Client extends OAuthClient
{
/**
* Indicates if the IDs are auto-incrementing.
*
* @var bool
*/
public $incrementing = false;
public static function boot()
{
static::creating(function ($model) {
$model->uuid = Uuid::generate()->string;
});
}
}

Now that we have this, we need to tell Laravel to use this instead of its version. To do this we go to our AppServiceProvider and then go to the register method just like before and then we call that Passport::useClientModel() method that we looked at earlier, passing in the namespace of our custom model:

public function register() 
{
Passport::ignoreMigrations();
Passport::useClientModel('App\Passport\Client');
}

We’re done!

That’s all we need to do, we’ve overwritten Laravel Passport’s default OAuth Client behaviour with our own and we now have UUIDs in place of the typical incremental IDs with no adverse effects.

Originally published at toms.dev.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in codeburst

Bursts of code to power through your day. Web Development articles, tutorials, and news.

Written by Richard Toms

A genuine geek that loves to build stuff, software is my tool.

Responses (2)

Write a response