HttpOnly Cookies not set to browser (client side with laravel) - node.js

NEED YOUR EXPERIENCE AND KNOWLEDGE.
im building a web application where client-side using Laravel 8 (fckid.test using valet or http://localhost:8000) and Node.js (http://localhost:3000) for server-side API.
Serverside using JWT for authorization and sending the token through Httponly Cookie.
My problem is when testing it with Postman and ThunderClient, it works perfectly as shown by picture below
here is on postman
here is how my backend looks like
1.Router
const usersController = require('../controllers/users.controller')
const express = require('express')
const router = express.Router()
router.post('/login', usersController.login)
The Controller
exports.login = (req, res, next) => {
const data = {
email: req.body.email,
password: req.body.password
}
console.log('Request made by :', req.headers)
usersService.login(data, (error, results) => {
if (error) {
console.error(error)
res.status(error.statusCode).send({
msg_head: "error",
msg_body: "Internal Server Error",
data: error
})
} else if (results.status) { //if status = true
console.log(results)
res
.status(results.statusCode)
.cookie("accessToken", results.accessToken, {
httpOnly: true,
path: '/',
sameSite: 'none',
domain: 'fckid.test'
})
.send({
msg_head: "success",
msg_body: results
})
} else {
console.error(results)
res.status(results.statusCode).send({
msg_head: "error",
msg_body: results
})
}
})
}
this is how my laravel client-side looks like
route (im using web.php route)
Route::post('/auth-login', [LoginController::class, 'login'])->name('auth-login');
LoginController
class LoginController extends Controller
{
public function login(Request $request)
{
$body = $request->post();
$response = Http::post('http://localhost:3000/users/login', $body);
if ($response->status() == 200) {
return redirect()->route('view_student');
// return $response;
} else {
return redirect()->route('login.page');
}
}
}
what i already tried are
give exception to this route to VerifyCsrfToken.php
making request from Microsoft Edge, Firefox and Chrome
but those attempts still give me the same result where httponly cookie is not set to the browser.
I try to $request->getHeaders() and it igives me correct response where i taught that the server already send correct information as expected.
on browser it looks like laravel overide the results from server
it's been 2 days looking for some information that can help me to solve this problem on youtube, google, but no one really talk about this. hope you guys could help me out of this stuck.
Thanks

Cannot send "httponly" value as false by PHP after new browser updates.
If you need to use your cookie on the JS side, set your cookie on the client side with document.cookie.
document.cookie = "test1=Hello; SameSite=None; Secure";

Related

"Sign in with Apple" redirects to a blank white page on PWA

I've implemented "Sign in with Apple" on my site. When I try it on my phone, it redirects me to a blank white page with the same URL as the redirect_uri I've configured.
I can't find any info on why this is happening. What's a possible fix?
UPDATE
It seems as if Apple JS SDK is creating a FORM HTML DOM element, sets the POST URL of the FORM to point to the redirect_uri, and finally programmatically clicks form.submit(). This for some reason causes the page to navigate to the redirect_uri and show the POST response as a new page.
I figured this out by tracking the Apple JS SDK in the debugger.
Here is my code
//---- Frontend ----
AppleID.auth.init({
clientId : '<client_id>',
scope : 'email',
redirectURI : 'mySite.com/apple_auth',
state : 'origin:web',
nonce : Date.now(),
//usePopup : true //not using this one. When false or undefined, Apple will make a POST request to the defined redirect_uri
})
// Listen for authorization success.
document.addEventListener('AppleIDSignInOnSuccess', (event) => {
// Handle successful response.
console.log(event.detail.data);
});
// Listen for authorization failures.
document.addEventListener('AppleIDSignInOnFailure', (event) => {
// Handle error.
console.log(event.detail.error);
});
//...
myButton.onClick = ()=>{
try {
var res = await AppleID.auth.signIn()
} catch(err) {
var x = 0
}
}
//---- Backend ----
var appleSignin = require("apple-signin-auth")
app.express.post('/apple_auth', async (req, res)=>{
var body = req.body
try {
const appleRes = await appleSignin.verifyIdToken(
body.id_token, // We need to pass the token that we wish to decode.
{
audience: '<client_id', // client id - The same one we used on the frontend, this is the secret key used for encoding and decoding the token.
ignoreExpiration: true, // Token will not expire unless you manually do so.
}
)
//do something with the Apple response
} catch (err) {
// Token is not verified
console.error(err)
}
})
From the documentation...
The HTTP body contains the result parameters with a content-type of application/x-www-form-urlencoded.
Make sure you've configured the urlencoded() body-parsing middleware in your Express app.
app.use(express.urlencoded());
Make sure you check for errors and actually send a response from your /apple_auth Express route
const { code, id_token, state, user, error } = req.body;
if (error) {
return res.status(500).send(error);
}
try {
const appleRes = await appleSignin.verifyIdToken(id_token, {
audience: "<client_id>",
ignoreExpiration: true,
});
// do something with the Apple response, then send a response
res.send(appleRes.sub);
} catch (err) {
console.error(err);
res.sendStatus(500); // send a 500 response status
}

Reactjs + Express Twitter API "Could not authenticate you."

I am trying to create a very simple app that allows me to post a tweet. I am currently using React running on port 3000 and express server.js running on port 5000
my server.js has the following:
app.post("/twitter/message", async(req, res) => {
const tweet = req.body.tweet;
try {
const response = await postToTwitter(tweet);
res.json({
message: 'Tweet successfully posted to twitter'
});
} catch (error) {
res.status(500).json({
message: 'Not able to post'
});
}
});
function postToTwitter(tweet) {
client.post(
"statuses/update",
{ status: tweet },
function (error, tweet, response) {
if (error) log(error);
/* log(tweet); // Tweet body. */
}
);
}
I am then using a script on the index.html page to post the input tweet:
<script>
$('button').on('click', (event) => {
event.preventDefault();
const tweet = $('#tweet').val();
// Post Tweet
$.ajax({
url: '/twitter/message',
method: 'POST',
data: {
tweet
}
})
.then(() => {
alert('Data successfully posted');
console.log('Data successfully posted');
})
.catch((error) => {
alert('Error: ', error);
console.log('Error: ', error);
});
})
</script>
This however is giving me the bellow error when I hit the post button:
[ { code: 32, message: 'Could not authenticate you.' } ]
If I use this exact same setup with just express it works perfectly fine, the issue occurs when trying to use react. Any help would be amazing.
It is possibly a CORS issue (which would show up in the frontend but not in Node/Backend).
If you're using some sort of API key to make the API request you're not showing it in this sample (don't show people your API key). By similar logic, do not have your API key on the client side, as anyone downloading your website would then have your Twitter API key. Instead, for multiple reasons it is better to have the backend be the one to make the API requests with your API key.
On the other hand if users are supposed to authenticate via O-Auth and you're supposed to pass a cookie with your authentication make sure you useCredentials on the request. axios.post(BASE_URL + '/api', { withCredentials: true }); . Looks like you're using jquery so add the same withCredentials:
Try adding this to your options:
crossDomain: true,
xhrFields: {
withCredentials: true
},
If you don't see a cookie when you type document.cookie in the browser that's probably a sign you're not authenticated in your computer.

CORS Error in a working setup while trying to authenticate using Google+ API in React/Node setup

I was implementing the Oauth2.0 authentication using Google. I used react-google-login npm on the frontend to authenticate the user using Google Oauth2.0. I successfully created the CLient-id and secret under google cloud platform for my project, along with the URI as needed.
The frontend is running on default localhost:3000 and backend (node/express) running on localhost:9001 with proxy enabled on frontend to redirect the request to backend.
I was able to authenticate using Google more than 2 dozen times last night as i was working on the backend siginIn contoller. I was also able to add the user to my Mongodb after successful authentication from Google.
All of a sudden, i was getting CORS error which is a bit strange as none of the code or Google configs were changed.
My Google config looks as follows.
My code on the frontend is still successfully redirecting the user to Google for authentication. Its also generating the right google credentials.
SignIn Component Code snippet passing the info to responseGoogle which resides in withLogin HOC Parent Component.
<GoogleLogin
clientId={GOOGLE_CLIENT_ID}
buttonText="Google"
render={(renderProps) => (
<button onClick={renderProps.onClick} style={customStyle}>
<img className="googleBtn" src={googleIcon} alt="GMAIL ICON" />
</button>
)}
onSuccess={responseGoogle}
onFailure={responseGoogle}
cookiePolicy={"single_host_origin"}
/>
withLogin HOC Parent Component dispatching the info to Redux thunk.
const responseGoogle = (res) => setGoogleResp(res);
useEffect(() => {
googleResp?.error &&
setValues({ ...values, serverError: "GOOGLE LOGIN FAILED" });
googleResp?.tokenId && dispatchGoogleSignInDataToBackend()
}, [googleResp]);
const dispatchGoogleSignInDataToBackend=async ()=>{
const data=await dispatch(allActions.googleSignInAction(googleResp,whoLoggedIn));
if (data.error) {
setValues({ ...values, serverError: data.error, success: false });
} else {
const {
email,
name,
_id,
role,
listOfEmailOfAllClientsForLawyerLogin,
} = data.userCred;
saveJwtToLocalStorage(
data.token,
{ name, email, _id, role, listOfEmailOfAllClientsForLawyerLogin },
() => {
setValues({
email,
serverError: false,
success: true,
});
}
);
}
}
I am sending the appropriate CORS header in the request to the backend.
export const dataHeaders = {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
"Access-Control-Allow-Headers" :"*"
};
Redux thunk code:-
export const googleSignInAction=(googleResp,whoLoggedIn)=>{
console.log("Login Success: currentUser:", googleResp);
return async (dispatch) => {
dispatch({ type: SIGNIN_LOADING });
try {
const response = await axios.post(
`${API_URL}/googlesignin`,
{
googleResp,
whoLoggedIn
},
{
headers: dataHeaders,
}
);
console.log("response inside googleSignInAction", response);
// CHANGED COZ OF ESLINT WARNING.
if (
response.status === 201 &&
Object.keys(response.data).includes("token") &&
Object.keys(response.data).includes("userCred")
) {
dispatch({ type: SIGNIN_SUCCESS, data: response.data });
return response.data;
} else {
dispatch({ type: SIGNIN_FAILED });
}
} catch (error) {
dispatch({ type: SIGNIN_FAILED });
return error.response.data;
}
};
}
API URL Points to following:-
export const API_URL="http://localhost:9001/api";
No request is reaching the backend because of CORS error.
Frontend receiving the Correct Response from Google Post authentication.
Errors on the Frontend.
Browsers will first send a pre-flight request to check CORS. In your backend code, you have to allow the front-end host and port. In this case localhost:3000.
The reason you are getting the cors error is bacause its on two different ports.
But if proper cors response is given by backend (port 9000), it will resolve.
Clearing the browser cookies and cache made everything work again. googlesignin is working without cors error. I have added following line of code to serve all static files from backend to frontend.
app.use(express.static(path.join(__dirname, '../frontend/public')));

My API works fine without passport.authenticate but with it, it gives an error Cannot set headers after they are sent to the client

I'm new to web development and I have been working on a project, I'm using the mern stack here but I have encountered a problem that I have been trying to solve here is a screenshot of the get request I'm sending when I use passport.authenticate I get this error but when I remove it from the get request the API works fine ! how can I make it work with the authentication!
thanks in advance for anybody who is willing to help!
I really appreciate it!
//get all client (private route)
router.get('/clients', passport.authenticate('managerauth', { session: false }), (req, res) => {
const errors = {};
Client.find({}, { username: 1, _id: 0 })
.then(clients => {
if (!clients) {
errors.noclients = 'No clients available';
return res.status(404).json(errors);
}
res.json(clients);
})
.catch(err => res.status(404).json(err));
});

Need assistance with the JWT authenticate process

I am working on a user login project. My front end is React, and my server side is Node/Express js. I am trying to learn about JWT to create protected routes. However, I have gotten stuck when it comes to sending my token (already successfully generated) back to my front end application. I am trying to store the token in a cookie, then send that back to my react side. It is my understanding that I must store the token on the react side before I can access a protected route on my server side, but I am at a loss as to how to proceed to accomplish this. Here is my code for the login post request on my server:
app.post('/userLogin', function(req, res) {
var {usernameLogin, passwordLogin} = req.query;
console.log(req.query);
var validateLogin = `SELECT CustomerID FROM CUSTOMER WHERE Username='${usernameLogin}' AND Password='${passwordLogin}'`;
mysqlConnection.query(validateLogin, (error, result) => {
if(error) {
console.log(error);
} else {
// Checks to see if the result object (CustomerID) exists or not.
if(Object.entries(result).length === 0) {
console.log('sorry');
} else {
console.log(result);
console.log('existing');
const token = jwt.sign({ usernameLogin: usernameLogin}, 'loginKey');
res.cookie('token', token, { httpOnly: true}).sendStatus(200);
console.log(token);
}
}
});
});
and this is my react onSubmit (called login) in my react app. Just as a side note, the "it worked" message successfully is printed to the console, but this is the point at which I do not know how to retrieve and store the token on my client side for future use to access protected routes:
login = (event) => {
event.preventDefault();
console.log(this.state);
fetch('http://localhost:3001/userLogin?usernameLogin=' + this.state.usernameLogin +
'&passwordLogin=' + this.state.passwordLogin, {method: 'POST'}
)
.then(res => {
if (res.status === 200) {
console.log("it worked!!!");
} else {
console.log('there was a problem at line 27');
}
})
};
Any insight would be greatly appreciated!!!
For the client side, you can store the token returned from server side using localStorage property.
Example to set the item: localStorage.setItem("token", <token>);
Example to get the item: let token = localStorage.getItem("token");
Docs: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Local_storage
PS: Let me know if was enough to solve your question.

Resources