serverless-offline
Version:
Emulate AWS λ and API Gateway locally when developing your Serverless project
95 lines (74 loc) • 3.64 kB
JavaScript
;
const Boom = require('boom');
const createLambdaContext = require('./createLambdaContext');
const functionHelper = require('./functionHelper');
const debugLog = require('./debugLog');
module.exports = function createAuthScheme(authFun, authorizerOptions, funName, endpointPath, options, serverlessLog, servicePath) {
const authFunName = authorizerOptions.name;
if (authorizerOptions.type !== 'TOKEN') {
throw new Error(`Authorizer Type must be TOKEN (λ: ${authFunName})`);
}
const identitySourceMatch = /^method.request.header.(\w+)$/.exec(authorizerOptions.identitySource);
if (!identitySourceMatch || identitySourceMatch.length !== 2) {
throw new Error(`Serverless Offline only supports retrieving tokens from the headers (λ: ${authFunName})`);
}
const identityHeader = identitySourceMatch[1].toLowerCase();
const funOptions = functionHelper.getFunctionOptions(authFun, funName, servicePath);
// Create Auth Scheme
return () => ({
authenticate(request, reply) {
console.log(''); // Just to make things a little pretty
serverlessLog(`Running Authorization function for ${request.method} ${request.path} (λ: ${authFunName})`);
// Get Authorization header
const req = request.raw.req;
const authorization = req.headers[identityHeader];
debugLog(`Retrieved ${identityHeader} header ${authorization}`);
// Create event Object for authFunction
// methodArn is the ARN of the function we are running we are authorizing access to (or not)
// Account ID and API ID are not simulated
const event = {
type: 'TOKEN',
authorizationToken: authorization,
methodArn: `arn:aws:execute-api:${options.region}:<Account id>:<API id>/${options.stage}/${request.method.toUpperCase()}/${endpointPath}`,
};
// Create the Authorization function handler
let handler;
try {
handler = functionHelper.createHandler(funOptions, options);
} catch (err) {
return reply(Boom.badImplementation(null, `Error while loading ${authFunName}`));
}
// Creat the Lambda Context for the Auth function
const lambdaContext = createLambdaContext(authFun, (err, result) => {
// Return an unauthorized response
const onError = (error) => {
serverlessLog(`Authorization function returned an error response: (λ: ${authFunName})`, error);
return reply(Boom.unauthorized('Unauthorized'));
};
if (err) {
return onError(err);
}
const onSuccess = (policy) => {
// Validate that the policy document has the principalId set
if (!policy.principalId) {
serverlessLog(`Authorization response did not include a principalId: (λ: ${authFunName})`, err);
return reply(Boom.forbidden('No principalId set on the Response'));
}
serverlessLog(`Authorization function returned a successful response: (λ: ${authFunName})`, policy);
// Set the credentials for the rest of the pipeline
return reply.continue({ credentials: { user: policy.principalId } });
};
if (result && typeof result.then === 'function' && typeof result.catch === 'function') {
debugLog('Auth function returned a promise');
result.then(onSuccess).catch(onError);
} else if (result instanceof Error) {
onError(result);
} else {
onSuccess(result);
}
});
// Execute the Authorization Function
handler(event, lambdaContext, lambdaContext.done);
},
});
};