Laravel unauthorized page when ajax send request - laravel

I'm making web based game. The game need to login first, so I use laravel auth::register and auth::login as usual, and add middleware auth to every pages except login&register page.
Then check the game status using smartupdater if ready or not.
$("section").smartupdater({
url : urlCheckStatus,
data : data,
dataType : 'json',
minTimeout: 2000
}, function(response){
var gameStatus = response.data.status;
if(gameStatus === 'start')
{
gameOn();
}
else if(gameStatus === 'active')
{
pleaseWait();
}
else if(gameStatus === 'stop')
{
backToMenu();
}
});
Register and login function
public function register(Request $request)
{
$name = $request->input('username');
$user = new User;
$user->name = $request->input('username');
$user->email = $name.'#abc.com';
$user->password = bcrypt(Carbon\Carbon::now());
$user->grade = $request->input('grade');
$user->numb = $request->input('numb');
$user->save();
Auth::login($user, true);
return redirect('menu');
}
check game status function
public function checkGameStatus()
{
$game_id = Request::input('game_id');
$data = Game::find($game_id);
return response()->json([
'data' => $data
]);
}
But sometimes I was thrown to login page, because error 401 Unauthorized. Trying to console log like the image above.
Thanks

I see you are using Javascript for authorization with Laravel, as per Laravel documentation for API Authentication with Javascript:
If you are using a different JavaScript framework, you should make
sure it is configured to send the X-CSRF-TOKEN and X-Requested-With
headers with every outgoing request.
Can you ensure X-CSRF-TOKEN and X-Requested-With are always present in your headers.

Related

Authorization isn’t executed in laravel

This is form ‘login’, when I’m enter information in input and after press button ‘login’ I move to page with audit where 0 is authorization isn't executed and 1 is authorization is executed. I'm trying to create authorization in laravel.
I did it in other project in order to understand how it works and then all was good. Now I'm trying to transfer it in my main project but when I am logging in nothing work.
I don't have any mistake, authorization is simply not executed. I will grateful for any help.
Registration function
public function sub(ContactSignup $request){
if(Auth::check()){
return redirect(route('user.mainpage'));
}
$contact = new SignUps();
$contact->name = $request->name;
$contact->surname = $request->surname;
$contact->age = $request->age;
$contact->password = bcrypt($request->password);
$contact->email = $request->email;
$contact->save();
Auth::login($contact);
if($contact){
Auth::login($contact);
return redirect(route('user.sign-up'))->with('success', 'Реєстрація пройшла успішно');
}
}
Function login
public function subin(ContactSignin $request){
if(Auth::check()){
return redirect()->intended(route('user.mainpage'));
}
$contact = $request->only(['email', 'password']);
if(Auth::attempt($contact)) {
dd(1);
}
else {
dd(0);
}
return redirect()->intended(route('user.mainpage'));
}
Web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return redirect()->route('mainpage');
});
Route::name('user.')->group(function(){
Route::view('mainpage', 'mainpage')->middleware('auth')->name('mainpage');
Route::get('/signin', function(){
if(Auth::check()){
return redirect(route('user.mainpage'));
}
return view('signin');
})->name('sign-in');
Route::post('/signin', [\App\Http\Controllers\ContactController::class, 'subin'])->name('sign-in');
Route::get('logout', function(){
Auth::logout();
return redirect('/');
})->name('logout');
Route::get('/signup', function(){
if(Auth::check()){
return redirect(route('user.mainpage'));
}
return view('signup');
})->name('sign-up');
Route::post('/signup', [\App\Http\Controllers\ContactController::class, 'sub'])->name('sign-up');
});
Route::get('/mainpage', function () {
return view('mainpage');
})->name('mainpage');
in your methods inside your controller you have form request class called ContactSignin
in this class you have code
public function authorize()
{
return false;
}
make it true
The form request class is responsible of validating your request ,
also contains an authorize method. Within this method, you may determine if the authenticated user actually has the authority to update a given resource
since you handle authorization logic for the request in the routes by provide
middleware('auth') it is no need to check for use authentication in your form request class
first you don't have route named ('user.mainpage') you have to define it in web.php
second , since you named your rout in
Route::get('/mainpage', function () {
return view('mainpage');
})->name('mainpage');
, you must redirect to the name of the route not to the path
for example if your route is ('/api/main')->name('main') , you have to put the rout name in the redirect method , for example redirect(route('main'));

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

How to get redirect url in Vuejs from Laravel/Passport

I'm building a simple login in Vue.js where my backend in laravel/passport for authentication method. Before implementing passport I was having a manual login controller which get the redirect url from session something like this:
Suppose my Routes was Route::post('/login', 'LoginController#postLogin) now in the controller I was computing something like this:
public function postLogin(Request $request)
{
$this->validate($request, User::$login_validation_rules);
$data = $request->only('email', 'password');
$intended_url = Session::get('url.intended', url('/'));
Session::forget('url.intended');
// Authentication codes
return $intended_url, $user;
}
Now I was taking this in Vue file like this:
axios.post('/login', postData).then(response => {
// computation of objects
window.location.href = '/dashboard';
})
Now my url as I'm using Laravel\passport has been changed to axios.post('/oauth/token', postData)
Can someone guide me how to add intended_url to this response, Is it possible to get in same axios request or I have to call another axios request
You can set the return to a Json object that's easy to parse and Axios should be able to be set up to automatically parse it.
public function postLogin(Request $request)
{
$this->validate($request, User::$login_validation_rules);
$data = $request->only('email', 'password');
$intended_url = Session::get('url.intended', url('/'));
Session::forget('url.intended');
// Authentication codes
return \Response::json([ 'url' => $intended_url, 'user' => $user], 200);
}
This will create a Json object with the pertinent information and pass it with the correct headers. in JS it should be something similar to this:
axios.post('/login', postData)
.then(
response => {
let JsonObj = JSON.parse(response.body)
// computation of objects
window.location.href = JsonObj.url;
}
)
you may have to do some tests to see what response is returning and go from there.

Laravel Passport consuming own API fail

I'm building a SPA with Vue. My front-end and my back-end (Laravel) are in the same codebase. I want to approach my API (that is in my back-end) via the Laravel Passport Middleware CreateFreshApiToken. I'm approaching my sign in method in my AuthController via web.php.
My problem:
As soon as I'm successfully signed in via my sign in method I would expect that at this time Passport created the laravel_token cookie. This is not the case. The cookie is created after a page refresh. But as I said I'm building a SPA and that's why I don't want to have page refreshes.
What I want:
I want to sign in via my sign in method then use the Passport CreateFreshApiToken middleware. After that I want to use the (just created in the middleware) laravel_token cookie so that I can correctly and safely speak to my own API in my signed-in section of the SPA.
More information:
Kernel.php
// Code...
protected $middlewareGroups = [
'web' => [
// other middlewares...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
];
// Code...
AuthController.php
// Code...
public function login()
{
if (Auth::attempt(['email' => Input::get('email'), 'password' => Input::get('password')], true)) {
return response()->json([
'user' => Auth::user(),
'authenticated' => auth()->check(),
]);
}
return response()->json(['authenticated' => false], 401);
}
// Code...
Login.vue
// Code...
methods: {
login: function (event) {
event.preventDefault();
this.$http.post(BASE_URL + '/login', {
email: this.email,
password: this.password,
})
.then(function (response) {
localStorage.user_id = response.body.user.id;
router.push({
name: 'home'
});
});
},
},
// Code...
What goes wrong? This:
CreateFreshApiToken.php
// Code...
public function handle($request, Closure $next, $guard = null)
{
$this->guard = $guard;
$response = $next($request);
// I'm signed in at this point
if ($this->shouldReceiveFreshToken($request, $response)) { // returns false unless you refresh the page. That's why it won't create the laravel_token cookie
$response->withCookie($this->cookieFactory->make(
$request->user($this->guard)->getKey(), $request->session()->token()
));
}
return $response;
}
protected function shouldReceiveFreshToken($request, $response)
{
// both methods below return false
return $this->requestShouldReceiveFreshToken($request) &&
$this->responseShouldReceiveFreshToken($response);
}
protected function requestShouldReceiveFreshToken($request)
{
// $request->isMethod('GET') - returns false because it's a POST request
// $request->user($this->guard) - returns true as expected
return $request->isMethod('GET') && $request->user($this->guard);
}
protected function responseShouldReceiveFreshToken($response)
{
// $response instanceof Response - returns false
// ! $this->alreadyContainsToken($response) - returns false as expected
return $response instanceof Response &&
! $this->alreadyContainsToken($response);
}
// Code...
I assume it is possible what I want to achieve right? If yes, how?
I had the same issue, decided to stick to client_secret way. I guess it's not relevant for you now, but I've found 2 ways of receiving the laravel token without refresh:
1) sending dummy get request with axios or $http, whatever you use - token will get attached to response;
2) changing requestShouldReceiveFreshToken method in CreateFreshApiToken.php - replace return $request->isMethod('GET') && $request->user($this->guard); with return ($request->isMethod('GET') || $request->isMethod('POST')) && $request->user($this->guard);
function consumeOwnApi($uri, $method = 'GET', $parameters = array())
{
$req = \Illuminate\Http\Request::create($uri, $method, $parameters, $_COOKIE);
$req->headers->set('X-CSRF-TOKEN', app('request')->session()->token());
return app()->handle($req)->getData();
}

Laravel, log in user for one request

I am building a REST API with Laravel, and I have a filter that checks for a TOKEN:
Route::filter('api.auth', function() {
$token = Request::header('X-CSRF-Token') ? Request::header('X-CSRF-Token') : '';
if (empty($token)) {
return Response::json(
['message' => 'A valid API key is required!'],
401
);
};
$user = User::where('token', '=', $token);
if ($user->count()) {
$user = $user->first();
Auth::login($user);
} else {
return Response::json(
['message' => 'Your token has expired!'],
401
);
};
});
If everything is ok, the filter will log in the user with uth::login($user);
How can I log him for only 1 request?
Since this filter is going to be checked on every request, I think it would be better to log the user out each time.
I have seen this in Laravel's docs, not sure how to apply it:
if (Auth::once($credentials))
{
//
}
Could I have a callback in my response? where I could log the user out?
/*
Get all products.
*/
public function getProducts() {
$products = Auth::user()->products;
return Response::json($products, 200);
}
Any ideas?
If you haven't user's password use this:
if(Auth::onceUsingId($userId)) {
// do something here
}
If I correctly understand the question then I would say that, just replace following
Auth::login($user);
with this (To log the user in only for current request):
Auth::once(['email' => $user->email, 'password' => $user->password]);
If you log in a user only for once then you don't have to manually logo out the user, the user will be asked again for to log in on next request.

Resources