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

---

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

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695764790098/51dd5dca-a922-47fd-90a8-4171718270b0.jpeg align="left")

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

---

### 1\. Create a key

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 align="left")

---

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

```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 = (
            filled($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

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

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 align="left")

---

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 align="left")

---

### 5\. Create a test class

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 align="left")

---

✸ Enjoy your coding!

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