I'm getting this TokenMismatchException with Laravel 4. It happens to me if the browser sits on the login page for a while. For example a lot of times when I come back to work on my project the next day, if my browser has the login page open in a tab, when I try to log in I get the TokenMismatchException. If I'm logging in and out throughout the day while working, I never see it. It's like the token expires or something.
Route.php
// route to show the admin login form
Route::get('login', array('uses' => 'AdminController#showLogin'));
// route to process the admin login form
Route::post('login', array('uses' => 'AdminController#doLogin'));
AdminController.php
public function showLogin()
{
// show the login form
return View::make('admin.login');
}
public function doLogin()
{
// validate the info, create rules for the inputs
$rules = array('username' => 'required','password' => 'required' );
// run the validation rules on the inputs from the form
$validator = Validator::make(Input::all(), $rules);
// if the validator fails, redirect back to the form
if ($validator->fails()) {
return Redirect::to('login')
->withErrors($validator) // send back all errors to the login form
->withInput(Input::except('password')); // send back the input (not the password) so that we can repopulate the form
} else {
// create our user data for the authentication
$userdata = array('my_username'=> Input::get('username'),'password'=> Input::get('password'));
// attempt to do the login
if (Auth::attempt($userdata)) {
return Redirect::intended('dashboard');
} else {
// Authentication not successful, send back to form
return Redirect::to('login')->with('message', 'Your username/password combination was incorrect');
}
}
}
Please, help is needed...
That's normal, session will expire if you get idle for too long. It's a security measure, so you just need to make sure you redirect your user to login when the token expires. Add this to your global.php file or create a exceptions.php file to it:
App::error(function(\Illuminate\Session\TokenMismatchException $exception)
{
return Redirect::route('login')->with('message','Your session has expired. Please try logging in again.');
});
Related
I have an api that has a method to start and I am calling it from a frontend project.
In the front end project I use Guzzle to make the call via post to the api and login, from which I get back a json with the user data and a jwt token.
But when I receive the token as I manage the session, I must create a session and save the token, since the laravel to authenticate I need a model user and have a database, which of course I do not have in this backend because I call the api to log in, which brings a token and user data, then as I manage it from the backend, I'm a little lost there.
$api = new Api();
$response = $api->loginapi(['user'=>'wings#test.com','password'=>'123']);
Because here I could not do Auth::login($user) to generate the session.
Because I don't have here the database because the login is done from the api.
There I call the api, of which the answer is the token, but how do I manage it from here, creating a session? saving the token?
thanks for your help.
With api, you don't usually manage a session. usually, you'd call something like
Auth::attempt([
'email' => 'me#example.com',
'password' => 'myPassword'
]);
If the credentials are correct, laravel will include a Set-Cookie header in response, and, that is how you authenticate with api. Via an auth cookie. You don't need to do anything else.
Let's show you how:
//AuthController.php
public function login(Request $request) {
$validatedData = $request->validate([
'email' => 'required|email',
'password' => 'required'
]);
if(Auth::attempt($validatedData)){
return ['success' => 'true'];
}
else{
return ['success' => false, 'message' => 'Email or password Invalid'];
}
}
public function currentUser (){
return Auth::user();
}
Now, the APi file
Route::post('/login', ['App\Http\Controllers\AuthController', 'login']);
Route::get('/current_user', ['App\Http\Controllers\AuthController', 'currentUser']);
Now if you make a call to /api/current_user initially, you'll get null response since you're not currently logged in. But once you make request to /api/login and you get a successful response, you are now logged in. Now if you go to /api/current_user, you should see that you're already logged in.
Important ::
If you are using fetch, you need to include credentials if you're using something other than fetch, check out how to use credentials with that library or api
You want to use the API to authenticate and then use the SessionGuard to create session including the remember_me handling.
This is the default login controller endpoint for logging in. You don't want to change this, as it makes sure that user's do not have endless login attempts (protects for brut-force attacks) and redirects to your current location.
public function login(Request $request)
{
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
if ($request->hasSession()) {
$request->session()->put('auth.password_confirmed_at', time());
}
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
The core happens when we try to "attemptLogin" at
protected function attemptLogin(Request $request)
{
return $this->guard()->attempt(
$this->credentials($request), $request->boolean('remember')
);
}
When using the SessioGurad (which is default) the method attemptLogin fires a couple of events, checks if the user has valid credentials (by hashing the password and matching it with db) and then logs the user in, including the remember me functionality.
Now, if you don't care about events, you can just check from your API if the credentials match and then use the login method from the guard. This will also handle the remember me functionality. Something like this:
protected function attemptLogin(Request $request)
{
$username = $request->input($this->username());
$password = $request->input('password');
$result = \Illuminate\Support\Facades\Http::post(env('YOUR_API_DOMAIN') . '/api/v0/login' , [
'username' => $username,
'password' => $password
])->json();
if(empty($result['success'])){
return false;
}
// Maybe you need to create the user here if the login is for the first time?
$user = User::where('username', '=', $username)->first();
$this->guard()->login(
$user, $request->boolean('remember')
);
return true;
}
I'm making a normal Inertia post to a base Laravel login route:
submit() {
this.$inertia.post("/login", {
email: this.emailAddress,
password: this.password,
}, {
preserveState: true,
preserveScroll: true,
});
}
I'm able to catch validation errors as expected, but what I'm trying to avoid is the redirect after a successful user authentication, and instead proceed in the "logged in" state (update header to show user info, etc).
The Laravel AuthenticatesUsers trait contains this contains two key methods that gets called as part of the out-of-the-box login flow
public function login(Request $request)
{
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
and
protected function sendLoginResponse(Request $request)
{
$request->session()->regenerate();
$this->clearLoginAttempts($request);
if ($response = $this->authenticated($request, $this->guard()->user())) {
return $response;
}
return $request->wantsJson()
? new Response('', 204)
: redirect()->intended($this->redirectPath());
}
I'm struggling to figure out if it's even possible to authenticate a user without redirecting this way.
You need to utilize the javascript frontend, not Inertia::post() . One way to do this is to use Axios:
submit() {
const data = {...this.form.data()};
axios.post('/auth/login', data, {
headers: {
'Content-Type': 'application/json',
},
})
.then(res => {
console.log('login success!', res);
});
Check your form and the way you submit - do you prevent the default behavior of the form submit? It seems like you are sending a POST but the native form behavior is also triggered.
You can also set a $redirectTo in your LoginController, also check RouteServiceProvider there is a public const HOME = '/' which triggered the redirect if nothing else is given.
This are my two cents...
A few days ago I was struggling with passing the result of the script to Vue without redirecting, using Inertia visits instead of Axios.
The solution I adopted was the following:
In vue:
this.$inertia.visit(`URL`, {
method: "post",
data: { //Email and password },
preserveState: false,
preserveScroll: false,
onError: (errors) => { // do what ever is needed if validation fails },
onSuccess: () => { // do what ever is needed if validation succeeds }
});
In Laravel:
// If validation fails:
return redirect()->back()->withErrors([
'login' => 'Validation fail details.'
]);
// If validation succeeds:
return redirect()->back()->with('login', 'Success message!');
This way the page does not redirect and the user can continue exactly wherever he is.
What i'm not sure is if it's possible to pass the user info over the success redirect message. Maybe returning a array like it's done in withErrors. If not possible it's always possible to make an additional request to the server to retrieve the desired information.
Hope it's usefull.
Can't...Log users... in... Angry.
Basically, I'm running users through the regular login, with my own authenticated method defined to return a json object instead of being redirected. For the moment, I'm returning the results of this function
Auth::check();
Which returns true when all is said and done.
The problem is that my logins don't appear to exist beyond this single call. If I make anymore ajax calls, Auth::check() fails every time. If I refresh the page, auth::check() fails.
Please god help me.
PS. My session driver is current set to cookies.
//EDIT TO SHOW THE PEOPLE I'M NOT CRAZY
public function ajaxLogin(Request $request)
{
if(Auth::check()){
return response()
->json('loggedIn');
}
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
return $this->handleUserWasAuthenticated($request, $throttles);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
if ($throttles && ! $lockedOut) {
$this->incrementLoginAttempts($request);
}
return response()
->json([
$this->loginUsername() => $this->getFailedLoginMessage(),
]);
}
protected function authenticated($request, $user){
Auth::login($user);
return response()->json( 'yo');
}
If I send the right credentials, I get back yo. If I send the right credentials again (another ajax call) I get yo. I can never get logged in.
From what I can tell, for some reason, my sessions are being destroyed with every request? I don't know why, but this does appear to be why I'm never logged in.
//EDIT ADD SITUATIONAL CLARITY
laravel -v 5.2
this is all through the auth controller, so just the default:
Route::group(['middleware' => 'web'], function () {
Route::auth();
Route::get('/home', 'HomeController#index');
});
I am new at laravel and I want to achieve the following results, let's say a guest gets to the result page after searching for a term and then decides to login, how can I get the user to login and keep the same result page in laravel
I have the following code
in the filters.php I have the following:
Route::filter('guest', function()
{
if (Auth::check()) return Redirect::to('/');
});
then in the user controller I have the following
the show login function
public function login()
{
return View::make('users.login');
}
the handle login function
public function handleLogin()
{
$data = Input::only(['email', 'password']);
if(Auth::attempt(['email' => $data['email'], 'password' => $data['password']])){
return Redirect::to('/profile');
}
return Redirect::route('login')->withInput();
}
right now the default page after login goes to the profile page but I want the user to go back to wherever he was before login.
any help? thanks
I think that's what you looking for
return Redirect::back();
In Laravel 4, you can use Redirect::guest and Redirect::intended to achieve your target easily.
Redirect::guest put the current URL into the session before redirect to the target URL.
Redirect::intended check whether there is any URL saved in the session, redirect to that URL or a default location if it does not exist.
In action, your code can be:
if(Auth::attempt(['email' => $data['email'], 'password' => $data['password']])){
return Redirect::guest('/profile');
}
and after log in
if (Auth::check()) return Redirect::intended();
Upon login, I return the user object + session token in JSON form, so that the mobile device that connects to my application can be authenticated.
However, I have a difficulty understanding how would I go about authenticating the user only with his session id?
Once logged in, the mobile device sends the session token upon every request, which means I somehow need to check whether it's the same user (using a custom auth filter).
How would I do it?
You may have a table for saving tokens
Add a filter in routes.php
Route::group(array('before' => 'auth'), function() { ... })
And in the filters.php you can search the token in the database, if isn't exist you return a no access response
Route::filter('auth', function () {
$input_token = Input::get('token');
if (!empty($input_token)) {
$validator = Validator::make(
['token' => $input_token],
['token' => 'token']
);
if (!$validator->fails()) {
$token = Token::where('hash', $input_token)->first();
if ($token) {
$user = User::find($token->user_id);
if ($user) {
Auth::login($user);
return;
}
}
}
}
$response = Response::make(json_encode([
'error' => true,
'messages' => [
Lang::get('errors.NO_ACCESS')
]
]), 200);
$response->header('Content-Type', 'application/json');
return $response;
});
You could do it like this:
$sessionID = '4842e441673747d0ce8b809fc5d1d06883fde3af'; // get this from \Session::getId(); from your previous authenticated request (after logging in because it changes).
$s = new \Illuminate\Session\Store(NULL, \Session::getHandler(), $sessionID);
$s->start();
$userID = $s->get('login_82e5d2c56bdd0811318f0cf078b78bfc');
\Session::set('login_82e5d2c56bdd0811318f0cf078b78bfc', $userID);
return \Auth::user();
Not the prettiest code but it works. It creates an instance of a session using the previous Session ID, then start loads it up from file. The user ID is in that key, so then it just sets the user id on the current session. Then when you call Auth::user() it loads up the User using that user id.
The reason for all the numbers in the key is because the larval developer thought it would be smart to hash the Auth class name to make the key as unique as possible... :-S