Laravel JWT makes new request insted of sending status - laravel

I'm making an API in laravel with tymon/jwt-auth package. based on https://jwt-auth.readthedocs.io/en/develop/
Login works fine.
Sending GET request with right token works fine.
BUT when I send a GET request with incorrect/no token it redirects me to POST login (wants me to login) rather than send a 401
GET /api/reviews with no Auth:
The GET method is not supported for this route. Supported methods: POST.
api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::group([
'prefix' => 'auth',
], function ($router) {
Route::post('login', [AuthController::class, 'login'])->name('login');
});
Route::get('reviews',[ReviewController::class, 'index']);
AuthController.php
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
}
/**
* Get a JWT via given credentials.
*
* #return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return response()->json([
"user" => auth()->user(),
"access_token" => $token,
]);
}
/**
* Get the authenticated User.
*
* #return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json(auth()->user());
}
/**
* Log the user out (Invalidate the token).
*
* #return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* Refresh a token.
*
* #return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
}
ReviewController.php
class ReviewController extends Controller
{
public function __construct()
{
$this->middleware('auth:api');
}
public function index()
{
$reviews = Review::all();
return response()->json([
'status' => 200,
'reviews' => $reviews
]);
}
}
Should I remove the auth middleware and do it manually?

Related

Laravel jwt returns 500, on unauthorized

I use jwt for my api authentication. when I use wrong token it returns 500 and I get the error that rout login is not defiend!
I'm using laravel 8 and "tymon/jwt-auth": "^1.0". my default guard is api and api driver is jwt.
I tried name login route as login, bu then I get error that get method is not supported for this route but I'm using Post method!
rout : api.php:
Route::group(['prefix'=>'v1'],function (){
Route::group([
'middleware' => 'api',
'prefix' => 'auth'
], function ($router) {
Route::post('login', 'App\Http\Controllers\AuthController#login');
Route::post('logout', 'App\Http\Controllers\AuthController#logout');
Route::post('refresh', 'App\Http\Controllers\AuthController#refresh');
Route::post('me', 'App\Http\Controllers\AuthController#me');
});
});
AuthController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
}
/**
* Get a JWT via given credentials.
*
* #return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
*
* #return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json(auth()->user());
}
/**
* Log the user out (Invalidate the token).
*
* #return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* Refresh a token.
*
* #return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**
* Get the token array structure.
*
* #param string $token
*
* #return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}
user 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 Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
// Rest omitted for brevity
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* #return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
error that I get:
Symfony\Component\Routing\Exception\RouteNotFoundException: Route [login] not defined. in file C:\wamp64\www\medrep-app\vendor\laravel\framework\src\Illuminate\Routing\UrlGenerator.php on line 444
🧨 Route [login] not defined
Try to change your routes, by adding a route name, and Add a GET request for route.
Route::group(['prefix'=>'v1'],function (){
Route::group([
'middleware' => 'api',
'as' => 'api.',
'prefix' => 'auth'
], function ($router) {
Route::post('login', 'App\Http\Controllers\AuthController#login')->name('login');
Route::get('login', 'App\Http\Controllers\AuthController#sessionExpired')->name('sessionExpired');
Route::post('logout', 'App\Http\Controllers\AuthController#logout')->name('logout');
Route::post('refresh', 'App\Http\Controllers\AuthController#refresh')->name('refresh');
Route::post('me', 'App\Http\Controllers\AuthController#me')->name('me');
});
});
The login & sessionExpired function may have this code as exemple:
public function login()
{
dd('test token');
$credentials = request(['email', 'password']);
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
public function sessionExpired()
{
return response()->json(['error' => 'session expired'],401);
}
In Postman make sure you are using POST method:

How to test logout while using tymon/jwt-auth in Laravel?

I'm trying to make a logout test for my api with tymon/jwt-auth package. Here I have defined the api routes, controller, and a unit test.
In api.php:
Route::group(['middleware' => 'api', 'prefix' => 'auth'], function ($router) {
Route::post('login', 'AuthController#login');
Route::post('logout', 'AuthController#logout');
Route::post('refresh', 'AuthController#refresh');
Route::post('me', 'AuthController#me');
Route::post('me/profile', 'AuthController#profile');
});
In AuthController.php:
/**
* Log the user out (Invalidate the token).
*
* #return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
In tests/Unit/AuthenticationTest.php:
/**
* Test if user can login trough internal api.
*
* #return void
*/
public function testLogin()
{
$response = $this->post('api/auth/login', [
'email' => 'admin#xscriptconnect.com',
'password' => 'password'
]);
$response->assertStatus(200)
->assertJsonStructure(['access_token', 'token_type', 'expires_in']);
$this->assertAuthenticated('api');
}
/**
* Test if user can logout trough internal api.
*
* #return void
*/
public function testLogout()
{
$user = User::first();
$user = $this->actingAs($user, 'api');
$user->post('api/auth/logout')
->assertStatus(200)
->assertJsonStructure(['message']);
$this->assertUnauthenticatedAs($user, 'api');
}
The login test works fine but when it starts the logout test, the assertion fails. It shows me this error:
There was 1 failure:
1) Tests\Unit\AuthenticationTest::testLogout
Expected status code 200 but received 500.
Failed asserting that false is true.
And when I tested it using this method:
public function testLogout()
{
$user = User::first();
$this->actingAs($user, 'api');
$response = auth()->logout();
$response->assertStatus(200);
$response->assertJsonStructure(['message']);
}
I got this error:
There was 1 error:
1) Tests\Unit\AuthenticationTest::testLogout
Tymon\JWTAuth\Exceptions\JWTException: Token could not be parsed from the request
What is the proper way to test a logout trough this package? Please help.
According to the this comment in it's github page, I have found the solution for this problem. I changed my script like this and it works.
/**
* Test if user can logout trough internal api.
*
* #return void
*/
public function testLogout()
{
$user = User::first();
$token = \JWTAuth::fromUser($user);
$this->post('api/auth/logout?token=' . $token)
->assertStatus(200)
->assertJsonStructure(['message']);
$this->assertGuest('api');
}
Please feel free to post another answer regarding to this question if any. Thank you very much.
Override the method be() in your TestCase to set the authorization header when use actingAs() aka be() method
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
abstract class TestCase extends BaseTestCase
{
public function be(UserContract $user, $driver = null)
{
$token = auth()->fromUser($user);
return parent::be($user, $driver)->withHeader('Authorization', "Bearer {$token}");
}
}

How to generate JWT refresh token in Laravel 5.7

First off, let me admit that I'm new to APIs, and right now I'm working on JWT with Laravel. I'm using tymon\jwt-auth (tymon/jwt-auth:dev-develop --prefer-source to be specific). I went through some tutorials and was able to generate JWT access token.
Here is my login code:
public function login() {
$credentials = request(['email', 'password']);
if (!$token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return response()->json([
'status' => 'success',
'message' => 'Login successful',
'data' => [
'access_token' => $token,
],
]);
}
I also need to get refresh token along with the access token, and cannot find code that works in my case.
I tried adding these lines in the code:
$refresh_token = JWTAuth::refresh($token);
but the postman returns with this error:
A token is required in file
/var/www/brochill-api/vendor/tymon/jwt-auth/src/JWT.php on line 331
I can also provide other configuration snippets I used if needed. Please help!
Let's start with creating a /refresh route:
Route::post('refresh', 'AuthController#refresh');
Now, in the AuthController,
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
}
/**
* Get a JWT via given credentials.
*
* #return \Illuminate\Http\JsonResponse
*/
public function login()
{
//
}
/**
* Get the authenticated User.
*
* #return \Illuminate\Http\JsonResponse
*/
public function me()
{
//
}
/**
* Log the user out (Invalidate the token).
*
* #return \Illuminate\Http\JsonResponse
*/
public function logout()
{
//
}
/**
* Refresh a token.
*
* #return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**
* Get the token array structure.
*
* #param string $token
*
* #return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}
The refresh() function refreshes the access token and invalidates the current one.
For more info on these, you can checkout the official documentation of tymon/jwt-auth, which can be found here.

Laravel route Error: Route [login] is not definded

I'm developing an Laravel API, and finding a bit difficult to find the right answer for following problem. The problem is I'm having that when I query my group/join endpoint I'm getting the following error.
InvalidArgumentException: Route [login] not defined. in file\vendor\laravel\framework\src\Illuminate\Routing\UrlGenerator.php on line 372
When I query my GroupController#getAll I'm getting results from the database. So now I'm a bit flustered about what to try next.
My Group Controller code (only the requests that are implemented)
/**
* #param CreateGroupRequest $request
*/
public function createGroup(CreateGroupRequest $request){
$data = $request->get('group', []);
}
/*
* #param JoinGroupUserRequest $request
*/
public function joinGroup(JoinGroupUserRequest $request){
$group_id = $request->group_id;
$user_id = $request->user_id;
$data = $this->groupService->joinGroup($group_id, $user_id);
return response()->json([], 201);
}
My Group repository
class GroupRepository{
/*
Get a new instance of empty Group Model
*/
public function GetModel(){
return new Group();
}
/**
* #param int $private
* #return mixed
*/
public function GetAllGroups($private = 0){
$groups = Group::where('group_private', $private)->get();
return $groups;
}
/**
* #param array $data
* #return Group
*/
public function CreateGroup(array $data){
$group = $this->GetModel();
$group->fill($data);
$group->save();
return $group;
}
public function AddUserToGroup(int $group_id, int $user_id){
$group_user = new GroupUsers();
$group_user->group_id = $group_id;
$group_user->user_id = $user_id;
$group_user->save();
return $group_user;
}
}
My GroupService
class GroupService
{
private $groupRepository;
/**
* GroupService constructor.
* #param php $groupRepository
*/
public function __construct(GroupRepository $groupRepository){
$this->groupRepository = $groupRepository;
}
/*
* Gets all Groups
*/
public function getAll()
{
return $this->groupRepository->GetAllGroups();
}
public function joinGroup($group_id, $user_id){
return $this->groupRepository->AddUserToGroup($group_id, $user_id);
}
}
My api routes
Route::group(['middleware' => 'web', 'prefix' => 'auth'], function () {
Route::post('login', 'AuthController#login');
Route::post('signup', 'AuthController#signup');
Route::get('social/{provider}', 'AuthController#signupSocial');
Route::get('callback/{service}', 'AuthController#callback');
Route::group(['middleware' => 'auth:api'], function() {
Route::get('logout', 'AuthController#logout');
Route::get('user', 'AuthController#user');
});
});
Route::group(['middleware' => 'auth:api', 'prefix' => 'group'], function () {
Route::get('/', 'GroupController#getAll');
Route::post('join', 'GroupController#joinGroup');
});
Postman request
POST /api/group/join HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjBhNThiOWY4NTBmZTNmNDZmMTQzYmM2NmY1NzVkZThkMTRiOGFhZjZjMWU5ZThiMjJjNDU3N2QzNmYxYTgyM2JkMjJiMTMzNzBhODcyODAxIn0.eyJhdWQiOiIzIiwianRpIjoiMGE1OGI5Zjg1MGZlM2Y0NmYxNDNiYzY2ZjU3NWRlOGQxNGI4YWFmNmMxZTllOGIyMmM0NTc3ZDM2ZjFhODIzYmQyMmIxMzM3MGE4NzI4MDEiLCJpYXQiOjE1MzM2NjY3NjIsIm5iZiI6MTUzMzY2Njc2MiwiZXhwIjoxNTY1MjAyNzYyLCJzdWIiOiI2Iiwic2NvcGVzIjpbXX0.s96tp1nxYJoXV8j1JNsmPKz0yw0qF1G13v2581HU6uVt5WJkOdXF4ysOQdccIaBDO05CPwqtzjtjgGDV41EuCWgXeT0qYJwPtZzx6OhYmeZiSlsYvC69ttxWRMFIefpX1tEZH0CaFVTV0ZaMpuwBdY7ElDxjM_XWuApFIyouqvNudKrMT0DztY1HrzOeqzzLBZgJbsrrTEnndq37TpXaFBjMfy0GCEt1RFNuGEkws1cQo4SBVt4Zbqdevmyo6kJ2rFMjOn6YdDVg-eYE08X1Qn-51fuHabgKy33_UnwvBATNpF0DgzjmaD7s9C0u8B1T9VIEdRnL6Fr9nVDaIV9aTcSozA-xdLQ7CLNgGLxkilw5Pm4tjo75-UcD-xMdvJ4APWMzk1R4VHa11JjPUzs_4aVLegwE3apExYxjMXO4wC0pyxUoY-1QvVloUEbckx2iJI91P16aKgvKl8IgxjZZdeYVjLwc6IRAHtF4Rv0PXSD6t_1IDSZydiu7s_mT0p3rRxF59bNC23O1QOtdKsYB6Bk1T9mdxG5ndTX_v2HqPZyhjuZQzmOJUH6GotkRPvcWldN-g0kKwA4dF2cYVA7el4RXge_bAAUbbas3l0pWuMNBJEfW78Kh7mmG9oJjj5Qipqzd7clWRhtkUyOikHPvIrJyLdNVdFNyfOeHesjWeaU
Cache-Control: no-cache
Postman-Token: c4b9178e-f64c-4146-ab7f-7453961e11f7
{
"group_id": "1",
"user_id": "1"
}
Now what I have tried is
Cleared my route cache
Named my login route which gave a MethodNotAllowedException
Can someone push me in the right direction ?
The solution was to add the Accept header with application/json

Laravel 5 middleware auth always fails and redirects to login

I am authenticating my user and redirecting him to dashboard if credentials are correct. I want to secure the dashboard route and added middleware auth, but now it always redirects to login page.
Routes.php
Route::get('login', array('uses' => 'HomeController#showLogin'));
Route::post('login', array('uses' => 'HomeController#doLogin'));
Route::get('logout', array('uses' => 'HomeController#doLogout'));
Route::group(['middleware' => 'auth'], function() {
Route::get('/', function () {
return view('dashboard');
});
Route::get('dashboard', function () {
return view('dashboard');
});
});
HomeController.php
public function showLogin(){
return View::make('login');
}
public function doLogin(Request $request){
$rules = array(
'email' => 'required|email',
'password' => 'required|alphaNum|min:3'
);
$validator = Validator::make($request::all(), $rules);
if ($validator->fails()) {
return Redirect::to('login')
->withErrors($validator)
->withRequest($request::except('password'));
}
else {
$userdata = array(
'email' => $request::get('email'),
'password' => $request::get('password')
/*'password' => Hash::make($request::get('password'))*/
);
if (Auth::attempt($userdata)) {
$userid = Auth::id();
return redirect()->intended('/');
} else {
return Redirect::to('login');
}
}
}
public function doLogout()
{
Auth::logout();
return Redirect::to('login');
}
Middleware Authenticate.php
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
Middleware RedirectIfAuthenticated.php
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
I am not sure why your code is not working, but you can try replace:
if (Auth::attempt($userdata)) {
$userid = Auth::id();
return redirect()->intended('/');
}
with:
if (Auth::attempt($userdata)) {
$userid = Auth::id();
return redirect('dashboard');
}
From the API Docs of intended method:
Create a new redirect response to the previously intended location.
is giving some error to you as it is going back to the previous location and not to the next location.
UPDATE 1:
I would have gone with the following approach.
Make your own middleware called UserAlreadyLoggedIn
php artisan make:middleware UserAlreadyLoggedIn
Open UserAlreadyLoggedIn.php and update handle method with the below code:
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(auth()->check()) {
return $next($request);
}
return redirect('login');
}
Register it in app/Http/Kernel.php file inside $routeMiddleware array:
$routeMiddleware = [
'user_already_logged_in' => \App\Http\Middleware\UserAlreadyLoggedIn::class,
];
Separate the already logged in user in controller by making UserSessionsController
php artisan make:controller UserSessionsController --plain
Inside UserSessionsController place the __constructor method:
/**
* Check if the user is already logged in.
*
* #return void
*/
public function __construct()
{
$this->middleware('user_already_logged_in');
}
routes.php
Route::get('login', 'HomeController#showLogin');
Route::post('login', 'HomeController#doLogin');
Route::get('logout', 'HomeController#doLogout');
// Replace the dashboard code inside the dashboard method..
Route::get('dashboard', 'UserSessionsController#dashboard');
Again I would have created a middleware called UserIsAGuest and would have replaced the if block inside the handle method:
if(auth()->guest()) {
return $next($request);
}
return redirect('dashboard');
And then inside the HomeController's __construct method:
/**
* Check if the user is already logged in.
*
* #return void
*/
public function __construct()
{
// Register the middleware in Kernel.php file
$this->middleware('user_is_guest');
}
Hope this helps you out. Happy Coding. Cheers.
Sorry for being late to the party, but after a long search for me, I had to change my middleware from ['middleware' => 'auth] to ['middleware' => 'auth:web']
I hope this helps out

Resources