DEV Community

Jonathan Ruiz
Jonathan Ruiz

Posted on

Laravel 11 API Rest Auth with jwt-auth

Installing via composer

composer require tymon/jwt-auth
Enter fullscreen mode Exit fullscreen mode

Publishing the config

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
Enter fullscreen mode Exit fullscreen mode

This command will create a file in config/jwt.php
There you can modify the default config like the expiration time of the token

Generating the secret key

php artisan jwt:secret
Enter fullscreen mode Exit fullscreen mode

This command will add the key JWT_SECRET in your .env

Adding api guard
Inside the config/auth.php
Make the following changes

'defaults' => [
    'guard' => env('AUTH_GUARD', 'api'),
    'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
],
Enter fullscreen mode Exit fullscreen mode
'guards' => [
    'web' => [
         'driver' => 'session',
         'provider' => 'users',
    ],
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
Enter fullscreen mode Exit fullscreen mode

Updating the User model

use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
Enter fullscreen mode Exit fullscreen mode

Adding the require methods

public function getJWTIdentifier()
    {
        return $this->getKey();
    }
public function getJWTCustomClaims()
    {
        return [];
    }
Enter fullscreen mode Exit fullscreen mode

Creating the UserController

php artisan make:controller UserController --api --model=User
Enter fullscreen mode Exit fullscreen mode

Defining the store method

public function store(Request $request)
{
    $data = $request->validate([
        'name' => 'required',
        'email' => 'required|email|unique:users',
        'password' => 'required|min:6',
    ]);
    $data['password'] = bcrypt($data['password']);
    $user = User::create($data);
    return response()->json($user, 201);
}
Enter fullscreen mode Exit fullscreen mode

Defining the me method

public function me()
{
    return response()->json(auth()->user());
}
Enter fullscreen mode Exit fullscreen mode

Creating the AuthController

php artisan make:controller AuthController
Enter fullscreen mode Exit fullscreen mode

Defining the login method

public function login(Request $request)
{
    $credentials = $request->validate([
        'email' => 'required|email',
        'password' => 'required'
    ]);
    $token = auth()->attempt($credentials);

    if (!$token) {
        return response()->json([
            'status' => 401,
            'message' => 'Credenciales incorrectas'
        ], 401);
    }

    return response()->json([
        'token' => $token,
        'user' => auth()->user(),
        'expire_in' => auth()->factory()->getTTL() * 60
    ]);
}
Enter fullscreen mode Exit fullscreen mode

Defining the me method

public function me()
{
    return response()->json(auth()->user());
}
Enter fullscreen mode Exit fullscreen mode

Creating GuestMiddleware

php artisan make:middleware GuestMiddleware
Enter fullscreen mode Exit fullscreen mode

This command will create the middleware in app/Http/Middlewares
import use Illuminate\Support\Facades\Auth;

public function handle(Request $request, Closure $next, ...$guards): Response
{
    if (Auth::guard($guards)->check()) {
        abort(401, 'You are not allowed to access this resource');
    }
    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

This middleware will prevent the users authenticated can access to some routes like login route

Creating AuthMiddleware

php artisan make:middleware AuthMiddleware
Enter fullscreen mode Exit fullscreen mode

This command will create the middleware in app/Http/Middlewares
import use Illuminate\Support\Facades\Auth;

public function handle(Request $request, Closure $next, ...$guards): Response
{
    if (!Auth::guard($guards)->check()) {
        abort(401, 'You are not allowed to access this resource');
    }
    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

This middleware will prevent the users unauthenticated can access to some routes like me route

Defining the routes

use App\Http\Controllers\UserController;
use App\Http\Controllers\AuthController;
use App\Http\Middleware\GuestMiddleware;
use App\Http\Middleware\AuthMiddleware;

Route::middleware(GuestMiddleware::class)->group(function () {
    Route::post('login', [AuthController::class, 'login'])->name('login');
});
Route::middleware(AuthMiddleware::class)->group(function () {
    Route::get('me', [AuthController::class, 'me'])->name('me');
});

Route::apiResource('users', UserController::class);
Enter fullscreen mode Exit fullscreen mode

Creating the AuthTest

php artisan make:test AuthTest
Enter fullscreen mode Exit fullscreen mode

This command will create a file in tests/Feature

use Illuminate\Foundation\Testing\RefreshDatabase;
use PHPUnit\Framework\Attributes\Test;
class AuthTest extends TestCase
{
    use RefreshDatabase;
    private User $user;
    protected function setUp(): void
    {
        parent::setUp();
        $this->user = User::create([
            'name' => 'User',
            'email' => 'user@gmail.com',
            'password' => '1234',
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

Tests methods

#[Test]
public function should_create_a_user(): void
{
    $data = [
        'name' => 'Test',
        'email' => 'test@gmail.com',
        'password' => '1234',
    ];

    $response = $this->postJson(route('users.store'), $data);

    $response->assertStatus(201);
    $this->assertDatabaseHas('users', ['email' => 'test@gmail.com']);
}

#[Test]
public function should_login(): void
{
    $data = [
        'email' => $this->user->email,
        'password' => '1234',
    ];
    $response = $this->postJson(route('login'), $data);

    $response->assertStatus(200);
    $response->assertJsonStructure(['token']);
}

#[Test]
public function should_not_login(): void
{
    $data = [
        'email' => $this->user->email,
        'password' => '12345',
    ];
    $response = $this->postJson(route('login'), $data);
    $response->assertStatus(401);
}

#[Test]
public function should_return_user_authenticated(): void
{
    $response = $this->actingAs($this->user)->getJson(route('me'));
    $response->assertStatus(200);
    $response->assertJsonStructure(['id', 'name', 'email']);
    $response->assertJson(['id' => $this->user->id]);
}
Enter fullscreen mode Exit fullscreen mode

Remove comments in the file phpunit.xml

<env name="APP_ENV" value="testing"/>
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_STORE" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="PULSE_ENABLED" value="false"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
Enter fullscreen mode Exit fullscreen mode

Running the tests

php artisan test --filter AuthTest
Enter fullscreen mode Exit fullscreen mode

Image description

Top comments (0)