I had a functioning user log in route in my controller however the method itself was very long and handled multiple issues such as the possibility of the user being banned, ip logging etc. I wanted to break the method down so that it was more managable and I was wondering if this is possible?
Here are the 3 methods where 'postLogin()' is the route:
public function postLogin()
{
$validator = Validator::make(Input::all(), array(
'login-username' => 'required',
'login-password' => 'required'
));
if($validator->fails())
return Redirect::back()->withErrors($validator)->withInput();
else
{
$user_attempt = Input::get('login-username');
$pass_attempt = Input::get('login-password');
$auth = Auth::attempt(array(
'username' => $user_attempt,
'password' => $pass_attempt
), true);
if($auth)
$this->handleAuth();
else
return Redirect::route('home')->with('fail', 'Incorrect username or password, username: ' . $user_attempt . ' pass: ' . $pass_attempt);
}
}
private function handleAuth()
{
$username = Auth::user()->username;
$banned_info = UserBans::getBanned($username);
$stored_ip = Auth::user()->ip_address;
$current_ip = User::getIp();
if($stored_ip != $current_ip)
User::updateIp(Auth::user(), $current_ip);
if(is_null($banned_info))
return Redirect::intended('/');
else
$this->handleBan($banned_info, $current_ip);
}
private function handleBan($banned_info, $current_ip)
{
if(UserBans::isBanned($banned_info['ban_end']))
{
$message = "This account is currently banned until: " . $banned_info['ban_end'] . " Reason: " . $banned_info['reason'];
if($banned_info['ip_address'] != $current_ip)
{
$banned_model = UserBans::getBannedModel($banned_info['id']);
User::updateIP($banned_model, $current_ip);
}
Auth::logout();
return Redirect::route('home')->with('fail', $message);
}
else
{
UserBans::destroy($banned_info['id']);
return Redirect::intended('/');
}
}
The issue I'm finding is that the the main controller method will call the helper methods with no problem however the helper methods attempt to redirect to routes for example in handleAuth():
if(is_null($banned_info))
return Redirect::intended('/');
This occurs if the user is not banned and has the correct credentials, normally it would redirect to the home page and you would be logged in, however when this method calls the intended I am left with a blank page at the 'postLogin' routes url. If you refresh the page you are at the home and are logged in. Here are the relevant routes:
Route::group(array('before' => 'guest'), function()
{
Route::group(array('before' => 'csrf'), function()
{
Route::post('/user/login', array('uses' => 'UserController#postLogin', 'as' => 'postLogin'));
});
});
Is this possible to do with laravel routing/controllers? if not can you give any recommendations on how to handle this situation?
at handleAuth(), return Redirect::intended('/'); is returning something to postLogin(). You need to return that value from the postLogin().
So, add return at postLogin().
if($auth)
return $this->handleAuth();
Other Fixes
at handleAuth(), also add return
else
return $this->handleBan($banned_info, $current_ip);
Looks you are forgot to return handleBan() results
public function postLogin()
{
//...
if($auth)
return $this->handleAuth();
//...
}
private function handleAuth()
{
//...
if(is_null($banned_info))
return Redirect::intended('/');
else
return $this->handleBan($banned_info, $current_ip);
}
Related
Im making a api with laravel 9 about a fitness app. This app will have 2 roles Admin and Normal user. What im struggling with is making an Authentication system for the users, so that an Admin can have access to certain routes that a normal user wont. To do that i created a middleware called AdminMiddleware
{
if(Auth::check()){
//if admin role = 1
$user = Auth::guard('api')->user();
if(Auth::user()->role == 1){
return $next($request);
}
else{
return redirect('/login')->with('message', 'Access Denied');
}
}
else{
return redirect('/login')->with('message', 'Log In to gain access');
}
return $next($request);
}
This checks whether the user is an admin or not.
This is the routes where the middleware is used
Route::middleware(['auth', 'isAdmin'])->get('/admin', function () {
Route::post('/addProducts', [ProductController::class, 'store']);
Route::delete('/deleteProducts/{id}', [ProductController::class, 'destroy']);
Route::post('/addRecipe', [RecipeController::class, 'store']);
Route::put('/updateRecipe/{id}', [RecipeController::class, 'update'], function (Request $id) {
return 'Recipe '.$id;
});
Route::delete('/deleteRecipe/{id}', [RecipeController::class, 'destroy']);
Route::post('/addEvent', [SpecialEventsController::class, 'store']);
Route::get('event/{id}', [SpecialEventsController::class, 'show'], function (Request $id) {
return 'Events '.$id;
});
Route::put('/updateEvent/{id}', [SpecialEventsController::class, 'update'], function (Request $id) {
return 'Recipe '.$id;
});
Route::delete('/deleteEvent/{id}', [SpecialEventsController::class, 'destroy']);
});
And this is the Login Controller, where im having my problems:
public function login(Request $request)
{
//(Auth::guard('api')->attempt(['email' => $request->email, 'password' => $request->password]))
if(Auth::guard('api')(['email' => $request->email, 'password' => $request->password])){
$user = Auth::guard('api')->user();
if($user){
$success['token'] = $user->createToken('MyApp')->accessToken;
$success['name'] = $user->name;
$success['role'] = $user->role;
return $this->sendResponse($success, 'User login successfully.');
}
else{
return $this->sendError('Unauthorised.', ['error'=>'Unauthorised']);
}
}
else{
return $this->sendError('Unauthorised.', ['error'=>'Unauthorised']);
}
}
}
This code is returning
Error: Object of type Laravel\Passport\Guards\TokenGuard is not callable in file C:\xampp\htdocs\Ritwell-App - Copy (2)\app\Http\Controllers\API\RegisterController.php on line 52
The part
Auth::guard('api')(['email' => $request->email, 'password' => $request->password])){
was originally
if(Auth::attempt
but that code gave me an exception
BadMethodCallException: Method Laravel\Passport\Guards\TokenGuard::attempt does not exist. in file C:\xampp\htdocs\Ritwell-App - Copy (2)\vendor\laravel\framework\src\Illuminate\Macroable\Traits\Macroable.php on line 113
I tried using resources online to help with answers but this is what i could come up with so far and it isnt working.
Can someone tell me what am i doing wrong and what is missing in my code?
I have two roles in my app admin and users. Both roles are using a middleware called auth. Now in the application, when i login as a admin, i am not able to route to user page (that is perfect).
But when i login as user, i am able to route to admin page but my auth must prevent the user from accessing the admin page. Currently, that is my issue... What am i not doing right?
Below is my code
AuthMiddleWare
if (Auth::check())
{
if(Auth::user()->roles->pluck('name')->first() == "admin")
{
// return $next($request);
return Redirect::to('/admin/dashboard');
}
else if(Auth::user()->roles->pluck('name')->first() == "user")
{
return Redirect::to('/user/dashboard/');
}
else{
return Redirect::to('login');
}
}
Route
Route::group(array('prefix' => 'admin', 'namespace' => 'Admin', 'middleware' => ['auth']), function () {
Route::get('dashboard','Controller#show');
}
Route::group(array('prefix' => 'user', 'namespace' => 'User', 'middleware' => ['auth']), function () {
Route::get('dashboard','Controller#show');
}
Try the following code:
if (Auth::check())
{
if(in_aaray('admin', Auth::user()->roles->pluck('name')->all()))
{
// return $next($request);
return redirect('/admin/dashboard');
}
else if(in_array('user', Auth::user()->roles->pluck('name')->all()))
{
return redirect('/user/dashboard/');
}
else{
return redirect('login');
}
}else{
return redirect('login');
}
you need to make custom auth in laravel and make different table for admin and user
I'm using Socialite to get user information from facebook. All is going well but my redirect isn't working
Sub-routes
I read that it's not possible to do a redirect from a submethod, or
any method that's not in your routes.
But how else can i redirect the user after I logged them in?
My URL looks like this after the successfull facebook handshake
http://tmdb.app/auth/login/facebook?code=AQBTKNZIxbfdBruAJBqZ8xx9Qnz...
Code
class SocialController extends Controller {
public function login(Authenticate $authenticate, Request $request)
{
return $authenticate->execute($request->has('code'), $this);
}
public function userHasLoggedIn($data)
{
$user = User::where('provider_id', $data->id)->first();
if( !$user )
{
$user = User::create([
'name' => $data->name,
'email' => $data->email,
'provider' => 'facebook',
'provider_id' => $data->id
]);
}
// NOT WORKING!
return redirect('test');
}
}
Your login function should be handling the redirect.
I'm guessing execute returns $data if the user is sucessfully logged in and false if not.
class SocialController extends Controller {
public function login(Authenticate $authenticate, Request $request)
{
if($data = $authenticate->execute($request->has('code'), $this))
{
$user = User::where('provider_id', $data->id)->first();
// maybe delegate the user creation to another class/service?
if( !$user )
{
$user = User::create([
'name' => $data->name,
'email' => $data->email,
'provider' => 'facebook',
'provider_id' => $data->id
]);
}
return redirect('test');
}
return redirect('fail_view');
}
}
You can do it using PHP header function in Laravel sub method. I try it and works properly. Hope it can help you.
// You can using the following code
$url= url("about-laravel");
header("Location:" . $url);
exit;
// Or using the following code to redirect and keep set flash message
$result= $this->yourMethod(); // return redirect($this->route)->with('flash_message', 'I\'m Flash Message'); for TRUE or NULL for false
if( $result ){
return $result;
}
I have this in my database
|username|password|type|
------------------------
|foo |12345 |1 |
|asd |adsdsd |0 |
Here 1 means that the user is an admin, 0 a normal user.
How can I redirect the admin to the admin page and the normal user to normal user page??
if($attempt)
{
$id = User::find($attempt);
$user = $id->type;
if($user === 0)
{
return Redirect::action('LoginUsersController#profile');
}
else
{
return Redirect::to('adminpage');
}
}
I created this in my UsersController page I don’t know if this is the proper way to do this, and my code is not working.
Are you using normal Laravel Authentication?
You will get Object Auth::user(), this will return current user Object.
It should look like this.
Controller (SessionsController#store)
public function store() {
$input = Input::all();
$attempt = Auth::attempt([
'username' => $input['username'],
'password' => $input['password']
]);
if($attempt) {
if(Auth::user()->type == 1) {
return Redirect::admin(); // If admin, redirect to admin
} else {
return Redirect::profile(); // Else, redirect to user profile
}
}
}
Route
Route::resource('sessions', 'SessionsController', ['only' => ['create','store','destroy']]);
Route::get('admin', 'AdminController#dashboard')->before('adminAuth');
Route::get('profile/{id}', 'UsersController#showProfile')->before('auth');
First of all you have to add a new field in your users table to check against, for example 'rank'. If rank for a user is '1' so he is an Admin,
else he is a normal user.
Then define all required routes in your routes file like this:
Route::get('login', 'adminController#login');
Route::post('login', 'adminController#checkuser');
Route::group(array('before' => 'auth'), function() {
Route::resource('admin', 'adminController');
Route::resource('normaluser', 'normaluserController');
} );
Then in your controller you have to define all actions:
public function login()
{
return View::make('loginview');
}
public function checkuser()
{
if (Auth::attempt(array('username'=>Input::get('username'), 'password'=>Input::get('password'))))
{
$user_data = Auth::getUser();
if ($user_data->rank == 1) //if true, so this user is an admin
{return Redirect::to('admin');} //go to adminController index action
else //if not, so he is a normal user
{return Redirect::to('normaluser');} // go to normaluserController index action
}
else
{
//return 'wrong user name or password';
Session::flash('mismatch', "Username and Password mismatch");
return Redirect::to('login'); // go again to login form to relogin
}
}
if any thing is not clear, don't hesitate to ask.
in the if statement use:
if($attempt)
{
$id = User::find($attempt);
$user = $id->type;
if($user === 0)
{
return Redirect::action('LoginUsersController#profile');
header('Location: http://www.example.com/userpahe.php');
}
else
{
return Redirect::to('adminpage');
header('Location: http://www.example.com/admin-page.php');
}
}
This is pretty standard login function and validation that works nicely. But I also want to check that the user is active. I have set up a column in my users table with 'active' set to either 0 or 1.
public function post_login()
{
$input = Input::all();
$rules = array(
'email' => 'required|email',
'password' => 'required',
);
$validation = Validator::make($input, $rules);
if ($validation->fails())
{
return Redirect::to_route('login_user')
->with_errors($validation->errors)->with_input();
}
$credentials = array(
'username' => $input['email'],
'password' => $input['password'],
);
if (Auth::attempt($credentials))
{
// Set remember me cookie if the user checks the box
$remember = Input::get('remember');
if ( !empty($remember) )
{
Auth::login(Auth::user()->id, true);
}
return Redirect::home();
} else {
return Redirect::to_route('login_user')
->with('login_errors', true);
}
}
I've tried something like this already:
$is_active = Auth::user()->active;
if (!$is_active == 1)
{
echo "Account not activated";
}
But this can only be used within the 'auth attempt' if statement and at that point the users credentials(email and pass) are already validated. So even if the users account if not active at this point they are already logged in.
I need a way to return validation to let them know they still need to activate their account and check if their account is set at the same time their email and pass are being checked.
Filters are the way to go. It's easy and clean to solve this problem, see my example below.
Route::filter('auth', function()
{
if (Auth::guest())
{
if (Request::ajax())
{
return Response::make('Unauthorized', 401);
}
else
{
return Redirect::guest('login');
}
}
else
{
// If the user is not active any more, immidiately log out.
if(Auth::check() && !Auth::user()->active)
{
Auth::logout();
return Redirect::to('/');
}
}
});
Can't you use something like this:
if (Auth::once($credentials))
{
if(!Auth::user()->active) {
Auth::logout();
echo "Account not activated";
}
}
Just make the active field one of the confirmations. You can do this:
$credentials = array(
'username' => $input['email'],
'password' => $input['password'],
'active' => 1
);
if (Auth::attempt($credentials))
{
// User is active and password was correct
}
If you want to specifically tell the user they are not active - you can follow it up with this:
if (Auth::validate(['username' => $input['email'], 'password' => $input['password'], 'active' => 0]))
{
return echo ('you are not active');
}
A better solution might be to create an Auth driver that extends the Eloquent Auth driver already in use and then override the attempt method.
Then change your auth config to use your driver.
Something like:
<?php
class Myauth extends Laravel\Auth\Drivers\Eloquent {
/**
* Attempt to log a user into the application.
*
* #param array $arguments
* #return void
*/
public function attempt($arguments = array())
{
$user = $this->model()->where(function($query) use($arguments)
{
$username = Config::get('auth.username');
$query->where($username, '=', $arguments['username']);
foreach(array_except($arguments, array('username', 'password', 'remember')) as $column => $val)
{
$query->where($column, '=', $val);
}
})->first();
// If the credentials match what is in the database we will just
// log the user into the application and remember them if asked.
$password = $arguments['password'];
$password_field = Config::get('auth.password', 'password');
if ( ! is_null($user) and Hash::check($password, $user->{$password_field}))
{
if ($user->active){
return $this->login($user->get_key(), array_get($arguments, 'remember'));
} else {
Session::flash('authentication', array('message' => 'You must activate your account before you can log in'));
}
}
return false;
}
}
?>
In your login screen, check for Session::get('authentication') and handle accordingly.
Alternatively, allow them to log in but don't let them access any pages other than one that offers a link to resend the activation email.
This is what I do:
if (\Auth::attempt(['EmailWork' => $credentials['EmailWork'], 'password' => $credentials['Password']], $request->has('remember'))) {
if (\Auth::once(['EmailWork' => $credentials['EmailWork'], 'password' => $credentials['Password']])) {
if (!\Auth::user()->FlagActive == 'Active') {
\Auth::logout();
return redirect($this->loginPath())
->withInput($request->only('EmailWork', 'RememberToken'))
->withErrors([
'Active' => 'You are not activated!',
]);
}
}
return redirect('/');
}
return redirect($this->loginPath())
->withInput($request->only('EmailWork', 'RememberToken'))
->withErrors([
'EmailWork' => $this->getFailedLoginMessage(),
]);