Note: This guide works for all S3-compatible storage services like Minio, Digital Ocean Spaces, etc.
- Passport Documentation: https://laravel.com/docs/10.x/passport#main-content
- S3 Documentation: https://laravel.com/docs/10.x/filesystem#driver-prerequisites
Laravel Passport needs encryption keys to generate access tokens. But, by default, these encryption keys are Git ignored to avoid accidentally exposing it to the internet. When it comes to production environment, we can't just generate these encryption keys every time we deploy since that will result to access tokens not working suddenly.
To solve this, we can use AWS S3 as the location for the encryption keys instead of the repository. If you are ready, proceed to the guide below.
First, let's create a new Laravel project. At the time of this documentation, the latest Laravel version is 10. At this point, I'll assume that you have already setup these:
- Database Connection
- AWS S3 (or Minio) Connection
If everything's ready, we can now install and setup the Laravel Passport package.
// Install the Laravel Passport package
composer require laravel/passport --with-all-dependencies
// Install the AWS S3 package
composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies
// Run the migration files
php artisan migrate
// Create the initial clients and encryption keys
php artisan passport:install
When running php artisan passport:install
for the very first time, it will create the encryption keys as well as create the first personal access client
and password grant client
.
Again, by default, these oauth keys
are Git ignored so it is up to the developer where they'll store these keys and how to let Laravel know about them.
Since it is the goal of this guide, we will use AWS S3 as the storage for our oauth keys
. For the sake of demonstration, I will use Minio
instead of AWS S3
since it is readily available and since it's S3-compatible.
When storing sensitive data like oauth keys
to AWS S3, make sure it is not publicly accessible. In the picture above, I created two folders named public
and private
. Files in the public
folder are publicly accessible, whereas, files in private
folder can only be accessed using a temporary URL. For this to work, we need to create a bucket policy. Here's an example which works for Minio
.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": {
"AWS": [
"*"
]
},
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<your-bucket-name>"
]
},
{
"Effect": "Allow",
"Principal": {
"AWS": [
"*"
]
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::<your-bucket-name>/public/*"
]
}
]
}
I then uploaded the oauth keys
to the private
folder like so.
As proof that the private
folder is truly "private", let's try accessing the encryptions keys via URL. And as expected, the files are truly "private".
On the codebase, let's check first if we can connect to the S3 storage. You can do so using php artisan tinker
. When inside the Tinker
, run this command: Storage::disk('s3')->allFiles()
. It should show all your S3 files and you should be able to see the oauth keys
.
Btw, if you want to make the private files accessible via a link, you can create a temporary link like so. Also, if you want to view the contents of a private file (which we will be needing later), you can do it like so.
If you are on this part of the guide, I'll assume that you have no issues doing the basic connectivity testing above. So now, let's dive into how we can utilize the knowledge above to override Laravel Passport's default behavior.
Passport's default behavior when it comes to encryption keys is that it will look for them on Laravel's /storage
folder. As mentioned, those are Git ignored and so will not be present when we deploy our app to production. And so we uploaded the generated encryption keys in a bucket at AWS S3
(or Minio
in this example) as the first step. The next step to do is to make Passport utilize those S3-stored encryption keys.
To be precise, the next step involves overriding Passport's config
values. You should be able to see this config
values like so and as you can see, both private_key
and public_key
are empty.
To start the overriding process, look for the AppServiceProvider
(or any provider class) as we will need add some code to its boot
method.
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// Make sure that the configs are not yet cached.
// Better to cache the configs since reading from S3 might take some seconds.
if (! app()->configurationIsCached()) {
// Prepare the config key-file array
$keys = [
'passport.private_key' => '/private/oauth-private.key',
'passport.public_key' => '/private/oauth-public.key',
];
// Loop through the array
foreach ($keys as $key => $file) {
// Get the content of the file.
// If not empty, update the config.
if ($content = Storage::disk('s3')->get($file)) {
config([$key => $content]);
}
}
}
}
If you have followed the guide well, you should be able to see these new config values for passport.public_key
and passport.private_key
.
This config overriding technique can be used to alter some behaviors of Laravel and Laravel packages so just be careful and be mindful when doing so to avoid unnecessary side effects.
That's it! If you have questions or feedback, let me know by sending an email at jamescarloluchavez@gmail.com.