We have a front end react app that has MSAL login that uses aws api gateway api endpoints for backend operations. After the login process I would like to send the bearer Token to the api and get it authenticated. From what I have read online you do this by using node.js and passport.authenticate(). Problem is trying to make this work in aws api gateway. So I came up with a scheme to do the authentication through a Authorizer lambda. 1) Use aws-serverless-express to run express as api proxy in lambda 2) Use passport-azure-ad module to set bearerstrategy. 3) run passport.authenticate() to authenticate token. 4) upon valid token return allow policy from lambda 5) The api request will continue on
I just placed the following files and required node modules into a node.js lambda. So this essentially is a middlewear between client and api. If token is valid will allow request to go through.
During my tests I am getting this error when it applies the bearerstrategy:
{ "name": "AzureAD: Bearer Strategy", "hostname": "169.254.43.173", "pid": 8, "level": 30, "msg": "authentication failed due to: token is not found", "time": "2020-08-24T23:48:35.497Z", "v": 0 }
Would be great if there is a simpler way to authenticate a Bearer token.
Lambda index.js file:
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./app')
const server = awsServerlessExpress.createServer(app)
exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context)
config.js
'use strict';
const config = {
identityMetadata: "https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration",
clientID: "xxxxxxxxxxxxxxxxxxxx",
validateIssuer: true,
loggingLevel: 'info',
passReqToCallback: false,
ignoreExpiration: true
};
module.exports = config
app.js file
'use strict'
const express = require('express')
const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')
const passport = require("passport");
const config = require('./config');
const BearerStrategy = require('passport-azure-ad').BearerStrategy;
const bearerStrategy = new BearerStrategy(config, (token, done) => {
// Send user info using the second argument
done(null, {}, token);
}
);
const app = express();
app.use(passport.initialize());
const router = express.Router()
passport.use(bearerStrategy);
router.use(awsServerlessExpressMiddleware.eventContext())
var generatePolicy = function(effect, resource) {
var authResponse = {};
authResponse.principalId = 'user';
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17'; // default version
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke'; // default action
statementOne.Effect = effect;
statementOne.Resource = "*";
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
return authResponse;
}
var generateAllow = function(resource) {
return generatePolicy('Allow', resource);
}
router.get('/', passport.authenticate('oauth-bearer', {session: false}),
(req, res) => {
res.send(generateAllow(req.apiGateway.event.methodArn))
}
);
// The aws-serverless-express library creates a server and listens on a Unix
// Domain Socket for you, so you can remove the usual call to app.listen.
// app.listen(3000)
app.use('/', router)
// Export your express server so you can import it in the lambda function.
module.exports = app