disable login from inactive user using Laravel Breeze - laravel

I want to disable inactive user from login, and logout the user if already logged in,
using breeze pack:
breeze pack
i"m having a boolean column: "active"
in users table.
i understand that i should edit
app\Http\Requests\Auth\LoginRequest.php
i understand that i should edit the current code:
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
1.What and how should i edit that (or other) code ?
2.Is it possible to show a message "only active users allowed to login" to inactive user, without make it complecated ?

For this I would turn to registering global middleware that can be checked on each route. You can generate middleware via the php artisan make:middleware YourMiddlewareName command.
Within the generated middleware file:
public function handle(Request $request, Closure $next)
{
if (Auth::check()) {
$user = Auth::user();
if ($user->active === 0) {
//Log them out
//Redirect them somewhere with a message
}
}
return $next($request);
}
In this code we can see that we first check that there is a user logged in, then checking if that logged in user is active. From there you can do whatever steps you need to take.
The problem with performing this action within the login request is that the user would not be affected until they log in again. Registering global middleware will make this fire on every request they make.

To disable inactive users from logging in, you need to add a condition in the code to check the active status of the user, and to show a message to inactive users, you can add another condition and throw a custom exception with a message.
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
$credentials = $this->only('email', 'password');
$user = User::where('email', $credentials['email'])->first();
if (!$user) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
if (!$user->active) {
throw ValidationException::withMessages([
'email' => 'Only active users are allowed to login',
]);
}
if (! Auth::attempt($credentials, $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}

Related

Verify email address without being logged in Laravel 8 (Sanctum)

I want to allow users to verify email addresses without being logged in.
When the user registers, he gets a verification link. If he clicks on it he gets verified and automatically logged in because he gets logged in at registration. But if a user uses a different device to verify, lets say smartphone, he's unable to directly verify email because he's not logged in. How can I change my controller to be able to do that?
this is my controller:
use Illuminate\Foundation\Auth\EmailVerificationRequest;
public function verify(EmailVerificationRequest $request)
{
if ($request->user()->hasVerifiedEmail()) {
return [
'message' => 'Email already verified'
];
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return [
'message' => 'Email has been verified'
//then front-end redirects user to login page
];
}
and this is API:
Route::get('verify-email/{id}/{hash}', [EmailVerificationController::class, 'verify'])->name('verification.verify')->middleware('auth:sanctum');
If I remove middleware('auth:sanctum'); I get Call to a member function getKey() on null.
Thank you!
public function verify(Request $request)
{
$user = User::findOrFail($request->id);
if ($user->email_verified_at) {
return '';
}
if ($user->markEmailAsVerified()) {
event(new Verified($user));
}
return redirect()->away('app://open'); // The deep link
}

redirect to different views in laravel logged in users based on role mentioned by using if condition in laravel controller

In laravel i have student,parent ,employee(Teacher,Librarian,warden) roles with different permissions...based on user role it should redirect to different blade files when user logged in..my problem is if user parent or student it is redirecting to different dashboards but whenever user is teacher or other it does not logged in but in users table already user exist.
below is my LoginController code
LoginController.php:
public function login(Request $request){
if(Auth::attempt([
'email'=>$request->email,
'username'=>$request->username,
'password'=>$request->password,
]))
{
$user=User::where('username',$request->username)->first();
$usertype=$user->user_type;
$username=$user->username;
$roles=DB::table('roles')->where('id','=',$usertype)->first();
$rolename=$roles->role_name;
if($rolename=="student"){
$student=DB::table('students')->where('stud_adm_id','=',$username)->first();
$classid=$student->class_id;
$sectionid=$student->section_id;
$class=DB::table('classtables')->where('id',$classid)->first();
$section=DB::table('sections')->where('id',$sectionid)->first();
return view('studentdashboard',compact('student','class','section'));
}elseif($rolename=="Teacher"){
$employeedetails=DB::table('employees')->where('employee_fname','=',$username)->first();
return view('teacherdashboard',compact('employeedetails'));
}
elseif($rolename=="parent"){
$parentdetails=DB::table('parents')->where('mother_phone','=',$username)->first();
$stateid=$parentdetails->state;
$state=DB::table('states')->where('state_id','=',$stateid)->first();
return view('parentdashboard',compact('parentdetails','state'));
}
}else{
return redirect()->back();
}
}
my roles are mentioned in role table and that id stored in users table
Thanks in advance...
You better create a middleware to check user role, and based on the role redirect user to different pages!
Run the command below to create a middleware that checks user's role.
php artisan make:middleware CheckRoleMiddleware
The command will create a file under App\Http\Middleware named CheckRoleMiddleware this class will come a predefined method handle() there you can place the logic that checks user's role and redirects them to different pages example:
<?php namespace App\Http\Middleware;
use Closure;
class CheckRoleMiddleware {
public function handle($request, Closure $next)
{
//User role is admin
if ( Auth::check() && Auth::user()->isAdmin() )
{
return $next($request);
}
//If user role is student
if(Auth::check() && auth()->user()->role === 'student')
{
return view('studentDashboard');
or route('routeName');
}
//If user role is teacher
if(Auth::check() && auth()->user()->role ==='teacher')
{
return view('teacherDashboard');
or route('routeName');
}
//default redirect
return redirect('home');
}
}
And don't forget to add CheckRoleMiddleware to App\Http\Kernel.php
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'user-role' => 'App\Http\Middleware\CheckRoleMiddleware', // this line right here
];
Lets just ignore everything about how this stuff should work and how it does by default and take what you have.
Do you see a problem here:
if ($rolename == "student")
elseif ($rolename == "Teacher")
elseif ($rolename == "parent")
If not, lets try it this way:
student
Teacher
parent
Which one is not like the others? And which one was the one that "was not working" correctly?
I would like to assume you have these roles named in a consistent fashion, so Teacher should be teacher.
You can do all of what you want to do with the default LoginController by overriding a method or two.
You should not be returning views from processing routes. POST REDIRECT GET. POST comes in, return a REDIRECT, that causes a GET request (which most likely is going to return a view).
For validating the login credentials:
protected function validateLogin(Request $request)
{
$this->validate($request, [
'email' => '...',
'username' => '...',
'password' => '...',
]);
}
For what credentials should be used from the request:
protected function credentials(Request $request)
{
return $request->only('email', 'username', 'password');
}
The response to return after successful authentication:
protected function authenticated(Request $request, $user)
{
// do your role checking here
// return a redirect response to where you want that particular user type to go
// return redirect()->route(...);
}
Everything related to building the views and gathering that data is its own route. All you have to do is return a redirect for the login flow. Where you redirect to is responsible for gathering the data needed to display a view.

Override login Authentication in laravel

my laravel version is 5.3. I'm using built-in Authentication in laravel to login users.
there is a column name in user table as status. when it is 0 it means the user cannot login.
now I don't know how to check this column before login method/user.
I don't want user can login when the status column is 0.
You can override authenticated() function:
protected function authenticated()
{
if (auth()->user()->status==0) {
auth()->logout();
return redirect('/');
}
}
You can also Manually authenticate users by overriding authenticate() function:
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password, 'status' => 1]))
{
// Authentication passed...
}
}

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

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)

Resources