Testing Laravel API endpoints with jwt-auth
Tags: laravel, php, authentication, security
Published: 2020-03-20 04:02:30

---
title: Testing Laravel API endpoints with jwt-auth
published: true
description: A quick guide to building PHPUnit tests in Laravel for jwt-auth protected endpoints
tags: laravel, php, authentication, security
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/5qj48q9m1gyytrnj5y3h.jpg
---
JWT authentication with Laravel is made relatively headache-free using the fantastic jwt-auth library, which can be found here:
https://github.com/tymondesigns/jwt-auth

Once you have JWT up and running in your project, you will want to start thinking about building tests as you add functionality to your codebase.

The three-step approach I've taken in this article is as follows:
1. Create a default API user and setting the details in config
2. Add tests for token handling
3. Add tests for project endpoints, using token auth

When these tests pass, you can be sure that tokens can be issued, refreshed, and used on your protected endpoints.

## 1. Setting a default API user
While you can certainly use a factory to automate this, sometimes it makes sense to use pre-saved credentials to test auth.

To that end, we can create a default `User` model for this purpose (manually, or using a seeder), and store the email and password in the `.env` file.

The best way to make these .env variables available in tests is to create a config file (e.g. `app\config\api.php`):

```php
/*
|--------------------------------------------------------------------------
| API specific config
|--------------------------------------------------------------------------
*/

return [
    // Default API user - mostly used for tests.
    'apiEmail' => env('API_USER_EMAIL'),
    'apiPassword' => env('API_USER_PASSWORD')
];
```

## 2. Testing the auth routes
The three endpoints we can test here are login, logout, and refresh. This will ensure we can:
* Generate a token
* Pass the token into a logout request
* Regenerate a token on the fly

### Logging in
Here we'll hit the auth login route with the default user's credentials (email and password) and get a token back.

To ensure that we're getting the proper response, we'll assert a 200 HTTP status check and the 3 necessary fields in the JSON response.

```php
/**
* Login as default API user and get token back.
*
* @return void
*/
public function testLogin()
{
    $baseUrl = Config::get('app.url') . '/api/auth/login';
    $email = Config::get('api.apiEmail');
    $password = Config::get('api.apiPassword');

    $response = $this->json('POST', $baseUrl . '/', [
        'email' => $email,
        'password' => $password
    ]);

    $response
        ->assertStatus(200)
        ->assertJsonStructure([
            'access_token', 'token_type', 'expires_in'
        ]);
}
```

### Logging Out
We can now use our token from the login test to request a logout action for the default user.

Since I have set up the logout method in the Auth Controller to return an exact message, we can assert an exact match on the return message.

**Note:** Add a use statement for `Tymon\JWTAuth\Facades\JWTAuth` to easily access previously generated tokens.

```php
/**
* Test logout.
*
* @return void
*/
public function testLogout()
{
    $user = User::where('email', Config::get('api.apiEmail'))->first();
    $token = JWTAuth::fromUser($user);
    $baseUrl = Config::get('app.url') . '/api/auth/logout?token=' . $token;

    $response = $this->json('POST', $baseUrl, []);

    $response
        ->assertStatus(200)
        ->assertExactJson([
            'message' => 'Successfully logged out'
        ]);
}
```

### Refreshing the token
Finally, we'll refresh the token for the default user.

```php
/**
* Test token refresh.
*
* @return void
*/
public function testRefresh()
{
    $user = User::where('email', Config::get('api.apiEmail'))->first();
    $token = JWTAuth::fromUser($user);
    $baseUrl = Config::get('app.url') . '/api/auth/refresh?token=' . $token;

    $response = $this->json('POST', $baseUrl, []);

    $response
        ->assertStatus(200)
        ->assertJsonStructure([
            'access_token', 'token_type', 'expires_in'
        ]);
}
```

## 3. Testing the endpoints
Now that we're confident the token handling is working as expected, we can now use our default API user to authenticate to JWT protected endpoints. 

Here's an example where we grab all data from the `User` model:

```php
/**
* Get all users.
*
* @return void
*/
public function testGetUsers()
{
    $user = User::where('email', Config::get('api.apiEmail'))->first();
    $token = JWTAuth::fromUser($user);
    $baseUrl = Config::get('app.url') . '/api/users?token=' . $token;

    $response = $this->json('GET', $baseUrl . '/', []);

    $response->assertStatus(200);
}
```

## Next Steps
Now that you have a basic framework for automated endpoint authentication, you can easily add your tests as you build out your project. 

Hope you enjoyed this post!