Skip to content

Commit

Permalink
Update for Lambda 8.10 (auth0-samples#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
fyockm authored and mostekcm committed May 31, 2018
1 parent f92d341 commit f9e7f29
Show file tree
Hide file tree
Showing 5 changed files with 1,533 additions and 95 deletions.
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AWS API Gateway Custom Authorizer for RS256 JWTs

An AWS API Gateway [Custom Authorizer](http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html) that authorizes API requests by requiring
An AWS API Gateway [Custom Authorizer](http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html) that authorizes API requests by requiring
that the OAuth2 [bearer token](https://tools.ietf.org/html/rfc6750) is a JWT that can be validated using the RS256 (asymmetric) algorithm with a public key that is obtained from a [JWKS](https://tools.ietf.org/html/rfc7517) endpoint.

## About
Expand All @@ -13,7 +13,7 @@ One popular use case is to provide an interface to AWS Lambda functions to deliv

### What are "Custom Authorizers"?

In February 2016 Amazon
In February 2016 Amazon
[announced](https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/)
a new feature for API Gateway -
[Custom Authorizers](http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html). This allows a Lambda function to be invoked prior to an API Gateway execution to perform custom authorization of the request, rather than using AWS's built-in authorization. This code can then be isolated to a single centralized Lambda function rather than replicated across every backend Lambda function.
Expand Down Expand Up @@ -181,7 +181,7 @@ Now we can finally create the lamda function itself in AWS. Start by going to [c

* Name: `jwtRsaCustomAuthorizer`
* Description: `JWT RSA Custom Authorizer`
* Runtime: `Node.js 4.3`
* Runtime: `Node.js 8.10`
* _Lambda function code_
* Code entry type: `Update a .ZIP file`
* Function package: (upload the `custom-authorizer.zip` file created earlier)
Expand Down Expand Up @@ -230,7 +230,7 @@ You can then test the new custom authorizer by providing an **Identity Token** a
```
Bearer ACCESS_TOKEN
```
A successful test will look something like:
```
Expand All @@ -241,14 +241,14 @@ Policy
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1459758003000",
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:*"
]
"Sid": "Stmt1459758003000",
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:*"
]
}
]
}
Expand Down Expand Up @@ -305,7 +305,7 @@ The above command is performed using the `GET` method.
```
fetch( 'INVOKE_URL/your/resource', { method: 'GET', headers: { Authorization : 'Bearer ACCESS_TOKEN' }}).then(response => { console.log( response );});
```
```
---
Expand Down
26 changes: 11 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
'use strict';

var lib = require('./lib');

const lib = require('./lib');
let data;

// Lambda function index.handler - thin wrapper around lib.authenticate
module.exports.handler = function (event, context) {
lib.authenticate(event, function (err, data) {
if (err) {
if (!err) context.fail("Unhandled error");
context.fail("Unauthorized");

}
else context.succeed(data);

});

module.exports.handler = async (event) => {
try {
data = await lib.authenticate(event);
}
catch (err) {
console.log(err);
return `Unauthorized: ${err.message}`;
}
return data;
};
101 changes: 43 additions & 58 deletions lib.js
Original file line number Diff line number Diff line change
@@ -1,85 +1,70 @@
'use strict';

require('dotenv').config({ silent: true });
var jwksClient = require('jwks-rsa');
var jwt = require('jsonwebtoken');

var getPolicyDocument = function (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 = resource;
policyDocument.Statement[0] = statementOne;
const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');
const util = require('util');

const getPolicyDocument = (effect, resource) => {
const policyDocument = {
Version: '2012-10-17', // default version
Statement: [{
Action: 'execute-api:Invoke', // default action
Effect: effect,
Resource: resource,
}]
};
return policyDocument;
}


// extract and return the Bearer Token from the Lambda event parameters
var getToken = function (params) {
var token;

const getToken = (params) => {
if (!params.type || params.type !== 'TOKEN') {
throw new Error("Expected 'event.type' parameter to have value TOKEN");
throw new Error('Expected "event.type" parameter to have value "TOKEN"');
}

var tokenString = params.authorizationToken;
const tokenString = params.authorizationToken;
if (!tokenString) {
throw new Error("Expected 'event.authorizationToken' parameter to be set");
throw new Error('Expected "event.authorizationToken" parameter to be set');
}

var match = tokenString.match(/^Bearer (.*)$/);
const match = tokenString.match(/^Bearer (.*)$/);
if (!match || match.length < 2) {
throw new Error("Invalid Authorization token - '" + tokenString + "' does not match 'Bearer .*'");
throw new Error(`Invalid Authorization token - ${tokenString} does not match "Bearer .*"`);
}
return match[1];
}

module.exports.authenticate = function (params, cb) {
const jwtOptions = {
audience: process.env.AUDIENCE,
issuer: process.env.TOKEN_ISSUER
};

module.exports.authenticate = (params) => {
console.log(params);
var token = getToken(params);
const token = getToken(params);

var client = jwksClient({
const decoded = jwt.decode(token, { complete: true });
if (!decoded || !decoded.header || !decoded.header.kid) {
throw new Error('invalid token');
}

const client = jwksClient({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10, // Default value
jwksUri: process.env.JWKS_URI
});

var decoded = jwt.decode(token, { complete: true });
var kid = decoded.header.kid;
client.getSigningKey(kid, function (err, key) {
if(err)
{
cb(err);
}
else
{
var signingKey = key.publicKey || key.rsaPublicKey;
jwt.verify(token, signingKey, { audience: process.env.AUDIENCE, issuer: process.env.TOKEN_ISSUER },
function (err, decoded) {
if (err) {
cb(err);

}
else {

cb(null, {
principalId: decoded.sub,
policyDocument: getPolicyDocument('Allow', params.methodArn),
context: {
scope: decoded.scope
}
});
}
});
}

});



const getSigningKey = util.promisify(client.getSigningKey);
return getSigningKey(decoded.header.kid)
.then((key) => {
const signingKey = key.publicKey || key.rsaPublicKey;
return jwt.verify(token, signingKey, jwtOptions);
})
.then((decoded)=> ({
principalId: decoded.sub,
policyDocument: getPolicyDocument('Allow', params.methodArn),
context: { scope: decoded.scope }
}));
}
Loading

0 comments on commit f9e7f29

Please sign in to comment.