How to handle laravel TokenMismatchException when session expires - javascript

When the session is expired, User cant log back in without a page refresh because the _token in ajax headers is expired(AKA TokenMismatchException). I cant handle the exception by redirecting user to a log in page because the login is an overlay modal and the request is handled via ajax.
I thought i could catch the mismatch exception in Handler.php and return a json response with a session token. and on the client side, use the new token to continue the intended process. However, when i use the new token passed from server, the session token will be changed again on server side which results another TokenMismatchException.
So how should i handle the exception in a secured way without refreshing a page?
Here's what i have right now:
setup csrf_token in a global js file:
$(function () {
$.ajaxSetup({
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf_token"]').attr('content') }
});
});
render method in app/exceptions/handler.php:
public function render($request, Exception $e)
{
if ($this->isHttpException($e))
{
return $this->renderHttpException($e);
}
else if ($e instanceof TokenMismatchException)
{
if ($request->ajax()) {
return response()->json([
'message' => 'TokenMismatchException',
'token' => csrf_token()
]);
}
}
else
{
return parent::render($request, $e);
}
}
in authentication.js:
$.ajax({
type: "POST",
url: "/auth/login",
data: {
"email" : $('#login_email').val(),
"password" : $('#login_password').val(),
'remember': $('#login_remember').is(':checked')
},
success: function(response) {
if (response.message === 'TokenMismatchException') {
console.log(response); //message and token exist
//if catch the exception, use the new token to set up the ajax headers and login again
$.ajaxSettings.headers["X-CSRF-TOKEN"] = response.token;
console.log($.ajaxSettings.headers["X-CSRF-TOKEN"]);
$.ajax({
type: "POST",
url: "/auth/login",
data: {
"email" : $('#login_email').val(),
"password" : $('#login_password').val(),
'remember': $('#login_remember').is(':checked'),
},
success: function(res) {
console.log(res);
},
error: function(err) {
console.log(err);
}
});
}
console.log('logged in');
},
error: function(xhr, status, err) {
}
});
thanks in advance.

In your render function, you've to check for a specific TokenMismatchException. So may be you can try something like this:
if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
return response()->json('msg', 'Your session has expired. Please try again.');
}
You may also pass a new csrf_token along with the json so that you can replace the old one with the new one and send the form request again.
if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
return response()->json(['msg'=> 'Your session has expired. Please try again.', 'token'=> csrf_token()]);
}
I haven't tested this code. But this should get you started.
Also, if you want, you can use a package: https://github.com/GeneaLabs/laravel-caffeine

Related

Response.redirect() is not a function in vanilla JS?

I am trying to redirect to another page after I receive a response from the post fetch, but as the title says it doesn't work.
These are the functions:
// send/post json
async function postData(json_data, api_path) {
const response = await fetch(api_path, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: json_data,
redirect: 'follow'
});
console.log("postData response: ", response);
return response;
}
// send JSON data to server on /api/${destination}
function saveSettings(form, destination) {
let json_data = toJSONstring(form);
let res;
console.log(json_data);
postData(json_data, `/api/${destination}`)
.then((response) => {
res = response;
if (!response.ok) {
throw new Error(`HTTP error, status = ${response.status}`);
}
return response.text();
}).then(text => {
if (destination === 'network/post') {
connected = false;
updatingToast(`You are no longer connected to the device !`, false);
updatingToast(`Please navigate to ${text}`, true, text);
}
console.log('res: ', res);
res.redirect(res.status, res.url);
});
}
Every console.log(); returns Response {type: 'basic', url: 'http://192.168.0.100/dashboard', redirected: true, status: 200, ok: true, …}
If I place response.redirect(response.status, response.url); in the first then() I get the same error.
So, does response.redirect exist in Vanilla JS ?
I don't want to use window.location.href or any other similar option because it bypasses HTTP Authentication header.
I see that you have the 'follow' argument given in the fetch.
You can check the if the response is being redirected using the code below. If it was not redirected you can simply change the window location and also force a redirect.
if (res.redirected) {
window.location.href = res.url;
}
EDIT:
After doing a bit more research into the redirect method I saw that you need to switch the URL and status variables, see: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect

post request on form with validation

i built a registration form and validated it using javaScript. but i want after a user filled the form, it should post to the server. i am confused on where to place my httpRequest function. i don't know if its after validation or inside the validation function
This is my validation function
function formregister(e){
if (first_name.value=== "" || last_name.value=== "" || user_id.value==="" || id_type.value=== ""
|| id_no.value=== "" || address.value==="" || !terms.checked) {
var fPassResult = '1';
} else{
var fPassResult = '0';
}
if(fPassResult === "1") {
window.location = "register.html";
}else{
Swal.fire({
type: 'success',
title: 'Your Registration has been Submitted Successfully',
text: 'Click Ok to Login',
timer: 10000
}
).then(function(){
window.location="Login.html";
})
}
e.preventDefault();
};
**And this is my post request function**
function registerationApiCall(e){
var data = {
"user_id":"user_id.value",
"user_pin": "user_pin.value",
"first_name":"first_name.value",
"last_name":"last_name.value",
"address":"address.value",
};
fetch("jehvah/api",{
type : 'POST',
data : data,
dataType : 'JSON',
encode : true,
success: function (response, status, xhr) {
if (result==="OK") {
console.log("success");
}else{
console.log("bad");
}
},
error: function (xhr, status, error) {
console.log("something went wrong");
}
});
}
Please kindly check my post request function, i dont know if i am doing it the right way
Hi ✌ when fPassResult === "0" in this case inside else{} call registerationApiCall()
you tell the user it's a success after you get OK from the server which is Asynchronous call
& inside fetch response you call swal.fire
for this code to work your server when checks the database & every thing is ok returns a msg like this {"msg":"OK"}
CODE:
else{
registerationApiCall()
}
function registerationApiCall becomes
fetch('jehvah/api',
{ method: 'POST',headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data)})
.then((response) => response.json())
.then((result) => {
console.log('Success:', result);
if (result.msg="OK") {
console.log("success");
Swal.fire({
type: 'success',
title: 'Your Registration has been Submitted Successfully',
text: 'Click Ok to Login',
timer: 10000
}).then(function(){window.location="Login.html";})
}else{ console.log("usres exsists / etc");}
})
.catch((error) => {
console.log("something went wrong");
});
}
Also in the request payload you sent a group of strings not the variables containing the form values
Here
var data = {
"user_id":"user_id.value",
"user_pin": "user_pin.value",
"first_name":"first_name.value",
"last_name":"last_name.value",
"address":"address.value",
};
Change that to
var data = {
"user_id":user_id.value,
"user_pin": user_pin.value,
"first_name":first_name.value,
"last_name":last_name.value,
"address":address.value
};
Looking at the fetch documentation (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch), success and error does not exists.
The fetch function returns a Promise, so you can handle results as any Promise should do:
fetch("jehvah/api", {
method: 'POST',
body : JSON.stringify(myDataObject)
})
.then(blob => blob .json())
.then(result => console.log('Success!!))
.catch(e => console.log('Failure :('))

Password stored in google chrome's browser memory

When I login and logout from my application, my username and password are stored into chrome browser memory. When I create a dump file from the task manager for that particular process Id and open that file in WinHex tool and search for username or password field I'm able to see my password in clear text and now I want to encrypt or clear that password field.
function onLogin(btnName) {
var parameters = getFormValues();
//if (!validateParameter(parameters.userName, parameters.password))
// return;
$.ajaxSetup({
beforeSend: function (xhr) {
xhr.setRequestHeader(parameters.antiForgeryTokenName, parameters.antiForgeryToken);
}
});
var getSecuritySettingsUrl = getVirtualDirectoryUpdatedURL("/login/GetSecuritySettings");
$.ajax({
url: getSecuritySettingsUrl,
type: "GET",
contentType: 'application/json; charset=utf-8',
success: function (result) {
try {
var response;
if (result.IsHashed) {
var decryptedData = decryptWithDefaultSetting(result.viewData);
if (decryptedData.isError) {
alert(decryptedData.result);
return;
}
response = JSON.parse(decryptedData.result);
}
else {
response = JSON.parse(result.viewData);
}
if (response.IsPasswordHashed) {
if (isNullOrUndefined(response.SaltText)) {
throw new Error("Please refresh the page and try again");
}
encriptPass = encryptByInputKey(parameters.form["Password"].value, response.SaltText).result;
}
$('#btnType').val(btnName);
$('form input[name="Password"]').val(encriptPass);
$('#loginForm').submit();
} catch (error) {
console.log(error);
if (!isNullOrUndefined(error)) {
if (!isNullOrUndefined(error.message)) {
alert(error.message);
}
else if (!isNullOrUndefined(error.Message)) {
alert(error.Message);
}
else {
alert("Some error has occurred. Please refresh the page and try again");
}
}
}
},
error: function (xhr, textStatus, error) {
console.log(xhr);
alert("Please refresh the page and try again : " + xhr.statusText);
}
});
}
I have solved this problem earlier by salting and hashing the user entered password in the UI and passing both the salted value and hash to server for authentication. The same salt has to be applied during the server side password validation.

Returning an exception with PHP to javascript FETCH API

I am currently coding with javascript and I've hit a wall.
Javascript FETCH api to POST is this:
('form') is my form #ID in html.
fetch(URL_TO_POST, {
method: 'post',
body: new FormData(document.querySelector('form'))
}).then(response => {
console.log(response);
});
php file:
if( !isset($_POST['email'])){
throw new exception (blabla);
}
URL_TO_POST is like a "validation.php" and inside the code I retrieve some POST informations and do some stuff there. The problem is that I can't throw any exception to the javascript response! Any thoughts on that? Note: I'm not using ajax to post because I'm trying to stay simple and clean with only js code.
Thanks!
In your JavaScript you could use the reject handler to handle invalid data sent to the server:
fetch(
URL_TO_POST,
{
method: 'post',
body: new FormData(document.querySelector('form'))
}
)
.then(
function(response) {
//https://httpstatuses.com/422
if (response.status === 422) {
return Promise.reject(response.json());
}
//check for other things that could have gone wrong
return response.json();
}
).then(
function(json) {
console.log("received success json",json)
}
,function(json) {
console.log("received reject json",json)
}
)
Not clear what you use for PHP but you can provide status code of 422 with some json:
http_response_code(422);
echo json_encode(array("error" => "missing field", "field" => "email"));

Destroy a "User" from parse sdk with javascript

i need to delete a user form parse sdk with javascript, i tried loading the user query and then calling the destroy() but it gives me:
[HTTP/1.1 400 Bad Request]
my code is here
var query = new Parse.Query("User");
query.equalTo("email", 'wathmal#hotmail.com');
query.find().then(function(results) {
console.log(results[0]);
results[0].destroy();
});
this won't destroy the user. can anybody help?
with the help of Bjorn's answer i figured out a way doing it. i had to use REST api of parse sdk and generate a DELETE request with a proper session key of the user.
var CurrentUser = Parse.User.current();
console.log(CurrentUser);
var sessiontoken;
Parse.User.logIn(CurrentUser.attributes.username, document.getElementById("curpassword").value, {
success: function (user) {
user.set("StayLoggedIn", "false");
console.log(user._sessionToken);
sessiontoken = user._sessionToken;
user.save();
$.ajax({
url: 'https://api.parse.com/1/users/' + user.id,
type: 'DELETE',
headers: {'X-Parse-Application-Id': APP_ID, 'X-Parse-REST-API-Key': REST_KEY, 'X-Parse-Session-Token': sessiontoken},
success: function (result) {
// Do something with the result
alert("you have successfully deleted your account.");
Parse.User.logOut();
window.location.href = "index.html";
}
});
// location.reload();
},
error: function (user, error) {
//alert(error);
alert("incorrect username or password");
}
});

Categories