I am using JWT token for authentication in my MERN app. when i click on this endpoint "http://127.0.0.1:1000/api/v1/users/login" in postman to login a user and this endpoint "http://127.0.0.1:1000/api/v1/users/verify" to verify a user's token, the token is returned. However, when i do the same operation in my react frontend app, the token returns undefined. Please what am i doing wrong? Here is my React code. P.S the token is stored in the cookies.
function Welcome() {
const [user, setUser] = useState("");
const sendRequest = async () => {
const res = await axios
.get("http://127.0.0.1:1000/api/v1/users/verify", {
withCredentials: true,
})
.catch((err) => console.log(err));
const data = await res.data;
console.log("RESPONSE", data);
return data;
};
React.useEffect(() => {
sendRequest().then((data) => console.log(data));
}, []);
console.log(user);
return <div>Welcome</div>;
}
Here is the Verify token code in my express app
exports.verifyToken = async (req, res, next) => {
const cookies = await req.headers.cookie;
const token = cookies.split("=")[1];
if (!token) {
res.status(404).json({ message: "no token found" });
}
jwt.verify(String(token), process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(400).json({message: "Invalid token" });
}
req.id = user.id;
});
next();
};
User cookie parser
const express = require("express");
const app = express();
const cookieParser = require("cookie-parser");
app.use(express.json());
app.use(cookieParser());
app.get("/", (req, res) => {
res.setHeader("Set-Cookie", "name=langesh;SameSite=None;");
res.send("hello");
});
app.get("/get-cookie", (req, res) => {
res.send(req.cookies);
});
app.listen(9000);
output
{name : "langesh"}
I personally don't have much experience with axios, I prefer fetch() method, it works perfectly.
That said, I think this issue could arise because token isn't identified in the request. Try adding headers: { Authorization: `Bearer ${token}`} to your get method and see if it is fixed.
Your code should look like :
axios.get("http://127.0.0.1:1000/api/v1/users/verify", {
headers: {
Authorization: `Bearer ${token}`
},
withCredentials: true,
})
Related
I'm trying to make a payment component to my react app. This is my first time using stripe. I've tried to follow a tutorial, and I have done exactly the same thing as in the tutorial, but still getting an internal server error or invalid value for stripe.confirmCardPayment (see image below) when posting the request with axios. Hopefully someone can guide me onto the right track with this.
CheckoutForm.js - HandleSubmit function
handleSubmit = async event => {
event.preventDefault();
const { stripe, elements } = this.props;
if (!stripe || !elements) {
return;
}
const card = elements.getElement(CardElement);
const { } = await axios.post("/api/stripe/charge", {
amount: 1000,
})
const paymentMethodReq = await stripe.createPaymentMethod({
type: 'card',
card: card,
billing_details: {
name: 'Daniel Olsen',
email: 'olsen.daniel04#gmail.com'
},
payment_method: {
card: card
}
})
console.log(paymentMethodReq)
axios.get('/api/stripe/charge').then(function(response) {
console.log(response)
return response.json();
}).then(function(responseJson) {
var clientSecret = responseJson.client_secret;
const confirm = await stripe.confirmCardPayment(clientSecret, {
payment_method: paymentMethodReq.paymentMethod
})
});
const result = await stripe.createToken(card);
if (result.error) {
console.log(result.error.message);
} else {
console.log(result.token);
}
};
Stripe.js
const stripe = require('stripe')('secret_key')
async function postCharge(req, res) {
try {
const { amount } = req.body
const charge = await stripe.paymentIntent.create({
amount: 2000,
currency: 'nok',
payment_method_types: ['card'],
})
if (!charge) throw new Error('charge unsuccessful')
res.status(200).json({
message: 'charge posted successfully',
charge
})
} catch (error) {
res.status(500).json({
message: error.message
})
}
}
module.exports = postCharge
server.js
const app = express()
const router = express.Router()
const port = 3000
app.post('/stripe/charge', postCharge)
router.all('*', (_, res) =>
res.json({ message: 'please make a POST request to /stripe/charge' })
)
app.use((_, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
)
next()
})
app.use(bodyParser.json())
app.use('/api', router)
app.use(express.static(path.join(__dirname, '../build')))
app.get('*', (_, res) => {
res.sendFile(path.resolve(__dirname, '../build/index.html'))
})
app.listen(port, () => console.log(`server running on port ${port}`))
Error:
After you create the payment intent you're sending back the entire "charge" [sic] object in the json:
res.status(200).json({
message: 'charge posted successfully',
charge
})
While I'd suggest changing this to only send back the client_secret, the way you have it now the client needs to retrieve responseJson.charge.client_secret. You're missing that charge layer.
I'm trying to setup CSRF tokens so that I can do a number of checks before issueing a token to the client to use in future requests.
Taking the guidance from the csurf documentation, I've setup my express route with the following:
const express = require('express');
const router = express.Router({mergeParams: true});
const csurf = require('csurf');
const bodyParser = require('body-parser');
const parseForm = bodyParser.urlencoded({ extended: false });
const ErrorClass = require('../classes/ErrorClass');
const csrfMiddleware = csurf({
cookie: true
});
router.get('/getCsrfToken', csrfMiddleware, async (req, res) => {
try {
// code for origin checks removed for example
return res.json({'csrfToken': req.csrfToken()});
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
router.post('/', [csrfMiddleware, parseForm], async (req, res) => {
try {
// this returns err.code === 'EBADCSRFTOKEN' when sending in React.js but not Postman
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
For context, the React.js code is as follows, makePostRequest 100% sends the _csrf token back to express in req.body._csrf
try {
const { data } = await makePostRequest(
CONTACT,
{
email: values.email_address,
name: values.full_name,
message: values.message,
_csrf: csrfToken,
},
{ websiteId }
);
} catch (error) {
handleError(error);
actions.setSubmitting(false);
}
Postman endpoint seems to be sending the same data, after loading the /getCsrfToken endpoint and I manually update the _csrf token.
Is there something I'm not doing correctly? I think it may be to do with Node.js's cookie system.
I think your problem is likely to be related to CORS (your dev tools will probably have sent a warning?).
Here's the simplest working back-end and front-end I could make, based on the documentation:
In Back-End (NodeJS with Express) Server:
In app.js:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
const cors = require('cors');
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
var app = express()
const corsOptions = {
origin: "http://localhost:3000",
credentials: true,
}
app.use(cors(corsOptions));
app.use(cookieParser())
app.get('/form', csrfProtection, function (req, res) {
res.json({ csrfToken: req.csrfToken() })
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
module.exports = app;
(make sure you update the corsOptions origin property to whatever your localhost is in React.
In Index.js:
const app = require('./app')
app.set('port', 5000);
app.listen(app.get('port'), () => {
console.log('App running on port', app.get('port'));
});
In React:
Create file "TestCsurf.js" and populate with this code:
import React from 'react'
export default function TestCsurf() {
let domainUrl = `http://localhost:5000`
const [csrfTokenState, setCsrfTokenState] = React.useState('')
const [haveWeReceivedPostResponseState, setHaveWeReceivedPostResponseState] = React.useState("Not yet. No data has been processed.")
async function getCallToForm() {
const url = `/form`;
let fetchGetResponse = await fetch(`${domainUrl}${url}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": localStorage.getItem('xsrf-token'),
},
credentials: "include",
mode: 'cors'
})
let parsedResponse = await fetchGetResponse.json();
setCsrfTokenState(parsedResponse.csrfToken)
}
React.useEffect(() => {
getCallToForm()
}, [])
async function testCsurfClicked() {
const url = `/process`
let fetchPostResponse = await fetch(`${domainUrl}${url}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": csrfTokenState,
},
credentials: "include",
mode: 'cors',
})
let parsedResponse = await fetchPostResponse.text()
setHaveWeReceivedPostResponseState(parsedResponse)
}
return (
<div>
<button onClick={testCsurfClicked}>Test Csurf Post Call</button>
<p>csrfTokenState is: {csrfTokenState}</p>
<p>Have we succesfully navigates csurf with token?: {JSON.stringify(haveWeReceivedPostResponseState)}</p>
</div>
)
}
Import this into your app.js
import CsurfTutorial from './CsurfTutorial';
function App() {
return (
<CsurfTutorial></CsurfTutorial>
);
}
export default App;
That's the simplest solution I can make based on the CSURF documentations example. It's taken me several days to figure this out. I wish they'd give us a bit more direction!
I made a tutorial video in case it's of any help to anyone: https://youtu.be/N5U7KtxvVto
Trying to figure out how to implement this request and response scenario with javascript's fetch() and an express server.
here's the server:
var express = require('express'),
stripeConnect = require('./routes/connect'),
cors = require('cors'),
bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cors());
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true');
next();
});
app.use('/connect', connect);
app.listen(process.env.PORT || 5000);
here's routes/connect:
const express = require('express');
const router = express.Router();
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.cert({
projectId: process.env.projectId,
clientEmail: process.env.clientEmail,
privateKey: process.env.privateKey.replace(/\\n/g, '\n'),
clientId: process.env.clientId
}),
databaseURL: process.env.databaseURL
});
const STRIPE_SK = 'sk_test_KEY';
const stripe = require('stripe')(STRIPE_SK);
// #route POST /stripeConnect/link
// #desc save stripe user account id to their firebase profile
// #access public
router.post('/link', (req, res) => {
console.log('\nLINK-REQUEST-BODY => ');
console.log(req.body);
return admin
.firestore()
.collection('users')
.doc(req.body.docId)
.update({ stripeId: 'test_Id' })
.then((success) => {
console.log('Firestore Update: Success');
res.json({ msg: 'Stripe account ID added to Slide profile.' });
})
.catch((err) => {
console.log('Firestore Update: Fail, Error: ' + err.message);
res.json({ msg });
});
});
module.exports = router;
here's the fetch POST:
function submit() {
$("#progress-label").text("Working...")
const request = {
method: "POST",
body: JSON.stringify({
docId: $('#id').val(),
stripeId: USER_ID
}),
mode: 'cors',
headers: { 'Content-Type': 'application/json'}
}
fetch(SERVER_URL + "/link", request).then(res => {
console.log("res => " + res)
console.log("res.json() => "+ res.json())
console.log("JSON.stringify(res.json()) => "+ JSON.stringify(res.json()))
console.log("res.data => " + res.data)
console.log("res.msg" => + res.msg
}).catch(err => {
document.getElementById("label").innerHTML = res.json()
})
}
The express server logs Firebase Update Success
the front end logs:
res => [object Response]
res.json() => [object Promise]
JSON.stringify(res.json()) => {}
res.data => undefined
res.msg => undefined
Just trying to figure out how to properly get this response from express. Not sure what all of these log-symptoms are telling me. just figured id log all the different ways I could think of handling the response object.
What do I need to do to get the response data?
Your .then() function is just a promise because your receiving it as soon as you get the headers from the request, you need to send the response back (res.send()) in the .then() of the res.json() because it is also a promise. so that modify your routes/connect as per below.
router.post('/link', (req, res) => {
console.log('\nLINK-REQUEST-BODY => ');
console.log(req.body);
return admin
.firestore()
.collection('users')
.doc(req.body.docId)
.update({ stripeId: 'test_Id' })
.then((success) => {
console.log('Firestore Update: Success');
res.json().then(data => ({
data: data,
status: response.status
})
).then(res => {
console.log(res.status, res.data)
})
.catch((err) => {
console.log('Firestore Update: Fail, Error: ' + err.message);
res.json({ msg });
});
});
In first request I'm asking external server to provide a token. And I'm getting it. Then I would like to use it in another request. All is done in express.js. What is the best solution to provide it to the another request?
It looks like this:
const express = require('express');
const axios = require('axios');
const config = require('./config');
const app = express();
axios.post('URL1', {
email: config.email,
password: config.password,
})
.then(function(response) {
console.log(response.data.token); //here I' getting the token
})
.catch(function(error) {
console.log(error);
});
const headers = { headers: { 'Authorization': 'Token ' + token } }; //here I would like to use (for the use of a second request)
axios.get('URL2', headers)
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
const PORT = process.env.PORT || 5000;
app.listen(PORT);
Of course I cannot just assign it to the variable. Thank you for helping!
You can call it in another function just as shown below.
const express = require('express');
const axios = require('axios');
const config = require('./config');
const app = express();
axios.post('URL1', {
email: config.email,
password: config.password,
}).then((response) => {
// calling function here
return handleToken(response.data.token);
console.log(response.data.token); //here I' getting the token
}).catch((error) => {
console.log(error);
});
//second request will be handled here
const handleToken = (token) => {
const headers = { headers: { 'Authorization': 'Token ' + token } };
//here I would like to use (for the use of a second request)
axios.get('URL2', headers)
.then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
}
const PORT = process.env.PORT || 5000;
app.listen(PORT);
It's preferable if you write a separate function to avoid callback hell.
EDIT - ROUTE WITH ASYNC/AWAIT
app.get('/', async (req, res)=>{
try {
let result = await axios.post('URL1', { email: config.email, password: config.password });
let final = await handleToken(response.data.token);
// other operations here
console.log(result);
} catch (err) {
//handle error here
console.error(err);
}
})
//second request will be handled here
const handleToken = async (token) => {
try {
const headers = { headers: { 'Authorization': 'Token ' + token } };
let response = await axios.get('URL2', headers);
return response;
} catch (err) {
throw err;
}
}
Following the example from OAuth2WebServer from google I'm trying to set up an authentication flow from an express app using the HTTP/REST method they have but with every request I am returned with an error
I went through Google OAuth “invalid_grant” nightmare — and how to fix it but unfortunately it did not help.
{
error: "unsupported_grant_type",
error_description: "Invalid grant_type: "
}
This is a shortened version of the error I am receiving. If you need to see more of the error let me know and I can post it.
Server
const express = require('express');
const axios = require('axios');
const { web } = require('./src/client_id.json');
const app = express();
const { client_id, client_secret } = web;
let count = 0;
app.use(express.json());
/*************************
** REDIRECT USER TO GOOGLE AUTH **
*************************/
app.get('/', (req, res) => {
const redirect_uri = 'http://localhost:5000/auth';
const scope = 'https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly';
const access_type = 'offline';
res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?scope=${ scope }&access_type=${ access_type }&redirect_uri=${ redirect_uri }&response_type=code&client_id=${ client_id }`);
});
/*************************
** ON AUTH WE EXCHANGE ACCESS TOKEN FOR REFRESH TOKEN **
*************************/
app.get('/auth', (req, res) => {
count++;
if (count >= 2) {
return res.redirect('http://localhost:3000');
}
const { code } = req.query;
const redirect_uri = 'http://localhost:5000/auth';
const grant_type = 'authorization_code';
axios.post('https://www.googleapis.com/oauth2/v4/token', {
code,
client_id,
client_secret,
redirect_uri,
grant_type
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(data => {
console.log(data)
res.redirect('http://localhost:3000');
})
// ALWAYS HITS THE CATCH "Invalid grant_type"
.catch(err => {
console.log(err);
console.log('ERROR')
});
});
app.listen(5000, console.log('Server listening on port 5000'));