loading...
Cover image for Testing Laravel API endpoints with jwt-auth

Testing Laravel API endpoints with jwt-auth

adam_crampton profile image Adam Crampton ・3 min read

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):

/*
|--------------------------------------------------------------------------
| 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.

/**
* 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.

/**
* 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.

/**
* 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:

/**
* 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!

Discussion

pic
Editor guide