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:
- Create a default API user and setting the details in config
- Add tests for token handling
- 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!
Top comments (0)