How to simply expose an endpoint with API Key in Laravel
Tags: laravel, api, apikey, tutorial
Published: 2023-08-02 21:20:22

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](https://laravel.com/docs/10.x/sanctum).

---

## First, the basics: What is an API Key?


![The Key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/torbrx6lpfxsr44za77p.jpg)


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](#step-1)
  2. [Create a middleware that checks the API Key](#step-2)
  3. [Create an example route and controller](#step-3)
  4. [Test with Postman](#step-4)
  5. [Create a test class](#step-5)

---

### 1. Create a key <a name="step-1"></a>

Add a variable in the `.env` file:

```ini
# .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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tod7fcrmd82jd0j9jqrc.png)

---

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

```php
// 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 <a name="step-2"></a>

```sh
$ php artisan make:middleware VerifyFastApiKey
```

```php
// app/Http/Middleware/VerifyFastApiKey.php

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

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

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

        return $next($request);
    }
}
```

Create an alias for this middleware:

```php
// 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 <a name="step-3"></a>

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

```php
// 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:

```sh
$ php artisan make:controller SomethingController
```

```php
// app/Http/Controllers/SomethingController.php

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

---

### 4. Test with Postman <a name="step-4"></a>

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9qa07640lyqa42m6xdze.jpg)

---

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/frye97orx3kzd8vdrzlz.jpg)

---

### 5. Create a test class <a name="step-5"></a>

Make test class:

```sh
$ php artisan make:test FastApiKeyTest
```

Add some test methods:

```php
// 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:

```sh
$ php artisan test
```

![Launch the tests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zu39iw0ck9wg3e7gt56p.png)

---

✸ Enjoy your coding! 

&nbsp;

###### If you liked this post, don't forget to add your **Follow** to my profile!

{% user tonyjoe %}

---

###### *If you want to preview my content, [Subscrive to my Newsletter](https://tonyjoe.dev/newsletter)*!