Login by code seems to not work in laravel - laravel

Basically i'm trying to send by email a link that lets you login with a specific account and then redirects you to a page.
I can seccessfully generate link and send them via email using URL functionalities in laravel using this code:
Generating the link:
$url = "some/page/".$travel_id;
$link = URL::temporarySignedRoute(
'autologin', now()->addDay(), [
'user_id' => 3,
'url_redirect' => $url,
]
);
And sending the mail:
Mail::send('emails.travel', $data, function ($message) use ($data) {
$message->from('mail#mail.com', 'blablabla');
$message->to('reciever#mail.com', 'blablabla')->subject('test');
});
There is a route that catches the link sent by mail that is supposed to log you in with the user (in this case, the one with the id '3') and redirect you to some page but when it redirects, it prompts you to the login page, as if you are not logged.
Here is the route:
Route::get('/autologin', function (Request $request) {
$user = User::findOrFail($request->user_id);
if (! $request->hasValidSignature()) {
abort(403);
}
Auth::login($user);
return redirect($request->input('url_redirect'));
})->name('autologin');
When i try to do a Auth::check() after the Auth::login($user); it returns true, so is the user logged in?
I also tried to use Auth::loginUsingId($request->user_id); with no different results.
Any idea of what's happening?

So i found the problem,
I was logging in with a backpack user but i was using the default auth feature of laravel.
Turns out i need to use: backpack_auth()->login($user); instead of Auth::login($user); if i want to login using a backpack user.
Also use backpack_auth()->check() instead of Auth::check().

Related

How can write test for laravel api route with auth.basic middleware

My laravel project has an API route by auth.basic middleware which is used id of the authenticated user in the controller. when I call it in postman it works well and I get 401 when the username or password is incorrect, but in laravel APITest which extends from DuskTestCase, authentication does not take place so I get 500 instead of 401 when the user's informations were incorrect. However, by correct information, I have the same error because auth() is null.
it is written like below, which is wrong?
api.php route:
Route::get('/xxxxxx/xxxxxx/xxxxxxx', 'xxxxx#xxxx')->middleware('auth.basic');
APITest:
$response = $this->withHeaders(['Authorization' => 'Basic '. base64_encode("{$username}:{$password}")])->get("/xxxxxx/xxxxxx/xxxxxxx");
You can use actingAs() method for authentication in tests.
An example from docs:
public function testApplication()
{
$user = factory(App\User::class)->create();
$this->actingAs($user)
->withSession(['foo' => 'bar'])
->visit('/')
->see('Hello, '.$user->name);
}
Another example that you can use for an API:
$user = User::factory()->create();
$response = $this->actingAs($user)->json('GET', $this->uri);
$response->assertOk();
For more information: https://laravel.com/docs/5.1/testing#sessions-and-authentication

Create session on consuming login with api on laravel

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;
}

Laravel Ajax login, redirect to previous url after success

Suppose I have a page A where auth middleware is being used. Because of no login it gets redirected to login page.
On login page I have custom ajax login system. On succesful login I want to redirect to page A with same url so that action can be completed.
My code for login is like this:
public function postLogin(Request $request)
{
$auth = false;
$errors = [];
$inputs = $request->all();
$validator = $this->validator($inputs);
if ($validator->fails()) {
return response()->json([
'auth' => false,
'intended' => URL::previous(),
'errors' => $validator->errors()
]);
}
$user = User::where('email', $request->get('email'))->first();
if ($user && $user->is_active == 0) {
$errors[] = "This account has been deactivated";
} else if ($user && $user->confirm_token != null) {
$errors[] = "Please verify your email in order to login";
} else {
$credentials = ['email' => $request->get('email'), 'password' => $request->get('password'), 'is_active' => 1];
if (Auth::attempt($credentials, $request->has('remember'))) {
$auth = true;
} else {
$errors[] = "Email/Password combination not correct";
}
}
if ($request->ajax()) {
return response()->json([
'auth' => $auth,
'intended' => URL::previous(),
'errors' => $errors
]);
}
return redirect()->intended(URL::route('dashboard'));
}
I am trying to get previous url through url()->previous() but it returns login page url. Can someone guide me in this please. Any improvements/help will be appreciated.
I am using Laravel 5.4
I have a very similar problem here: Ajax Auth redirect on Laravel 5.6
As #aimme (https://stackoverflow.com/users/1409707/aimme) pointed out, Ajax calls are stateless, so basically you can't interact with backend.
His suggestion and my suggestion is to pass in the URL the intended page to redirect to, or maybe in your case you could to it via post parameters, e.g.:
return response()->json([
'auth' => false,
'intended' => $request->intended,
'errors' => $validator->errors()
]);
There is no need to do anything special for AJAX calls.
Redirect the same way you normally would on the back-end after a form submission.
return redirect()->route('dashboard');
On the front-end you just need to be sure that you use the redirected URL to change the window.location. This will cause the browser to refresh and go to the new page.
axios.post(url, formData).then(response => {
window.location = response.request.responseURL;
});
This code snippet is for the popular Axios library but the same thing can be done with jQuery or vanilla JavaScript.
It might help you
Instead of these return redirect()->intended(URL::route('dashboard'));
use
return redirect('dashboard');
Laravel 5.4 + support . not know for lower version
return redirect()->back();
This will redirect to previous page from where you came .
Ajax part
<script type="text/javascript">
var url = "<?php echo url()->previous(); ?>";
location.href = url;
</script>
OR simply javascript function
history.go(-1);
Check for above and working fine for me please check for your code .
If I understand your problem correctly, the problem is that you're confusing the previous URL for the intended URL when you're trying to provide a URL to redirect to in your JSON response. The previous URL actually refers to the HTTP Referrer, not the intended URL, which is set in the session by Laravel's auth middleware.
The HTTP referrer is the page that initiates a request. If you are currently on page /foo and you click a link to a page /bar, the HTTP Referrer on /bar will be /foo. The same thing happens when you initiate an AJAX request, the page you're on will be the referrer of the end point you're hitting. In your case your login page is initiating the request to your login handler, via AJAX.
When you try to visit a page protected by Laravel's auth middleare, it is at that point Laravel sets a value for the intended URL in the session, before redirecting you to the login page. Laravel stores the intended URL in the session as url.intended (As you will be able to see in Illuminate\Routing\Redirector::intended, which is what redirect()->intended() calls). So all you need to do is grab that from the session.
if ($request->ajax()) {
return response()->json([
'auth' => $auth,
'intended' => session()->pull('url.intended') ?: URL::route('dashboard'),
'errors' => $errors
]);
}
return redirect()->intended(URL::route('dashboard'));
Note: Using ->pull will remove the item from the session after it has been retrieved.
An easier way to do this would be just to grab the target URL from an intended RedirectResponse:
$redirect = redirect()->intended(URL::route('dashboard'))
if ($request->ajax()) {
return response()->json([
'auth' => $auth,
'intended' => $redirect->getTargetUrl(),
'errors' => $errors
]);
}
return $redirect;
I solved it by making a hidden field in form containing url()->previous() value because no other way I was getting previous page i.e Page A url. I tried almost all above answers.
URL::previous();
this method will help you get previous URL. and you can redirect user to there using jQuery somelike this:
window.location.href = url; // <- you can try your url here.
Good Luck !!
First of all when you got a request in backend save the redirect()->intended();
intended() checks if the session index url.intended exists and
redirects to it by default or else redirect to $default='/' which can
be overwritten.
then pass this URL when request success, example:
function testAjax(handleData) {
$.ajax({
url:"getvalue.php",
success:function(data) {
window.location.href = data.url;
}
});
}

Laravel 5.4 show activation pending message on login form

I am working on Laravel 5.4 project. I love the login provided by Laravel and it works fine with both login or register.
I add below code to Auth/LoginController.php. It allows only activated users (status=1) to successfully login, but not pending users or blocked users (status =0 or something else).
protected function credentials(\Illuminate\Http\Request $request)
{
return ['email' => $request->{$this->username()}, 'password' => $request->password, 'status' => 1];
}
Anyway, to protect spam I would like to allow only activated users to login. For those whose account are not activated, I would like to show the pending message on the login form. Also, I would like to do the same thing for blocked users.
Could you please advise me how to achieve this?
This way Laravel would only pick only users by the credentials you specify, if you want to check status the user has and what view to show you can overwrite the authenticated() method of the login controller. It will have access to the already logged in user so note that you have to logout it the status is invalid.
protected function authenticated(Request $request, $user)
{
if ( $user->status == 0 ) {
auth()->logout();
return back()->withErrors(['email' => 'You are blocked or not activated.']);
}
return redirect()->intended($this->redirectPath());
}

Redirects an authenticated user back to the originally requested URL or the default URL in laravel

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();

Resources