Multiple Issuers for common-endpoint - passport-azure-ad

According to the passport-azure-ad documentation, I should be able to specify a list of issuers as a string array.
However, I'm having trouble getting the second example below to work.
The first example works fine:
Example 1 - Works with a token issued by <TENANT_1_GUID>
{
identityMetadata: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
issuer: [
'https://login.microsoftonline.com/<TENANT_1_GUID>/v2.0'
],
clientID: '<APP_GUID>',
validateIssuer: true,
}
Example 2 - Does not work with a token issued by <TENANT_1_GUID> or <TENANT_2_GUID>
But my problem is that that the token is not successfully validated when a list of acceptable issuers is provided:-
{
identityMetadata: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
issuer: [
'https://login.microsoftonline.com/<TENANT_1_GUID>/v2.0',
'https://login.microsoftonline.com/<TENTANT_2_GUID>/v2.0'
],
clientID: '<APP_GUID>',
validateIssuer: true,
}
Is this not the correct way to validate a token that could have come between one of two tenants?
Thanks!

I've come up with something that works... created a middlewear with the passport in it:-
const jwt = require('jsonwebtoken');
const passport = require('passport');
const BearerStrategy = require('passport-azure-ad').BearerStrategy;
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
decodedToken = jwt.decode(token);
let issuer;
const iss_company1 = "https://login.microsoftonline.com/<TENANT_1_GUID>/v2.0";
const iss_company2 = "https://login.microsoftonline.com/<TENANT_2_GUID>/v2.0";
switch (decodedToken.iss) {
case iss_company1:
issuer = iss_company1;
break;
case iss_company2:
issuer = iss_company2;
break;
default:
issuer = iss_company1;
}
let passportOptions = {
identityMetadata: process.env.passport_identityMetadata,
issuer,
clientID: process.env.passport_clientID,
passReqToCallback: process.env.passport_passReqToCallback,
loggingLevel: "error",
loggingNoPII: true
};
let bearerStrategy = new BearerStrategy(passportOptions,
function(token, done) {
return done(null, token);
}
);
passport.use(bearerStrategy);
passport.authenticate('oauth-bearer', { session: false })(req, res, next);
} catch (error) {
res.status(401).json({ message: "Unauthorized" });
}
};

Related

Express-jwt is not returning any response

I'm trying to create a Login functionality using express-jwt, and using the middleware function in my app.js file. But whenever I'm trying to send a get request using the postman, it sending request for infinite of time and never returns back any error or success message.
I'm using dynamoDB as database.
here's my Login.js file
const AWS = require("aws-sdk");
const express = require("express");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
require("dotenv").config();
AWS.config.update({ region: "us-east-2" });
const docClient = new AWS.DynamoDB.DocumentClient();
const router = express.Router();
router.post("/login", (req, res) => {
user_type = "customer";
const email = req.body.email;
docClient.get(
{
TableName: "users",
Key: {
user_type,
email,
},
},
(err, data) => {
if (err) {
res.send("Invalid username or password");
} else {
if (data && bcrypt.compareSync(req.body.password, data.Item.password)) {
const token = jwt.sign(
{
email: data.Item.email,
},
process.env.SECRET,
{ expiresIn: "1d" }
);
res.status(200).send({ user: data.Item.email, token: token });
} else {
res.status(400).send("Password is wrong");
}
}
}
);
});
module.exports = router;
Here's my jwt.js file:
const expressJwt = require("express-jwt");
require("dotenv").config();
function authJwt() {
const secret = process.env.SECRET;
return expressJwt({
secret,
algorithms: ["HS256"],
});
}
module.exports = authJwt;
And I'm trying to use the expressJwt like this in my app.js file:
app.use(authJwt); //If I'm not using this, then the code works fine without API protection
Can Anyone tell me what's wrong with my code?
Any help from your side is appreciated.
Remove function from your jwt.js ,it should look like this
const expressJwt = require('express-jwt');
const secret = process.env.secret
const authJwt = expressJwt({
secret,
algorithms:['HS256']
})
module.exports = authJwt;

JWT Token doesn't expire after a certain time

Why my jwt token doesn't expire after 1 hour?
I've noticed that it doesn't expire when I forgot to logout my account in my admin panel that I created in vuejs with vuex.
here is my API that I created in ExpressJS using bcrypt and express-jwt for token.
router.post('/login', (req, res) => {
let sql = "SELECT * FROM AUTHENTICATION WHERE email = ?";
myDB.query(sql, [req.body.email, req.body.password], function (err, results) {
if (err) {
console.log(err);
} else {
if (!results) {
res.status(404).send('No user found.')
} else {
try {
let passwordMatched = bcrypt.compareSync(req.body.password, results[0].password);
if (passwordMatched) {
// Passwords match
let token = jwt.sign({ id: results.id }, config.secret, {
expiresIn: '1h'
});
res.status(200).send({ auth: true, token: token, user: results });
} else {
//Password doesn't match
return res.status(401).send({ auth: false, token: null });
}
} catch (error) {
res.send({ Success: false })
}
}
}
})
});
here's my login in vuex where I received the token from my backend.
import axios from 'axios';
const state = {
status: '',
token: localStorage.getItem('token') || '',
user: {}
};
const getters = {
isLoggedIn: state => !!state.token,
authStatus: state => state.status,
};
const mutations = {
auth_request(state) {
state.status = 'loading'
},
auth_success(state, token, user) {
state.status = 'success'
state.token = token
state.user = user
},
auth_error(state) {
state.status = 'error'
},
logout(state) {
state.status = ''
state.token = ''
},
};
const actions = {
login({ commit }, user) {
return new Promise((resolve, reject) => {
commit('auth_request')
axios({ url: 'http://localhost:9001/login/login', data: user, method: 'POST' })
.then(resp => {
const token = resp.data.token
const user = resp.data.user
localStorage.setItem('token', token)
// Add the following line:
axios.defaults.headers.common['Authorization'] = token
commit('auth_success', token, user)
resolve(resp)
})
.catch(err => {
commit('auth_error')
localStorage.removeItem('token')
reject(err)
})
})
}
};
EDIT: Added vuejs code for login
thanks for the help guys!
Your JWT token is just an encoded + signed JSON with relevant fields such as expiresIn, iat.
While it may contain the expiresIn field, it does not mean that the backend server will honour it.
Logic needs to be written in the backend server to parse the timestamp, do comparison with the current time to determine whether it has expired. If it is expired, the backend should return a response code of 401 Unauthorized to tell the frontend (your Vue client) that the token is no longer valid.
What you can do is to put the expiry-checking logic in a middleware to look into the request headers' Authorization field.

OpenId issue for authentication

I have an embarassing issue with cognito.
My authentication strategy works with current usage but when I try to run tests that sign up a new user and then log it in for an access to other APIs in my website
const authenticationData = {
Username: req.body.email,
Password: req.body.password,
};
const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
const poolData = {
UserPoolId: config.development.UserPoolId,
ClientId: config.development.ClientId,
TokenScopesArray : config.development.TokenScopesArray
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
const userData = {
Username: req.body.email,
Pool: userPool,
TokenScopesArray : config.development.TokenScopesArray
};
const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('success')
token = result.getAccessToken().jwtToken;
const idToken = result.idToken.jwtToken;
console.log(token)
res.cookie("accessToken",token)
res.status(200).send(token);
},
onFailure: function (err) {
console.log(err)
res.status(404).send(err)
},`
Then when I try to authenticate with the following code :
app.use(function (req, res, next) {
var token = req.body.token || req.query.token || req.cookies.accessToken || req.headers['x-access-token'];
try {
if (token) {
let promise = new Promise((resolve, reject) => {
const data = null;
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log('response', this.responseText);
}
})
xhr.open("GET", "https://gridmanager.auth.us-east-1.amazoncognito.com/oauth2/userInfo");
xhr.setRequestHeader("Authorization", "Bearer " + token);
xhr.setRequestHeader("cache-control", "no-cache");
xhr.setRequestHeader("TokenScopesArray", config.development.TokenScopesArray)
xhr.send(data);
resolve(xhr.responseText)
})
.then(function (response) {
if (response != null) {
res.decoded = response
next();
}
else {
return res.status(404).send('User not authenticated')
}
})
}
else {
console.log('No token')
return res.status(403).send('No token')
}
} catch (error) {
// if there is no token
// return an error
console.log('error')
return res.status(403).send({
success: false,
message: error.message
});
}
I get the following error in xhr.responseText :
{"error":"invalid_token","error_description":"Access token does not contain openid scope"}
And when I log the accessToken I get in the login function, it only has 'aws.cognito.signin.user.admin'
I already tried to change the settings in my appclient but nothing works
Thanks for your help
Unfortunately, only access tokens issued by the Cognito hosted UI can include scopes other than aws.cognito.signin.user.admin. Cognito hosted UI supports OpenId Connect and Cognito API doesn't. It's a big gap in terms of functionality provided by those two. The /oauth2/userInfo endpoint is part of the Hosted UI and it also follows the OpenID Connect spec.
Why do you want to call the /oauth2/userInfo endpoint when you have access to the id_token? The id_token payload has all the information about the user that /oauth2/userInfo would return.

ExpressJS - JWT and Passport Implementation

I'm currently trying to learn JWT and Passport for ExpressJS while trying them out but I can't seem to grasp the idea of how Passport works.
Here's what I have done initially in my ExpressJS application.
/api/login POST API
Accepts username and password
/api/login then creates a JWT with the username and password as the payload
The token is then responded to the client
I think my /api/login API simulates the general idea of JWT of hard authenticate once and respond with token.
/api/test GET API on the other hand
Only authenticated users can access
Simply returns "Hello World!"
What is the problem/s?
My code doesn't seem to validate the token (I tried putting the token in Authenticate header.
Where in my request should I include the token returned from /api/login when requesting to /api/test?
Now to my actual code:
app.js
var express = require("express");
var bodyParser = require("body-parser");
var jwt = require("jsonwebtoken");
var passport = require("passport");
var LocalStrategy = require('passport-local').Strategy;
var mySecret = "mySecret";
var app = express();
var port = process.env.PORT || 3000;
app.use(bodyParser.json());
passport.use(new LocalStrategy(
function (token, done) {
var credentials = jwt.verify(token, mySecret);
if (credentials.username == "test" && credentials.password == "test") {
return done(null, credentials);
} else {
return done(null, false);
}
}
));
app.use(passport.initialize());
app.post("/api/login", function (request, response) {
var user = {
"username": request.body.username,
"password": request.body.password
};
response.send(jwt.sign(user, "mySecret"));
});
app.get("/api/test", passport.authenticate("local", {
"session": false
}), function (request, response) {
response.send("Hello World!");
});
app.listen(port, function () {
console.log("Listening on port: " + port);
});
You need to configure jwtStratagy also to authenticate the user.
here is working example: -
const express = require("express");
const bodyParser = require("body-parser");
const jwt = require("jsonwebtoken");
console.log(jwt.verify);
const passport = require("passport"),
LocalStrategy = require("passport-local").Strategy;
const cors = require("cors");
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(passport.initialize());
var secret = '11210646';
var JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true
},
function(req, username, password, done) {
console.log('ohh', username, password);
let err = null;
if (err) { return done(err); }
if (username != 'abhi') {
return done(null, false, { message: 'Incorrect username.' });
}
if (password != 'pass') {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, username);
}
));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
console.log(err, user, info);
if (err) { return next(err); }
if (!user) { res.send({ "status": info.message }); }
res.send({ "status": user });
})(req, res, next);
});
var opts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: secret,
issuer: 'jonu',
audience: 'jonu bhai',
passReqToCallback: false
};
app.post('/me2', function(req, res, next) {
passport.authenticate('jwt', { session: false }, function(err,user, info) {
if (err) { return next(err); }
if (!user) { res.send({ "status": info.message }); }
res.send({ "status": user });
})(req, res, next);
});
//jwt
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
let err = null;
if (err) {
return done(err, false);
}
if (jwt_payload) {
return done(null, jwt_payload);
}
else {
return done(null, false);
// or you could create a new account
}
}));
app.post('/signup', (req, res) => {
let token = jwt.sign({
user: {
id: "idididid",
name: "Abhishek Singh",
username: "abhishek11210646"
}
},
secret, {
algorithm: 'HS256',
expiresIn: '5h',
issuer: 'jonu',
audience: 'jonu bhai'
});
res.send({ "token": token });
});
app.get('/', (req, res) => {
res.send({ "status": "Up and Running..." });
});
app.listen(8080, () => {
console.log('server running');
});

Getting "error": "Unknown authentication strategy \"jwt\""

I'm implementing an authorization feature using Express, Mongoose, Passport and JWT.
I'm able to register a user ok. I'm able to authenticate and generate a JWT, which I can parse on the JWT site, but for some reason, I'm getting an Unknown authentication strategy error message.
I have all my code blocks laid out on a Plunker instance at:
https://plnkr.co/edit/ZNjQwcZ4rMymzBXNy5nX?p=catalogue
Here is my passport.js file, which contains my strategy:
var JwtStrategy = require('passport-jwt').Strategy;
// load up the user model
var User = require('../models/user');
var config = require('../config/database'); // get db config file
module.exports = function(passport) {
var opts = {};
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Here is what my authentication.js file looks like:
var express = require('express');
var router = express.Router();
var jwt = require('jwt-simple');
var config = require('../config/database');
var User = require('../models/user');
router.route('/')
.post(function(req, res) {
User.findOne({
name: req.body.name
}, function(err, user) {
if (err)
res.send(err);
if (!user) {
res.send({success: false, msg: 'Authentication failed. User not found.'});
} else {
// check if password matches
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// if user is found and password is right create a token
var token = jwt.encode(user, config.secret);
// return the information including token as JSON
res.json({success: true, token: 'JWT ' + token});
} else {
res.send({success: false, msg: 'Authentication failed. Wrong password.'});
}
});
}
});
});
module.exports = router;
Here is the endpoint I'm calling that is generating the error:
var express = require('express');
var router = express.Router();
var jwt = require('jwt-simple');
var config = require('../config/database');
var passport = require('passport');
var User = require('../models/user');
router.route('/')
.get(passport.authenticate('jwt', { session: false}), function(req, res) {
var token = getToken(req.headers);
if (token) {
var decoded = jwt.decode(token, config.secret);
User.findOne({
name: decoded.name
}, function(err, user) {
if (err) throw err;
if (!user) {
return res.status(403).send({success: false, msg: 'Authentication failed. User not found.'});
} else {
res.json({success: true, msg: 'Welcome in the member area ' + user.name + '!'});
}
});
} else {
return res.status(403).send({success: false, msg: 'No token provided.'});
}
});
getToken = function (headers) {
if (headers && headers.authorization) {
var parted = headers.authorization.split(' ');
if (parted.length === 2) {
return parted[1];
} else {
return null;
}
} else {
return null;
}
};
module.exports = router;
You forgot to include your own passport.js module in the application. This leads nodejs to not find the definition of JWTStrategy which is ultimately causing the error that you see.
In your endpoint file, just include the local passport.js file:
var express = require('express');
var router = express.Router();
var jwt = require('jwt-simple');
var config = require('../config/database');
var passport = require('passport');
require('./passport')(passport) // as strategy in ./passport.js needs passport object
var User = require('../models/user');
router.route('/')
.get(passport.authenticate('jwt', { session: false}), function(req, res) {
var token = getToken(req.headers);
...
if you look at your passport configuration file (passport.js) you will see
module.exports = function (passport) {
//bla bla bla
}
as you see it needs passport instance
now how to pass this instance to your passport.js file
simply
var passport = require('passport');// create a passport instance
var myPassportService = require('../config/passport')(passport);// pass it into passport.js file
hope this helps you
Just on the off chance that someone is having the same issue but it isn't resolved by the top answer.
My issue ended up being accidently moving node modules into another directory and then of course running npm install to fix module import errors. I ended up with two node_modules directory and although the server started fine, passport errored when it was called.
I eventually found the blunder and removed the second node_modules directory and the Unknown authentication strategy “jwt” was no resolved.