When you have to choose how to start a new project you have many choices if you think about languages, frameworks and authentication.
In our case (we = company where I work) we're going with a classic SPA: Vue + Laravel API. Additionally, we're using Auth0 to authenticate our SPA, because it's a company backoffice and with Auth0 we're creating a kind-of-SSO between all our backoffices.
Sounds cool, right? I mean, there are plenty of tutorials about this for Vue. Indeed. But I found a lack of tutorials for Laravel.
Wait wait, does Auth0 have an official quickstart for Laravel? They have two!? One for classic webapp application and one for "API only"? This is awesome! And there's more? Their guide is for Laravel 6? Well, I'm using Laravel 8, I'm sure it wor--. Yes, that's why we're here.
Installation
One of the best things Auth0 did is a provider for Laravel to integrate their service: github.com/auth0/laravel-auth0. Before you go, I have to sadly inform you that when I'm writing this and the provider is at v6.1.0 it still doesn't support Guzzle7, so you have to downgrade it to 6.5.5 by running composer require guzzlehttp/guzzle:^6.5.5
. Very sad times. There's already an issue for this, we hope they upgrade it soon.
Let's focus folks and proceed with the tutorial: install the provider running composer require auth0/login:"~6.0"
and the magic word of Laravel and Composer will automatically discover and register it.
Usage
Now, following their README, we can set up a Guard and use it as a middleware to protect our routes. Open your config/auth.php file and add a new Guard:
'guards' => [
...
'auth0' => [
'driver' => 'auth0',
'provider' => 'auth0',
],
],
Now, in the same file, add the provider too:
'providers' => [
...
'auth0' => [
'driver' => 'auth0',
],
],
Given that, we can protect our routes using the guard as a middleware:
Route::get('/private', function () {
return response()->json(['message' => 'Private endpoint']);
})->middleware('auth:auth0');
From now on, you can only access /private
endpoint providing a valid JWT token in the Authorization
header like this: Authorization: Bearer <jwt-here>
.
Testing
Since Laravel 8 the way Models Factories are made is a bit different, in fact, you have to add HasFactory
trait to your model to create a new "fake" model. I think that currently Auth0 still does not support this for the Auth0User
model and, because of that, we can't (still) rely on Auth0User::factory()
syntax.
To workaround this we're just gonna initialize a new Auth0\Login\Auth0User
instance and using that for the actingAs
helper.
When creating an Auth0User instance you should provide 2 arguments:
array $userInfo
, where you can find thesub
key (provided by Auth0) or theuser_id
key;string|null $token
- I think this is the JWT, but I don't really know.
Anyway for our test where we don't need to store the user nor to retrieve it from the sub
/ user_id
property, we can just leave them both "empty".
namespace Tests\Feature;
use Tests\TestCase;
use Auth0\Login\Auth0User;
use Illuminate\Http\Response;
class PrivateEndpointTest extends TestCase
{
public function testAuthorizedWithFakeUser()
{
$user = new Auth0User([], null);
$this->actingAs($user, 'auth0')
->getJson('/api/private')
->assertStatus(Response::HTTP_OK)
->assertExactJson([
'message' => 'Private endpoint',
]);
}
}