0

I have login metod in Laravel api. When I try to send request i have code 200 without content in Postman

AuthController.php

<?php

namespace App\Http\Controllers;

use App\Http\Requests\LoginRequest;
use App\Services\AuthService;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    protected $authService;

    public function __construct(AuthService $authService)
    {
        $this->authService = $authService;
    }

    public function login(LoginRequest $request)
    {
        try 
        {
            $res = $this->authService->loginUser($request);
            return response($res, 202);
        } 
        catch(Exception $e)
        {
            if($e instanceof AuthenticationException)
                return response(['message' => 'Nieprawidłowy adres email lub hasło!'], 401);
        }
    }

    public function logout(Request $request)
    {   
        try
        {
            $res = $this->authService->logoutUser($request);
            return response($res, 200);
        }
        catch(Exception $e)
        {
            throw $e;
        }
    }
}

api.php

<?php

use App\Http\Controllers\AuthController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::fallback(function () {
    return abort(404);
}); 

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::post('/auth/login', [AuthController::class, 'login']);
Route::post('/auth/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');

AuthService.php

<?php

namespace App\Services;

use App\Http\Requests\LoginRequest;
use App\Http\Resources\UserResource;
use App\Repositories\UserRepository;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;

class AuthService {

    protected $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    } 
    
    public function loginUser(LoginRequest $request) 
    {       
        $user = $this->userRepository->findByEmail($request['email']);
        
        if(!$user) throw new AuthenticationException();
        
        $isCorrectPassword = $this->userRepository->comparePassword($request['hasło'], $user);

        $this->validateUser($user, $isCorrectPassword);
        
        $token = $this->createToken($user);

        return $this->returnUserWithToken($user, $token);
    }

    public function createToken($user)
    {
        return $this->userRepository->createToken($user);
    }

    public function validateUser($user, $isCorrectPassword)
    {
        if (!$user || !$isCorrectPassword) throw new AuthenticationException();
    }

    public function returnUserWithToken($user, $token)
    {
        $res = [
            'data' => new UserResource($user),
            'token' => $token
        ];

        return $res;
    }

    public function logoutUser(Request $request)
    {
        try
        {
            $this->userRepository->deleteToken($request);
            return $res = ['message' => 'Wylogowanie przebiegło pomyślnie!'];
        }
        catch(Exception $e)
        {
            throw $e;
        }
    }
    
} 

UserRepository.php

<?php

namespace App\Repositories;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class UserRepository {

    protected $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function findByEmail(string $email)
    {
        return $this->user::where('email', $email)->first();
    }

    public function comparePassword(string $password, User $user)
    {
        return Hash::check($password, $user->password);
    }

    public function createToken(User $user)
    {
        return $user->createToken('token')->plainTextToken;
    }

    public function deleteToken(Request $request)
    {
        $request->user()->tokens()->delete();
    }
}

User.php (model)

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function product()
    {
        return $this->hasMany(Product::class);
    }
}

Postman screen

When i write wrong password or email i have this Postman screen2

This message is in Polish "Wrong email or password"

I dont know when i have mistakes... In another project i have similar method and it's works. I use sanctum.

MISIU
  • 19
  • 5

5 Answers5

1

You have this

if($e instanceof AuthenticationException)

What if it is not an instance of that exception? The code falls to the end of the controller function and returns nothing except a 200 code.

Its probably the syntax error in findByEmail mentioned by @Ali which is throwing a different error.

Snapey
  • 3,604
  • 1
  • 21
  • 19
1

I solved my problem... I just used laravel version 9 in this project, instead of the latest 10... The differences were in the columns in the personal_access_token table.. SQL was processing the query incorrectly... And since postman returned me the code 200 without content, the error was hard to catch. Currently, I rewrote it to a laravel version 10 project and it works. Thanks everyone for your commitment.

MISIU
  • 19
  • 5
  • Don't forget to catch any type of exception, not only the one you expect) – lezhni May 27 '23 at 12:41
  • the reason you were getting 200 was not due to the columns issue, it was due to only returning a response when you catch a specific error. Unecessary catching can cause very hard to trace bugs. – Bernard Wiesner May 27 '23 at 14:33
0

I would like to inform you that Request $request should not be used in service class or any repository class.

Change this line

$this->user::where('email', $email)->first();

to following line and then try.

$this->user->where('email', $email)->first();
Ali
  • 141
  • 2
  • 6
0

The problem in this line: $isCorrectPassword = $this->userRepository->comparePassword($request['hasło'], $user);

password field added to hidden property, this means your User model doesn't have this value after retrieving from database (it's null), and Hash::check caused an error, as second parameter should be a string

But you are not catching this exception in AuthController

lezhni
  • 292
  • 2
  • 12
0

As others have mentioned your issue is in not returning anything in case an exception other than AuthenticationException is thrown.

The bug that is throwing a different exception is here:

    public function findByEmail(string $email)
    {
        return $this->user::where('email', $email)->first();
    }

it should be User::where

    public function findByEmail(string $email)
    {
        return User::where('email', $email)->first();
    }

However I also would like to suggest you refactor your code.

  • unnecessary try catches
  • unnecessary repository pattern
  • over-engineering

Repository pattern has its place, but this is not a use case for it. Its mostly useful inside packages/libraries you develop that need flexibility to the consumer. Such as providing multiple DBs (mysql, postgres, sql, etc) to be used by eloquent, etc.

You could bring down your logic to this:

AuthController.php

<?php

namespace App\Http\Controllers;

use App\Http\Requests\LoginRequest;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;

class AuthController extends Controller
{

    public function login(LoginRequest $request)
    {
        $user = User::where('email', $request['email'])->first();
        
        if(!$user || !Hash::check($password, $user->password)) {
            throw new AuthenticationException("Nieprawidłowy adres email lub hasło!");
        } 

        return response([
            'data' => new UserResource($user),
            'token' => $user->createToken('token')->plainTextToken
        ], 202);

    }

    public function logout(Request $request)
    {   
        $request->user()->tokens()->delete();
        return response([
            'message' => 'Wylogowanie przebiegło pomyślnie!'
        ], 200);

    }
}
  • AuthService.php - deleted
  • UserRepository.php - deleted
Bernard Wiesner
  • 961
  • 6
  • 14
  • How I should create unit tests if ia according to you delete services and repositories? – MISIU May 27 '23 at 04:59
  • not working still 200 without content – MISIU May 27 '23 at 06:38
  • What exactly to you want to unit test? There is no logic you wrote, the unit logic is all internal package such as Hash and eloquent, no point in unit testing that. In this case feature/acceptance tests are appropriate. Just test the endpoints and the expected response. – Bernard Wiesner May 27 '23 at 12:29
  • In general go for feature testing over unit testing, it's easier to refactor later and covers your code better. – Bernard Wiesner May 27 '23 at 12:30
  • @BernardWiesner `$this->user::where` and `User::where` both works. Yes, first variant isn't a good one, but works) – lezhni May 27 '23 at 12:40
  • 1
    @lezhni yeah you are right, still works but bad practice – Bernard Wiesner May 27 '23 at 14:23