Express Middleware not rendering the view - node.js

I have written an auth middleware , which when fails , I want to render a page instead of sending response to my xhr request . How to do it ?
const jwt = require('jsonwebtoken')
const User = require('../models/users')
const express = require('express')
const auth = async (req, res ,next) => {
try {
//res.render("404")
const token = req.header('Authorization').replace('Bearer ' , '') //token comes from client in the header section named Auth(here)
const data = jwt.verify(token , 'itistechvaulttoken') //this gives back the id of the user and also iat :- issued at , a callback can be attached to
// .verify :-- see jsonwebtoken documentation for details .
const user = await User.findOne({ _id : data._id})
if(!user) {
throw new Error()
}
req.token = token //this is being use mainly for logout route handler
req.user = user
next()
} catch(e) {
res.status(401).render("404")
}
}
module.exports = auth
I have create a form and sending that data to my backend using xhr request to route and from there i am redirecting it to another route where I want to render the view .
My routes :-
router.post('/users' ,async (req, res) => {
console.log("Request Recieved")
const user = new User(req.body)
try {
await user.save()
const token = await user.generateAuthToken()
//console.log(token)
//res.status(201).send({ user , token})
req.accesstoken = token
res.redirect(302 , "/users/me" )
}
catch(e) {
console.log(e)
res.status(400).send("User not created" + e)
}
})
/user/me :-
router.get('/users/me', auth, async (req, res) => {
console.log("in users/me")
res.send(req.user)
})
I know here authentication has to fail, and code under the catch of middleware should run ,where I am trying to render a view but failing .

I guess you want your xhr endpoint code on your server to make your user's browser display an error page of some kind upon auth failure.
To do that you need the Javascript code in your browser to detect an error message in the response to the xhr request and navigate to the error page. Your nodejs app can't force any kind of redirect when responding to an xhr request without cooperation from the browser.

Related

How to check wheter the Azure AD token send by React to Node.js is valid

Hi I have a code from https://github.com/Azure-Samples/ms-identity-javascript-react-spa
I changed it a little bit, so instead calling an Microsoft Graph API endpoint, I call mine endpoint on localhost:7000.
So it basically starts with me logging in (here i did not change enything). Then there is this function which acquires token:
const { instance, accounts } = useMsal();
const [graphData, setData] = useState(null);
function RequestProfileData() {
// Silently acquires an access token which is then attached to a request for MS Graph data
instance
.acquireTokenSilent({
...loginRequest,
account: accounts[0],
})
.then((response) => {
callMyEndpoint(response.accessToken).then((response) =>
setData(response)
);
});
}
it uses function callMyEndpoint which looks like this:
export async function callMyEndpoint(accessToken) {
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;
headers.append("Authorization", bearer);
const options = {
method: "POST",
headers: headers,
};
return fetch("http://localhost:7000/myendpoint", options)
.then((response) => response.json())
.catch((error) => console.log(error)) // if the user is not logged in- catch an error;
}
Now, onto my Node.js backend application where the http://localhost:7000/myendpoint is served.
app.post("/myendpoint", async (req, res) => {
console.log("TOKEN", req.headers.authorization); // it is being printed here, everything seems fine.
// here i would like to check whether the token is valid
// if req.headers.authorization == AZURE_TOKEN?
// How to do this?
});
And now the question is? How to check in backend if the token send from frontend is valid for the user, so only logged users, or users which are added in my app registration in azure can post onto this request?
You can use the libraries such as validate-azure-ad-token or you can write your own logic using jsonwebtoken
Here I have my custom logic for that first you will need client_id , tenat_id and scope name.
I am assuming you already have client and tenant id and for scope name it will be available in the Expose Api tab of your app registration.
Here I have console app which will take your token and try to validate it.
var jwt = require('jsonwebtoken');
var token = 'your Token';
var clientid = '' ;
var tenantid = "" ;
var scope = "";
// Create an audiance variable
var audiance = 'api://'+clientid;
// decoded token
var decodedToken = jwt.decode(token , {complete :true});
if((decodedToken.payload.aud==audi)&&(decodedToken.payload.scp==scope)&&(decodedToken.payload.tid==tenantid))
{
console.log("The token is valid");
}
else
{
console.log("The Token is invalid")
}
Output :

Delete User and Logout that user from all Devices

I wanted to implement a feature in my app. Where an Admin can delete the user. So basically the delete is working fine but somehow i cannot logout the logged in user. Let me explain it more briefly, Suppose there is a User A which is currently using my app and the admin decided to remove that user from the app so they can't no longer access the features of the app. To remove the user i can call an API and delete that user but if i completely delete the user it loses all the access to the API's call coz user with the certain ID isn't available anymore and the app breaks coz the API call will fail for that deleted User. So I was wondering is there anyway to logout the user after admin deletes it.
The Frontend is on ReactJs and Backend is on NodeJs. And i am using JWT for authentication. Any help will be appreciated and if this question isn't clear enough please let me know so i can explain it more.
In backend in every protected route you should verify the token and token should contain user id or email using that you will verify the token. After deleting the user throw error with no user found and in frontend make sure if there are the error no user found then it will delete the JWT token.
What comes into my mind is to put a middleware between your requests and server. By doing so, instead of trying to log out from all devices, we will not allow any action if user does not exist; in this very example, we will prevent the user to delete a place and toast a message on the front end. I will share an example of that, but you need to tweak the code according to your needs.
Http Error Model
class HttpError extends Error {
constructor(message, errorCode) {
super(message);
this.code = errorCode;
}
}
module.exports = HttpError;
Middleware
const HttpError = require('../models/http-error');
module.exports = (req, res, next) => {
try {
// Check user if exists
User.findById(req.userData.userId).exec(function (error, user) {
if (error) {
throw new Error('Authentication failed!');
}
else {
return next();
}
});
}
catch (error) {
return next(new HttpError('Authentication failed!', 403));
}
};
Route
const express = require('express');
const router = express.Router();
const checkAuth = require('../middleware/check-auth');
router.use(checkAuth);
// Put after any routes that you want the user to be logged in
router.delete('/:placeId', placesControllers.deletePlace); //e.x.
...
module.exports = router;
E.x. controller (with MongoDB)
const deletePlace = async (req, res, next) => {
const placeId = req.params.placeId;
let foundPlace;
try {
foundPlace = await Place.findById(placeId).populate('userId').exec();
}
catch (error) {
return next(new HttpError('Could not find the place, please try again', 500));
}
// Delete place
res.status(200).json({message: 'Deleted place'});
};
FRONT END PART
import toastr from 'toastr';
....
try {
const response = await fetch(url, {method, body, headers});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message);
}
}
catch(error) {
// handle the error, user not found
console.log(error.message);
toastr.error(error.message, 'Error', {
closeButton: true,
positionClass: 'toast-top-right',
timeOut: 2000,
extendedTimeOut: 1,
});
}

Error while trying to login with through Steam from node.js

I'm trying to login through steam from my webapp but I'm having an hard time.
This is the code in my backend (I'm using Firebase cloud functions) that let me authenticate my user.
const steam = new SteamAuth({
realm: "https://storm.co.gg", // Site name displayed to users on logon
returnUrl: "http://localhost:5001/stormtestfordota/europe-west1/api/auth/steam/authenticate", // Your return route
apiKey: apiKey // Steam API key
});
let loggedUser = "";
const redirectSteamAuth = async (req, res) => {
loggedUser = req.user.username;
const redirectUrl = await steam.getRedirectUrl();
return res.json(redirectUrl);
}
So this is the first endpoint that the user calls when trying to login to Steam. And it works, so the steamcommunity.com opens without problem.
But when I click login in steamcommunity page I'm prompted with this error
So over the name of my account you can see "ERRORE" that stands for "ERROR"
This is the endpoint that should be called after authentication:
const loginWithSteam = async (req, res) => {
try {
const user = await steam.authenticate(req);
db.collection("users").doc(loggedUser).update({
steamId: user.steamid
})
activeDota2(loggedUser, user.steamid);
return res.redirect("https://storm.co.gg/dashboard/home");
} catch (error) {
console.error(error);
return res.status(401).json(error)
}
}
These are the two endpoints:
app.post("/auth/steam", (req, res, next) => validateFirebaseIdToken(req, res, next), redirectSteamAuth);
app.get("/auth/steam/authenticate", loginWithSteam);
I solved this issue. The problem was in the urls of the steam object AND there was a problem with CORS options, I didn't add the DNS of steamcommunity in origins accepted by CORS.

Using both JWT and Passportjs

I am working on mern stack application where I use both jwt for registration and passportjs for social login. I am currently facing issue of authenticating routes.
Here is the Auth middleware of jwt and passportjs:
const jwt = require("jsonwebtoken")
const Auth = async (req,res,next)=>{
//if(req,headers)
try {
const token = req.headers.authorization.split(" ")[1]
if(!token){
return res.status(404).json({msg:"no token authorization"})
}
let decodeData
decodeData = jwt.verify(token, process.env.JWT_SECRET)
req.userId = decodeData.user.id
next()
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
}
const passportAuth = (req,res,next)=>{
if(!req.user){
return res.status(404).json({msg:"user not loggged in"})
}else{
next()
}
}
module.exports = Auth
module.exports = passportAuth
logged in user can create post but here I have jwt auth and passportauth.
if use both [Auth, passportAuth] in the route it wont be working becuase either user have to logged in with google or simple signup registration. Here is the authenticated route code
route.post("/",[auth,passportAuth], async(req,res)=>{
const post = req.body
try {
const createpost = new Post({
tags:post.tags,
creator:post.creator,
title: post.title,
message: post.message,
selectedFile: post.selectedFile,
user:req.userId
})
await createpost.save()
res.json(createpost)
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
})
So what should I do here?
Maybe instead of returning error responses set some property on the response (something like res.authenticated = true / res.authenticated = false). Then have another middleware which would check if this property is set and only return error response if it's false or not present. You would just have to make sure that this middleware is called always as the last one (I don't remember now how it works in express).
As a side note - 404 response is not found. For the case of missing/bad authentication you should rather use 401.

problems trying to use the Instagram api

I am working with Angular and Node.js with Express.js. I have all day trying to get the posts from my Instagram account but I have not even been able to get the API token. I understand that you first have to make a request to this URL: https://api.instagram.com/oauth/authorize?client_id=[instagram-app-id-lex.europa.eu&redirect_uri=[redirect-uriíritu&scope=[scope-lex.europa.eu&response_type=code&state = {state} (replacing the id and redirect-uri obviously) but I get no response. I already created my app in the instagram api following its documentation and add the tester and accept the authorization from the account settings.
At the moment I have this code en node.js:
const instagram = require('../controllers/instagram/instagram.controller');
const route = '/auth'
module.exports= (app,db, protegerRutas)=> {
app.get(`${route}/instagram`, (req, res)=> instagram.auth(req, res));
app.get(`/handle`, (req,res)=> instagram.handleauth(req,res));
// app.get(`${}`)
// app.post(`${route}/actualizar`, protegerRutas, (req, res)=> actualizar.actualizarDatos(req, res, db))
}
controller:
const Instagram = require('node-instagram').default;
const axios = require('axios');
const clavesInstagram = require('../../config/config').instagram;
const {clientId, clientSecret}= clavesInstagram;
const urlInstagram = 'https://api.instagram.com/oauth/authorize?client_id=201577004938695&redirect_uri=https://localhost:3008/auth/handle&scope={scope}&response_type=code';
const redirectUri = 'https://b4ae544459e3.ngrok.io/handle';
const instagram = new Instagram({
clientId: clientId,
clientSecret: clientSecret
})
module.exports= {
auth: (req,res)=> {
axios.get(urlInstagram)
.then( res => {
console.log('\n\nRespuesta: ',res);
})
.catch( err => {
console.log('\n\nError: ',err);
})
},
// auth: (req, res)=> {
// console.log('\n\nAuth Controller...')
// res.redirect(
// instagram.getAuthorizationUrl(redirectUri, {
// scope: ['basic','likes'],
// state: 'your state'
// })
// )
// },
handleauth: async (req, res)=> {
try {
console.log('\n\n\n\nHandleAuth....\n\n\n\n\n')
const code = req.query.code; //código devuelto por Instagram
const data= await instagram.authorizeUser(code, redirectUri);
console.log('\n\nData Instagram:', data)
res.json(data);
} catch(e) {
// statements
console.log(e);
res.json(e)
}
}
}
I understand that in the instagram API configuration the URL of my app is added to which they redirect and send the token, the url of my local app is something like this: http://localhost:3008/ and the path to which I want instagram to redirect would be this: https://localhost:3008/auth/handle (I must put it with 'https' in instagram because it does not allow http, so I don't know if this would include me). The problem is that with this code I am not getting the token that should be sent to my route https://localhost:3008/auth/handle. What am I doing wrong? I've been at this for many hours and honestly I can't find a solution. I read the documentation and try to do what it says but I can't do it. Could someone help me with this? I would be enormously grateful.
Thanks in advance.

Resources