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.
Related
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
}
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";
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')));
I have registered my app in Oauth in github
//#router GET api/profile/github/:githubUsername
//#desc get github profile
//#access public
router.get('/github/:githubUsername', (req,res)=>{
try {
const options = {
uri:`https://api.github.com/users/${req.params.githubUsername}
/repos?per_page=5&sort=created:asc&client_id=${config.get('githubClientId')}
&client_secret=${config.get('githubSecretKey')}`,
method: 'GET',
headers: { 'user-agent':'node.js' }
};
request(options, (err, response, body)=>{
if(err) console.error(err)
if(response.statusCode!==200) return res.status(404).json({msg : 'Github user not found'})
res.status(200).json(JSON.parse(body));
})
} catch (error) {
console.log(error);
res.status(500).send('Server Error');
}
})
This URL is working when used directly in browser.
It is giving status 404. I have checked with clientId and secret key they are working fine. I am also not sure about what URL to be pasted in Callback URL in github OAuth apps.
Thanks in advance.
The url line break was the problem. Thanks!
I am trying to build a web application using Firebase and NodeJS with ejs templates at the client. I want to make sure the user is authenticated before accessing a particular route through a middleware. In this middleware, I verify ID tokens sent through the client and then render the page if they are verified. But when the tokens are verified the app doesn't show the new page i.e. "user-profile.ejs".
<button onclick="sendRequest('/profile')">Profile</button>
//The client function which sends ID token
function sendRequest(url){
if(firebase.auth().currentUser){
firebase.auth().currentUser.getIdToken(true)
.then(idToken => {
client({
method: 'get',
url: url,
headers: {
'authtoken': idToken
}
})
.then(res => {
console.log("Auth token sent.")
})
.catch(err => {
console.log(err)
})
})
.catch(err => {
console.log(err)
})
}else{
client({
method: 'get',
url: url,
}).then(res => {
console.log("Request sent without header.")
})
.catch(err => {
console.log(err)
})
}
}
//The NodeJS server which contains the routes and checkAuth middleware
function checkAuth(req,res,next){
if(req.headers.authtoken){
console.log('Auth token with headers received. User has logged in.')
admin.auth().verifyIdToken(req.headers.authtoken)
.then(verifiedUser => {
console.log('Auth token verified')
return next()
})
.catch(err => {
console.log(err)
res.redirect('back')
})
}else{
console.log('Without header received.')
res.redirect('back')
}
}
app.get('/',(req,res)=>{
res.render('home')
})
app.get('/profile',checkAuth,(req,res)=>{
console.log("Reached user profile")
res.send("user-profile")
})
Remember that when you redirect, the client get the redirected URL and issues an entirely new http request. That starts a completely new request cycle on your server that will go through all your middleware again that matches that new URL.
Automatic client redirects do NOT include custom headers from the original response. If that's what you were expecting, that will not work. If you're following a redirect manually from an Ajax call, you can manually get the token from the redirect response header and manually add it to a new Ajax call to the redirected location.
If you're expecting the browser or some automatic redirect to handle the redirect, they won't take the token with them from the headers. So, your server will have to either send the response to the original URL without redirecting or you will have to put the token in something that will survive the redirect such as a query parameter on the redirect (usually a bad idea for a credential) or in a cookie or in a server-side session object that is tied to a cookie.