How can I email links work on the front end? - javascript

I have built some functionality into my API that sends a verification email to a user and then when a get request is made to the route the user then becomes active and can be used to login. In order to make this work on the front end I have used a res.redirect which takes me to the login page.
The problem, however, is that this means that when I want to replicate this journey in postman I then receive a load of html rather than a formatted json response. So my question is, is there a way that we can handle this request so that, depending on where it is called, a different response is sent back? As I do not think an event listener will work in the case of an email.
For context, my application uses nodejs, mongodb and pug templates.
`exports.signup = catchAsync(async (req, res, next) => {
const { token } = req.params;
const { email } = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findOne({ email });
if (!user || !((await user.signUpId) === token)) {
return next(new AppError('Invalid link', 401));
}
user.ready = true;
user.singUpId = undefined;
await user.save({ validateBeforeSave: false });
const url = `${req.protocol}://${req.get('host')}/me`;
await new Email(user, url).sendWelcome();
await res.redirect('http://localhost:5000/login');
});`

Related

Req.params and req.user missing after hitting stripe checkout flow

I'm trying to integrate Stripe subscriptions billing in my app with a tiered pricing model and based on my understanding, I need to do two things:
Allow my new users to also create a stripe customer account (via the integration)
Monitor Stripe webhook 'events' to provision access while customer subscription payments are active
My userflow is as follows:
create profile in my app (saved to database) -> redirected to stripe checkout portal for billing info (saved to stripe database) -> attempt to save stripe customerId to my database so I can monitor subscription status
However, I can't figure out how to save the customerId info in my app because req.user and req.params are empty as the users are sent back to my app from the stripe billing portal
Controller function
module.exports.stripeWebhook = async (req, res) => {
let data;
const webhookSecret = stripeWebhookSecret;
if (webhookSecret) {
let event;
let signature = req.headers["stripe-signature"];
try {
event = stripe.webhooks.constructEvent(
req.body,
signature,
webhookSecret
);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed.`);
return res.sendStatus(400);
}
data = event.data;
eventType = event.type;
} else {
// retrieve the event data directly from the request body.
data = req.body.data;
eventType = req.body.type;
}
switch (eventType) {
case 'payment_intent.succeeded': {
console.log('PaymentIntent was successful!');
break;
}
case 'checkout.session.completed':
// Payment is successful and the subscription is created.
// You should provision the subscription and save the customer ID to your database.
console.log(data.object.customer); <- works
const user = await User.findById(req.user.id); <- comes back empty so my next two lines of code don't work
user.stripeId.push(data.object.customer);
await user.save();
break;
default:
}
res.sendStatus(200);
};
App.js
app.use(bodyParser.raw({type: "application/json"}));
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ extended: true }));
I included the app.js code because the bodyparser.raw has an impact on how the body comes through in my controller function.
I was counting on the req.user or req.params to find the user in my database but it's not working. How do I save the customerId to my database like the stripe comments suggest?
You should create Stripe customer account before creating checkout session for the customer.
Check if customer already have stripe_customer account (Stripe customer account). If yes, use that one. If not, create one for him and save it in database.
Set stripe_customer for the checkout session, so customer will be automatically authenticated in the Stripe checkout
You can optionally put user's _id in the metadata of the Stripe checkout session, so you access that data later in the webhook.
Note: You should create one stripe_customer account for each currency. So one user can have multiple stripe_customer accounts, one for each currency.
router.post('/create-checkout-session', authenticate.verifyUser, async (req, res) => {
const { currency } = req.body;
...
// If user does not have stripe customer for order currency, create a new one.
if (!(req.user.stripe_customer && req.user.stripe_customer[currency])) {
const new_stripe_customer = await stripe.customers.create({
email: req.user.email,
metadata: {
user_id: req.user._id.toString(),
},
});
let update = {};
update[`stripe_customer.${currency}`] = new_stripe_customer.id;
await Users.findByIdAndUpdate(req.user._id, update);
if (!req.user.stripe_customer) {
req.user.stripe_customer = {};
req.user.stripe_customer[currency] = new_stripe_customer.id;
} else {
req.user.stripe_customer[currency] = new_stripe_customer.id;
}
}
...
// Set `stripe_customer` for checkout session.
const session = await stripe.checkout.sessions.create({
customer: req.user.stripe_customer[currency],
...
});
...
}

Having issues with app.post, results in Cannot Get /

I'm trying to create a sign-up page where users can enter information, however, I'm having issues with "app.post" properly working. This is what I have so far:
const express = require("express");
const db = require("./dbConnectExec.js")
const app = express();
app.use(express.json());
app.listen(5000, () => {
console.log('App is running on port 5000');
});
app.get("/hi", (req,res) => {
res.send("Hello world.");
});
app.get("/", (req,res) => {
res.send("API is running.");
});
// app.post();
// app.put();
app.post("/customers", async(req, res) => {
// res.send("/contacts called");
// console.log("request body", req.body)
let nameFirst = req.body.nameFirst;
let nameLast = req.body.nameLast;
let email = req.body.email;
let password = req.body.password;
let emailCheckQuery = `
SELECT customer_email
FROM Customer
WHERE customer_email = '${email}'`;
let existingUser = await db.executeQuery(emailCheckQuery);
console.log("existing user", existingUser);
if(existingUser[0]){return res.status(409).send("duplicate email")};
})
When I attempt to add a user through Postman, for example:
{"nameFirst": "Robert",
"nameLast": "Redford",
"email": "rob#mail.com",
"password": "asdfasdf"}
I end up with "Cannot GET /customers"
You have no GET handler for /customers only a POST handler
In postman you can change your request from a GET to a POST request. Once you do it should hit this route endpoint.
note:
"GET is used for viewing something, without changing it, while POST is used for changing something. For example, a search page should use GET to get data while a form that changes your password should use POST . Essentially GET is used to retrieve remote data, and POST is used to insert/update remote data."

Sending jwt through header response with resolver mutation GraphQL and Appollo server

Today I'v been trying to send a jwt token back to the client via a header.
Sadly I cant get it to work, my current code looks like this.
the resolver/mutation
// log user in
Login: async(parent, args, context, info) =>{
const LoginUser = await user.findOne({username: args.username})
if (LoginUser.password == args.password){
//loginsucces
const token = jwt.sign({id: user.id}, process.env.TOKEN_SECRET);
context.res.header = ("auth", token)
LoginUser.token = token;
return LoginUser;
}else{
return LoginUser;
}
}
app.js
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ res }) => ({
res
})
});
For some reason the code doesnt recognise .header so it wont send the token. Currently i send it via the token field form the user. But that takes up space in my mongodb, and destroys the whole purpose of jwt.
Am I just forgetting a function or not getting it?

Problem maintaining session data with Vue JS and Sails JS

Session information is not being maintained when APIs invoked via the VueJs.
In the sails backend, login action set the user id to the session. Whereas in the sessionAuth policy I am simply checking for the user id in the req session.
module.exports = async function (req, res, next) {
// User is allowed, proceed to the next policy,
// or if this is the last policy, the controller
const id = req.session.userId;
if (id) {
const user = await User.findOne({ id });
req.user = user;
if (user) {
return next();
}
}
// User is not allowed
// (default res.forbidden() behavior can be overridden in `config/403.js`)
return res.forbidden('You are not permitted to perform this action.');
};
The above policy works perfectly when requests are being made from Postmen. I invoke login action, any action invoked after that does have user id set in the session. But if the same sequence is followed in a web application user id is missing from the session even after the successful login.
Login component
axios.post(`${this.$config.baseUrl}/login`, this.user)
.then(res => {
this.$router.push({ path: "/home" });
})
Component accessing secured data
const response = await axios.get(`${baseUrl}/course`
return { courseList: response.data };
}

How do i store jsonwebtoken on cookie for front-end so client can send back the token for auth

I've been struggling to do this for about 6 days...
Everything is working perfectly such as authorization but one problem I had is making authentication.
On my user model (for creating the database schema) I do have a way to generate a token for logged in users or registered.
userSchema.methods.generateAuthToken = function(){
const token = jwt.sign({ _id: this._id }, config.get('jwtPrivateKey'));
return token;
}
So when user post to /login, server will respond with a token:
router.post('/', async (req, res) =>{
// Here i'm validating data and then if everything is right the code under will run.
console.log('logged in as: ' + user.username);
// Here i'm using the function to generateAuthToken().
const token = user.generateAuthToken();
console.log("Token from server: " + token);
// now here is my main problem i would like to use cookies to store it for an hour or so.
// then client can send it back to server for protected route.
res.status(200).send(token);
});
I have made a middleware function for auth (to check the token if you're going through a protected route)
module.exports = function (req, res, next){
// instead of using headers i would like to check for the cookie value if it's the token,
// pass the user in, else Access denied.
// I have no idea how to use cookie parser with middleware functions.
const token = req.header('x-auth-token');
if(!token) return res.status(401).send('Access denied. Sign in or register.');
try{
const decoded = jwt.verify(token, config.get('jwtPrivateKey'));
req.user = decoded;
next();
}
catch(err){
res.status(400).send('Invalid Token!');
}
}
here i'm using the auth middleware function:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
// but it's actually not passing the user in since i haven't done it with cookies.
router.get('/', auth, (req, res) =>{
res.render('index', {});
});
I do know I can do it with localStorage but it's a terrible practice and it would be better to store it on cookies so no one could hack on.
Is there any good approach to solve this problem? I'm kinda lost and lost hope to go back to sessionID (which I don't want to :( ).
After you request on frontend, you need get the response (token) and save on browser using this for example:
fetch('http://your-api-host/login', {
method: 'POST',
body: {
username: "user1",
password: "passworduser"
}
})
.then((res) => res.text((res)))
.then((token) => {
document.cookie = `AUTH_API=${token}`; <-- this save the cookie
})
With this value saved on frontend you need send this information on all requests, it's commum send this value on your HEADER (how you makes), to save on header you need read the value from token and put on header, like this:
const headersTemp = document.cookie.split(';'); // <-- this get all cookies saves and splits them in the array.
const finalHeaders = {};
headersTemp.forEach((header) => { // <-- looping on all cookies
const headerTemp = header.split('='); // <-- split each cookie to get key and value
finalHeaders[headerTemp[0].trim()] = headerTemp[1].trim() // <-- save on object to access using keys.
})
Now you can access all cookies using the key (the same used before), I used the key AUTH_API to save my cookie, let's send the request using fetch api:
fetch('http://your-api-host/route-protected', {
method: 'POST',
headers: {
'x-auth-token': finalHeaders['AUTH_API']
},
})
If you creating your application using libraries how React or any SPA framework, probably you will use tools like Axios, and I recommend uses libraris how This, it's more easy to work with cookies.

Categories