I have a login route that makes sure that a user is verified before logging in like so.
Route::post('/login', [AuthController::class, 'login'])->middleware('userIsVerified');
However in the case of the user entering a wrong password, it passes through the middleware first and then returns that the user is not verified in case of the user is not verified.
How to tweak this middleware to make it run after the request is processed by the controller?
Here is the middleware code:
class UserIsVerified
{
public function handle(Request $request, Closure $next)
{
if (auth()->user()) {
$userIsVerified = auth()->user()->verified;
} else {
$user = Client::where('phone', $request->phone)->first();
if (!$user)
return response(['message' => 'User doesnt exist.'], Response::HTTP_NOT_FOUND);
$userIsVerified = $user->verified;
}
if (!! $userIsVerified)
return $next($request);
else
return response(['message' => 'User is not verified.'], Response::HTTP_UNAUTHORIZED);
}
}
Laravel middleware has one more method callled terminate(), this method apply after the response. Its called terminable middleware.
Read More About terminate
I have an API task where I should authorize before deleting data (BookReview),
Access to this endpoint requires authentication with an API token and admin privileges.
in User table I have api_token and is_admin field:
[![User Table][1]][1]
this is my BookReviewController:
public function destroy(int $bookId, int $reviewId, Request $request)
{
// #TODO implement
$check_bookReview = BookReview::firstWhere('id', $reviewId);
if ($check_bookReview) {
BookReview::destroy($reviewId);
return response()->noContent();
} else {
abort(404);
}
}
i don't know what to add in my controller, and further more I need to authorize it from postman
[![Postman][2]][2]
the delete function works fine, but there is no authorization yet
[1]: https://i.stack.imgur.com/ywvC8.png
[2]: https://i.stack.imgur.com/fyhpG.png
If you have is_admin in your DB so you can check user is admin or not.
public function destroy(int $bookId, int $reviewId, Request $request)
{
// #TODO implement
$check_bookReview = BookReview::firstWhere('id', $reviewId);
if ($check_bookReview && auth()->user()->is_admin == 1) {
BookReview::destroy($reviewId);
return response()->noContent();
} else {
abort(404);
}
}
or in your controller:
use Auth;
if (Auth::user() && Auth::user()->is_admin == 1) {
//destroy
}
and if you want use middleware then:
in routes/web.php add:
Route::group(['middleware' => ['auth', 'admin']], function () {
Route::post('/destroy-something', 'YourController#destroy')->name('destroy.review');
});
in app/Http/Middleware create a middleware for admin.
then add it to app/Http/Kernel.php to the protected $routeMiddleware array.
You should use some middleware. The middleware is the authetication step that you are missing. In the middleware, you should make the validation like find the user by de token that he sent to you, see if he is an admin an then send the request to the controller (if he is an admin) or return an 401 status code if he is not authorized to do this.
To help you with middleware is nobody better then the documentation
I have a field (can_login) in my users table.
normally,users can login. and logout with this code:
Auth::logout();
I want, when each time a logged-in user refresh a page,
laravel check can_login in user table.
if can_login is false, then auto logout.
I try this in RouteServiceProvider
public function boot()
{
parent::boot();
if(auth()->user()->can_login){
return route('logout');
}
}
but auth()->user() is always empty.
You can use a middleware or add a check to your existing authentication middleware.
On each request it passes through a middleware to check if user is authenticated and after that auth()->user() will not be empty.
Simplest solution would be to create a middleware in app/Http/Middlewares/CheckUserCanLoginMiddleware.php
class CheckUserCanLoginMiddleware
{
public function handle(Request $request, Closure $next)
) {
if ($request->user()->can_login ?? false) {
Auth::logout();
return $this->sendUnauthorizedResponse();
}
return $next($request);
}
}
And then register it as a routeMiddleware` in your bootstrap/app.php file.
The last thing you need is to use it to our routes middlewares after authentication middleware.
I created a custom middleware for checking if request is submitted by user who owns the resource or owned by admin.
Route::middleware(['web', 'selforadmin'])->group(function () {
Route::post('users/update-account/{id}', 'UsersController#UpdateAccount');
Route::post('users/update-email/{id}', 'UsersController#UpdateEmail');
Route::post('users/update-password/{id}', 'UsersController#UpdatePassword');
});
and then the middleware handler:
public function handle($request, Closure $next)
{
print_r($request->all());
print_r($request->user());
dd();
return $next($request);
}
But I don't know why user model is not accessible here. I read that request needs to pass from web middleware first so I did but still can't access this middleware.
It gives null on $request->user() or Auth::user()
I am using Laravel 5.4
EDIT:
Middleware is being called as I see other inputs. Only Auth is empty. And User is logged.
You can't access using models directly in middleware. You need to define a terminate method in your middleware to perform some processing after response has been sent to browser.
public function terminate($request, $response)
{
// Your code ...
}
If user is not logged in it will always return null.
There are two solution for this.
First is, check if $request->user() is not null.
public function handle($request, Closure $next)
{
if($request->user())
{
// do your stuff
}
else
{
// do otherwise
}
return $next($request);
}
Second is, add auth middleware before your middleware to assure that only logged in users are allowed.
Route::middleware(['web', 'auth', 'selforadmin'])->group(function () {
// .....
}
I'm currently using 2 projects. 1 front end (with laravel backend to communicate with API) and another laravel project (the API).
Now I use Laravel Passport to authenticate users and to make sure every API call is an authorized call.
Now when I want to log out my user, I send a post request to my API (with Bearer token) and try to log him out of the API (and clear session, cookies,...)
Then on the client I also refresh my session so the token is no longer known. Now when I go back to the login page, it automatically logs in my user. (Or my user is just still logged in).
Can someone explain me how to properly log out a user with Laravel passport?
Make sure that in User model, you have this imported
use Laravel\Passport\HasApiTokens;
and you're using the trait HasApiTokens in the User model class using
use HasApiTokens
inside the user class.
Now you create the log out route and in the controller,
do this
$user = Auth::user()->token();
$user->revoke();
return 'logged out'; // modify as per your need
This will log the user out from the current device where he requested to log out. If you want to log out from all the devices where he's logged in. Then do this instead
$tokens = $user->tokens->pluck('id');
Token::whereIn('id', $tokens)
->update(['revoked'=> true]);
RefreshToken::whereIn('access_token_id', $tokens)->update(['revoked' => true]);
Make sure to import these two at the top
use Laravel\Passport\RefreshToken;
use Laravel\Passport\Token;
This will revoke all the access and refresh tokens issued to that user. This will log the user out from everywhere. This really comes into help when the user changes his password using reset password or forget password option and you have to log the user out from everywhere.
You need to delete the token from the database table oauth_access_tokens
you can do that by creating a new model like OauthAccessToken
Run the command php artisan make:model OauthAccessToken to create the model.
Then create a relation between the User model and the new created OauthAccessToken Model , in User.php add :
public function AauthAcessToken(){
return $this->hasMany('\App\OauthAccessToken');
}
in UserController.php , create a new function for logout:
public function logoutApi()
{
if (Auth::check()) {
Auth::user()->AauthAcessToken()->delete();
}
}
In api.php router , create new route :
Route::post('logout','UserController#logoutApi');
Now you can logout by calling posting to URL /api/logout
This is sample code i'm used for log out
public function logout(Request $request)
{
$request->user()->token()->revoke();
return response()->json([
'message' => 'Successfully logged out'
]);
}
Create a route for logout:
$router->group(['middleware' => 'auth:api'], function () use ($router) {
Route::get('me/logout', 'UserController#logout');
});
Create a logout function in userController ( or as mentioned in your route)
public function logout() {
$accessToken = Auth::user()->token();
DB::table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update([
'revoked' => true
]);
$accessToken->revoke();
return response()->json(null, 204);
}
I am using Laravel 6.12.0, below function is working for me.
public function logout(Request $request){
$accessToken = Auth::user()->token();
$token= $request->user()->tokens->find($accessToken);
$token->revoke();
$response=array();
$response['status']=1;
$response['statuscode']=200;
$response['msg']="Successfully logout";
return response()->json($response)->header('Content-Type', 'application/json');
}
This is my first post.. and i find a clean solution (Laravel last Version)
/**
* Logout api
*
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
if (Auth::check()) {
$token = Auth::user()->token();
$token->revoke();
return $this->sendResponse(null, 'User is logout');
}
else{
return $this->sendError('Unauthorised.', ['error'=>'Unauthorised'] , Response::HTTP_UNAUTHORIZED);
}
}
Below is the simplest way I found to do it.
1. USE database SESSION INSTEAD OF file SESSION
Official documention
php artisan session:table
php artisan migrate
Replace SESSION_DRIVER=file by SESSION_DRIVER=database in your .env file.
2. DELETE USER SESSION RIGHT AFTER LOGIN
After a user is redirected to your frontend and logs in to finally get a token, you probably call a route in api/routes.php to get the user information, that's where I'm closing the user backend session before sending back user information to the frontend:
Route::middleware('auth:api')->get('/user', function (Request $request) {
// Close user session here
Illuminate\Support\Facades\DB::table('sessions')
->whereUserId($request->user()->id)
->delete();
return $request->user();
});
3. REVOKE TOKENS AT LOGOUT
Then, to "log out" (actually, revoke tokens) the user from the frontend, you just need to call another route to revoke the token and refresh_token:
Route::middleware('auth:api')->post('/logout', function (Request $request) {
// Revoke access token
// => Set oauth_access_tokens.revoked to TRUE (t)
$request->user()->token()->revoke();
// Revoke all of the token's refresh tokens
// => Set oauth_refresh_tokens.revoked to TRUE (t)
$refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($request->user()->token()->id);
return;
});
You may prefer to put these two closures in the UserController.
Hope help someone:
if (Auth::check()) {
$request->user()->tokens->each(function ($token, $key) {
$token->delete();
});
}
Good Luck.
I use this in my project to logout from multiple device.
public function logout(Request $request, $devices = FALSE)
{
$this->logoutMultiple(\Auth::user(), $devices);
return response()->json([], 204);
}
private function logoutMultiple(\App\Models\User $user, $devices = FALSE)
{
$accessTokens = $user->tokens();
if ($devices == 'all') {
} else if ($devices == 'other') {
$accessTokens->where('id', '!=', $user->token()->id);
} else {
$accessTokens->where('id', '=', $user->token()->id);
}
$accessTokens = $accessTokens->get();
foreach ($accessTokens as $accessToken) {
$refreshToken = \DB::table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update(['revoked' => TRUE]);
$accessToken->revoke();
}
}
Try this code to help you to logout from passport authentication.
Route::post('/logout', function(){
if (Auth::check()) {
Auth::user()->AauthAcessToken()->delete();
}
return response()->json([
'status' => 1,
'message' => 'User Logout',
], 200);
});
check whether your model contains OauthAccessToken which needs to connect with the database oauth_access_tokens. The access token is stored in the database table oauth_access_tokens. and makes a relation from users to oauth_access_tokens.
public function AauthAcessToken(){
return $this->hasMany(OauthAccessToken::class);
}
You can use following code to remove to token for logged in user.
$request->user()->token()->revoke();
If you want to learn about this in-depth then watch this tutorial:
https://www.youtube.com/watch?v=UKSQdg1uPbQ
public function logout(Request $request)
{
$request->user()->token()->revoke();
if ($request->everywhere) {
foreach ($request->user()->tokens()->whereRevoked(0)->get() as $token) {
$token->revoke();
}
}
return response()->json(['message' => 'success']);
}