Skip to content

Instantly share code, notes, and snippets.

@luchtech
Last active August 15, 2023 10:25
Show Gist options
  • Save luchtech/84fbbbbc27075838557a7785db2995a8 to your computer and use it in GitHub Desktop.
Save luchtech/84fbbbbc27075838557a7785db2995a8 to your computer and use it in GitHub Desktop.
S3-stored Encryption Keys for Laravel Passport

AWS S3-stored Encryption Keys for Laravel Passport

Note: This guide works for all S3-compatible storage services like Minio, Digital Ocean Spaces, etc.

Introduction

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.

Guide

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:

  1. Database Connection
  2. 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.

image image

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. image

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. image

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".

image

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.

image

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.

image image

Overriding Laravel Passport's Encryption Keys

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.

image

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]);
            }
        }
    }
}

Conclusion

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.

image

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment