DEV Community

Jonathan Ruiz
Jonathan Ruiz

Posted on

7

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

Heroku

Amplify your impact where it matters most — building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay