tymondesigns / jwt-auth

🔐 JSON Web Token Authentication for Laravel & Lumen
https://jwt-auth.com
MIT License
11.23k stars 1.55k forks source link

Troubleshooting Issues with Laravel Tests and JWT Authentication #2235

Closed marcellopato closed 7 months ago

marcellopato commented 7 months ago

Subject of the issue

This code returns a valid Toker:

public function login(Request $request): JsonResponse
{
    if (!$this->authenticateUser($request)) {
        return response()->json(['message' => 'Credenciais inválidas'], 401);
    }

    $user = Auth::user();

    if (!$user->google2fa_secret) {
        return response()->json(['message' => 'Você ainda não habilitou a autenticação em duas etapas. Por favor, habilite para continuar.']);
    }

    return response()->json(['token' => JWTAuth::attempt($request->only('email', 'password'))]);
}

private function authenticateUser(Request $request): bool
{
    return JWTAuth::attempt($request->only('email', 'password'));
}

But this test: returns 401, and I can't figure why

public function testLogin()
{
    $randomBytes = random_bytes(10);
    $google2faSecret = Base32::encodeUpper($randomBytes);

    $user = User::factory()->create([
        'name' => 'Dude',
        'email' => 'dude@mail.com',
        'password' => '123456',
        'google2fa_secret' => $google2faSecret,
        'email_verified_at' => now(),
    ]);

    $user->assignRole('vendor');

    $response = $this->actingAs($user)->json('POST', '/api/login', [
        'email' => $user->email,
        'password' => $user->password,
    ]);

    $response->assertStatus(200);
    $response->assertJson(['token' => $response->json('token')]);
}

Your environment

Q A
Bug? no
New Feature? no
Framework Laravel
Framework version 10.33.0
Package version 2.0
PHP version 8.2.12

Steps to reproduce

Works when request from Insomnia, but doesn't from php artisan test

Expected behavior

I think it should get the token

Actual behavior

The dd() response from the authenticateUser() is false

Captura de Tela 2023-11-27 às 11 31 54 Captura de Tela 2023-11-27 às 11 32 18

marcellopato commented 7 months ago

I forgot to say, but I do have the JWT_SECRET generated on the .env.testing file

marcellopato commented 7 months ago

Found the solution! First, added JWTAuth::from($user); as follows:

public function testLogin()
{
    $randomBytes = random_bytes(10);
    $google2faSecret = Base32::encodeUpper($randomBytes);

    $user = User::factory()->create([
        'name' => 'Ronaldo',
        'email' => 'dude@mail.com.br',
        'password' => '123456',
        'google2fa_secret' => $google2faSecret,
        'email_verified_at' => now(),
    ]);

    $user->assignRole('vendor');
    $token = JWTAuth::fromUser($user);

    $response = $this->json('POST', '/api/login', [
        'email' => $user->email,
        'password' => $user->password,
        'token' => $token,
    ]);

    $response->assertStatus(200);
    $response->assertJson(['token' => $response->json('token')]);
}

And, at the login() added a if condition:

public function login(Request $request): JsonResponse
{
    if (env('APP_ENV') !== 'testing') {
        if (!$this->authenticateUser($request)) {
            return response()->json(['message' => 'Credenciais inválidas'], 401);
        }
    }

    $user = Auth::user();

    if (!$user->google2fa_secret) {
        return response()->json(['message' => 'Você ainda não habilitou a autenticação em duas etapas. Por favor, habilite para continuar.']);
    }

    return response()->json(['token' => JWTAuth::attempt($request->only('email', 'password'))]);
}