Laravel Guest Middleware and user authentication - laravel

I just created a simple login with guest middleware that allows the user to access one account at a time, but I am just worried if this is the right way to do it.
/** Routes **/
Route::group(['middleware' => 'guest'], function () {
Route::get('/', 'LoginController#index')->name('login');
Route::post('/', 'LoginController#post')->name('login.post');
});
/** login.post controller **/
public function post(Request $request){
$this->rules($request);
$rules = array(
'username' => $request->username,
'password' => $request->password,
);
if(Auth::attempt($rules)) {
if(Auth::user()->is_active == true){
/** IF THE USER IS CURRECTLY LOGIN **/
if(Auth::user()->is_login == true){
Auth::logout();
Session::flash('multilog', 'Your account is log-in to another device!!');
return redirect('/')->withInput();
}
$user = user::find(Auth::user()->id);
$user->is_login = true;
$user->save();
return redirect('admin/home');
}
Session::flash('unactivated', 'Your account is not activated!!');
return redirect('/')->withInput();
}
Session::flash('unmatch', 'Invalid username or password!!');
return redirect('/')->withInput();
}
/** **/

If you are not sure, you can use Laravel to create authentication. Write in command line:
php artisan make:auth
Then just look how the logic works in files.
More you can read here:
https://laravel.com/docs/5.5/authentication

Related

Laravel Sanctum SPA Logout Testing

I logout of my application via:
Auth::guard('web')->logout();
Please note, it's the SPA use of Sanctum so no tokens.
Then in my test, I check if the user is logged out via:
Sanctum::actingAs(User::first(), ['*']);
$response = $this->postJson(
route('logout')
); // runs auth logout
$this->assertGuest();
The above fails, yet, when I do not use Sanctum::actingAs(User::first(), ['*']); and instead do a call to my login:
$response = $this->postJson(
route('login'),
[
'email' => User::first()->email,
'password' => 'xyz',
]
);
The test passes.
Am I missing something with Sanctum::actingAs(User::first(), ['*']);, the docs say to use this for testing, does this not work when it's an SPA with cookie based authentication?
Change your logout middleware to web:
Route::middleware('auth:web')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
});
Then, in logout():
class AuthController extends Controller
{
/**
* #return JsonResponse
*/
public function logout(): JsonResponse
{
// If with tokens - delete them:
// Auth::user()->tokens()->delete();
Auth::guard('web')->logout();
return response()->json([
'message' => 'Tokens Revoked'
]);
}
}

How to logout a user from API using laravel Passport

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']);
}

How to check user status while login in Laravel 5?

I have used Laravel Authentication (Quickstart). But I need to check the status of the user (approved/pending). If not approved, then an error will be shown in the login page. I need to know in which file I have to make the change and what is the change. Currently I am working on Laravel 5.3.
You can create a Laravel Middleware check the link for additional info
php artisan make:middleware CheckStatus
modify your middleware to get
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class CheckStatus
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
//If the status is not approved redirect to login
if(Auth::check() && Auth::user()->status_field != 'approved'){
Auth::logout();
return redirect('/login')->with('erro_login', 'Your error text');
}
return $response;
}
}
then add your middleware to your Kernel.php
'checkstatus' => \App\Http\Middleware\CheckStatus::class,
and finally add the middleware to your route
Route::post('/login', [
'uses' => 'Auth\AuthController#login',
'middleware' => 'checkstatus',
]);
I hope it helps
I found a simple solution for this. Artisan create App\Http\Controllers\Auth\LoginController, in this default controller just add this code if you have some conditions to login, for example I have a field state, you posibbly have status, email_status or other.
// Custom code for Auth process
protected function credentials( Request $request )
{
$credentials = $request->only($this->username(), 'password');
$credentials['state'] = 1;
return $credentials;
}
upper answer saves me
if (Auth::attempt(['email'=>$input['email'],'password'=>$input['password'], 'user_status'=>1 ]))
this will check the status
Just Add following method in my LoginController works like charm
protected function authenticated(Request $request, $user)
{
if ($user->yourFirldName != "Active") {
Auth::logout();
return redirect('/login')->with('error', 'Looks Like Your status is InActive');
}
}
I don't agree with upper answer, which will lead to your application performance is very low, and also don't recommend to modify the Laravel's source code.
So you can rewrite getCredentials function to your app\Http\Controllers\Auth\AuthController.php file like this:
<?php
//app\Http\Controllers\Auth\AuthController.php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
//you need add this function
protected function getCredentials(Request $request)
{
$data = $request->only($this->loginUsername(), 'password');
$data['is_approved'] = 1;
return $data;
}
}
then you can use Laravel Authentication (Quickstart) directly.
Hope this will help.
The pinned answer is the best approach.
Just a note: if you are using Laravel 5.8+ you need use:
//Default Auth routes
Auth::routes();
//Override and add middleware
Route::post('/login', [
'uses' => 'Auth\LoginController#login',
'middleware' => 'checkstatus',
]);
Follow the steps...
First add a column in your user table (suppose is_approved)
In App/Http/Controllers/Auth/LoginController file
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password, 'is_approved'=>1])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
Hope this will help
Auth/LoginController
Though it is a long time from the question created date. You can go this way.
Go to Auth/LoginController and add this line.
protected function credentials(Request $request)
{
return [
'email' => $request->email,
'password' => $request->password,
'status' => 1,
];
}
For this to work you have to have a column named 'status' in users table. 1 is for active and 0/2 is for inactive user.
Hope this will work for you.
public function login(Request $request){
if ($request->isMethod('post')) {
$data= $request->all();
$roles=[
'email' => 'required|email|max:255',
'password' => 'required',
];
$customessage=[
'email.required' =>'Email is required',
'email.email' => 'Email is not vaild',
'password.required' => 'Password is required',
];
$this->validate($request,$roles,$customessage);
if(Auth::guard('admin')->attempt(['email'=>$data['email'],'password'=>$data['password'],'status'=>1])) {
return redirect('admin/dashboard');
} else {
Session::flash('error_message','You are not Active by Admin');
return redirect()->back();
}
}
return view('admin.admin_login');
}

Laravel 5.2 - Auth::login not preserve when using new \App\User

I've used these code to authenticate username from external source without using database, but laravel 5.2 not save the authentication, and request the external source every time.
class Authenticate
{
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
$username = getFromExternalSource();
if($username==null){ redirect()->guest('auth/login'); }
$user = new \App\User(['username'=>'admin']);
Auth::login($user);
}
}
return $next($request);
}
}
But when I change Auth::login using $user model get from database it work, I don't know why:
$userDB = \App\User::where('username','=','admin')->first();
Auth::login($userDB);
My route (for example I want to access http://myApp/api)
Route::group(['middleware' => ['web']], function () {
Route::group(['middleware' => 'auth'], function () {
Route::get('api/', 'ApiController#index');
});
Route::group(['middleware' => ['csrf']], function () {
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController'
]);
});
});
As far as I know for default authenticate method, you need to pass model that exists in database. In your example you created just object and assigned to it username property.
If you need to authenticate selected user, you should use:
Auth::loginUsingId($id);
(where in place of $id you should pass id of user you want to authenticate - in your case id of user with username admin)

Laravel 5 Auth - Change Login Route

I'm trying to redirect my user to 'homepage' after successful login.
I've been able to redirect after logout by adding the following to AuthController:
protected $redirectAfterLogout = 'homepage';
However, adding the following to AuthController does not work after login. It directs me to 'home."
protected $redirectPath = 'homepage';
I then changed the default redirect in the handle() function in RedirectIfAuthenticated to:
return redirect('homepage');
Not only does that not work, it gives me the following error:
This webpage has a redirect loop
ERR_TOO_MANY_REDIRECTS
Does anyone know how I can achieve redirecting to 'homepage' after login?
Edit - Adding Routes:
Route::get('/', function () {
return view('welcome');
});
Route::get('home', 'HomepageController#getIndex');
Route::get('homepage', 'HomepageController#getIndex');
Thanks for any guidance!
Dude the attribute should be named as $redirectTo:
$redirectTo = "homepage";
now if you got more than one rule like admin and user, stored within your user model as type field, you may override the value of this attribute within postLogin() function, override the function first then do your changes. i.e:
// AuthController.php
/**
* #param Request $request
* #return $this|\Illuminate\Http\RedirectResponse
*/
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
$user=User::find($this->auth->user()->id);
if($user->type == "ADMIN")
$this->redirectTo = "/dashboard";
return redirect()->intended($this->redirectPath());
}
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => $this->getFailedLoginMessage(),
]);
}
I got around this by simply modifying the existing Route::get('/') to the following:
Route::get('/', 'HomepageController#getIndex');
I really didn't need the Welcome View any longer, so this solution made the most sense.
Also, after modifying this Route, I was able to remove my previous 'home' and 'homepage' Routes.

Resources