How to simply expose an endpoint with API Key in Laravel

·

3 min read

How to simply expose an endpoint with API Key in Laravel

We all need, sooner or later, to expose an endpoint quickly and with the greatest possible security. Laravel provides advanced methods to manage authentication, whether username/password or API token.

But here we are talking about an agile method that we can use in a project where, for example, we do not intend to use users or use Laravel Sanctum.


First, the basics: What is an API Key?

An API Key is a key or token to be used for authenticating one or more server-to-server calls.

In other words, it is a secret key and therefore must never be exposed in the frontend code (such as that of a SPA).


Steps

  1. Create a key

  2. Create a middleware that checks the API Key

  3. Create an example route and controller

  4. Test with Postman

  5. Create a test class


1. Create a key

Add a variable in the .env file:

# .env
# ...

APP_FAST_API_KEY=paste_here_a_generated_api_key

The key must be in the .env file, because we don't want to keep it in repository.

💡 SMALL TIP to generate a key on the fly: Launch php artisan tinker and then simply \Str::random(64)

Generate an API Key on-fly


Then, add an array key that refer the variable in the .env file:

// config/app.php

return [
    // ...

    'fast_api_key' => env('APP_FAST_API_KEY'),

];

💡 SMALL TIP: In case, remember to launch php artisan cache:config


2. Create a middleware that checks the API Key

php artisan make:middleware VerifyFastApiKey
// app/Http/Middleware/VerifyFastApiKey.php

class VerifyFastApiKey
{
    public function handle(Request $request, Closure $next): Response
    {
        $apiKey = config('app.fast_api_key');

        $apiKeyIsValid = (
            filled($apiKey)
            && $request->header('x-api-key') === $apiKey
        );

        abort_if (! $apiKeyIsValid, 403, 'Access denied');

        return $next($request);
    }
}

Create an alias for this middleware:

// app/Http/Kernel.php

class Kernel extends HttpKernel
{
    // ...

    protected $middlewareAliases = [
        // ...

        'with_fast_api_key' => \App\Http\Middleware\VerifyFastApiKey::class,
    ];

}

3. Create an example route and controller

Add some routes in routes/api.php with the newly created with_fast_api_key middleware:

// routes/api.php

Route::group([
    'prefix' => 'v1',
    'middleware' => 'with_fast_api_key'
], function () {

    Route::post('/just/an/example', [SomethingController::class, 'justAnExample']);

    // ...
});

Create an example controller:

php artisan make:controller SomethingController
// app/Http/Controllers/SomethingController.php

class SomethingController extends Controller
{
    public function justAnExample()
    {
        return [
            'msg' => 'It works!'
        ];
    }
}

4. Test with Postman

First, call the endpoint /just/an/example without an API Key set in Headers, and check if it fails as expected:

Test the endpoint - 403


Finally, call the endpoint /just/an/example with the correct API Key set in the Header X-API-Key, and check if it works as expected:

Test the endpoint - OK 200


5. Create a test class

Make test class:

php artisan make:test FastApiKeyTest

Add some test methods:

// tests/Feature/FastApiKeyTest.php

class FastApiKeyTest extends TestCase
{
    public function test_fail_without_api_key(): void
    {
        $response = $this->postJson('/api/v1/just/an/example');

        $response->assertStatus(403);
    }

    public function test_fail_with_wrong_api_key(): void
    {
        $response = $this->postJson('/api/v1/just/an/example', [], [
            'X-API-Key' => 'a-wrong-key'
        ]);

        $response->assertStatus(403);
    }

    public function test_success_with_corrent_api_key(): void
    {
        $response = $this->postJson('/api/v1/just/an/example', [], [
            'X-API-Key' => config('app.fast_api_key')
        ]);

        $response->assertStatus(200);
    }
}

Finally, launch the tests:

php artisan test

Launch the tests


✸ Enjoy your coding!

If you liked this post, don't forget to add your Subscribe to my newsletter!