Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions aws-node-auth0-custom-authorizers-api/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
.serverless
secrets.json
9 changes: 1 addition & 8 deletions aws-node-auth0-custom-authorizers-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,7 @@ Custom Authorizers allow you to run an AWS Lambda Function before your targeted

2. Setup an [auth0 client](https://auth0.com/docs/clients) and get your `client id` and `client secrets` from auth0.

3. Plugin your `AUTH0_CLIENT_ID` and `AUTH0_CLIENT_SECRET` in `handler.js`. These will be used by the JSON web token decoder to validate private api access.

```js
/* handler.js */
// Replace with your auth0 client values
const AUTH0_CLIENT_ID = 'your-auth0-client-id-here';
const AUTH0_CLIENT_SECRET = 'your-auth0-client-secret-here';
```
3. Plugin your `AUTH0_CLIENT_ID` and `AUTH0_CLIENT_SECRET` in a new file called `secrets.json`. These will be used by the JSON web token decoder to validate private api access.

4. Deploy the service with `serverless-deploy` and grab the public and private endpoints.

Expand Down
2 changes: 1 addition & 1 deletion aws-node-auth0-custom-authorizers-api/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.auth0.com/js/lock-9.0.min.js"></script>
<script src="//cdn.auth0.com/js/lock/10.1.0/lock.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="app.css" rel="stylesheet">
</head>
Expand Down
116 changes: 78 additions & 38 deletions aws-node-auth0-custom-authorizers-api/handler.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,92 @@
const jwt = require('jsonwebtoken');
const jwt = require('jsonwebtoken')

const AUTH0_CLIENT_ID = 'your-auth0-client-id-here';
const AUTH0_CLIENT_SECRET = 'your-auth0-client-secret-here';
// Set in `enviroment` of serverless.yml
const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID
const AUTH0_CLIENT_SECRET = process.env.AUTH0_CLIENT_SECRET

// Policy helper function
const generatePolicy = (principalId, effect, resource) => {
const authResponse = {};
authResponse.principalId = principalId;
const authResponse = {}
authResponse.principalId = principalId
if (effect && resource) {
const policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
const statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
const policyDocument = {}
policyDocument.Version = '2012-10-17'
policyDocument.Statement = []
const statementOne = {}
statementOne.Action = 'execute-api:Invoke'
statementOne.Effect = effect
statementOne.Resource = resource
policyDocument.Statement[0] = statementOne
authResponse.policyDocument = policyDocument
}
return authResponse;
};
return authResponse
}

// Reusable Authorizer function, set on `authorizer` field in serverless.yml
module.exports.auth = (event, context, cb) => {
if (event.authorizationToken) {
// remove "bearer " from token
const token = event.authorizationToken.substring(7);
const options = {
audience: AUTH0_CLIENT_ID,
};
jwt.verify(token, AUTH0_CLIENT_SECRET, options, (err, decoded) => {
if (err) {
cb('Unauthorized');
} else {
cb(null, generatePolicy(decoded.sub, 'Allow', event.methodArn));
module.exports.auth = (event, context, callback) => {
console.log('event', event)
if (!event.authorizationToken) {
return callback('Unauthorized')
}

const tokenParts = event.authorizationToken.split(' ')
const tokenValue = tokenParts[1]

if (!(tokenParts[0].toLowerCase() === 'bearer' && tokenValue)) {
// no auth token!
return callback('Unauthorized')
}
const options = {
audience: AUTH0_CLIENT_ID,
}
// decode base64 secret. ref: http://bit.ly/2hA6CrO
const secret = new Buffer.from(AUTH0_CLIENT_SECRET, 'base64')
try {
jwt.verify(tokenValue, secret, options, (verifyError, decoded) => {
if (verifyError) {
console.log('verifyError', verifyError)
// 401 Unauthorized
console.log(`Token invalid. ${verifyError}`)
return callback('Unauthorized')
}
});
} else {
cb('Unauthorized');
// is custom authorizer function
console.log('valid from customAuthorizer', decoded)
return callback(null, generatePolicy(decoded.sub, 'Allow', event.methodArn))
})
} catch (err) {
console.log('catch error. Invalid token', err)
return callback('Unauthorized')
}
};
}

// Public API
module.exports.publicEndpoint = (event, context, cb) => {
cb(null, { message: 'Welcome to our Public API!' });
};
module.exports.publicEndpoint = (event, context, callback) => {
return callback(null, {
statusCode: 200,
headers: {
/* Required for CORS support to work */
"Access-Control-Allow-Origin": "*",
/* Required for cookies, authorization headers with HTTPS */
"Access-Control-Allow-Credentials": true
},
body: JSON.stringify({
message: 'Hi ⊂◉‿◉つ from Public API',
}),
})
}

// Private API
module.exports.privateEndpoint = (event, context, cb) => {
cb(null, { message: 'Only logged in users can see this' });
};
module.exports.privateEndpoint = (event, context, callback) => {
return callback(null, {
statusCode: 200,
headers: {
/* Required for CORS support to work */
"Access-Control-Allow-Origin": "*",
/* Required for cookies, authorization headers with HTTPS */
"Access-Control-Allow-Credentials": true
},
body: JSON.stringify({
message: 'Hi ⊂◉‿◉つ from Private API. Only logged in users can see this',
}),
})
}
2 changes: 1 addition & 1 deletion aws-node-auth0-custom-authorizers-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"description": "Demonstration of protecting API gateway endpoints with auth0",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^7.1.9"
"jsonwebtoken": "^8.1.0"
}
}
4 changes: 4 additions & 0 deletions aws-node-auth0-custom-authorizers-api/secrets.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"AUTH0_CLIENT_ID": "your-client-id",
"AUTH0_CLIENT_SECRET": "your-client-secret"
}
52 changes: 36 additions & 16 deletions aws-node-auth0-custom-authorizers-api/serverless.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,55 @@

service: aws-auth0-protected-endpoints
service: aws-custom-authorizer-auth0

provider:
name: aws
runtime: nodejs4.3
runtime: nodejs6.10
region: us-west-2
environment:
AUTH0_CLIENT_ID: ${file(./secrets.json):AUTH0_CLIENT_ID}
AUTH0_CLIENT_SECRET: ${file(./secrets.json):AUTH0_CLIENT_SECRET}

functions:
auth:
handler: handler.auth
cors: true
publicEndpoint:
handler: handler.publicEndpoint
events:
- http:
path: api/public
method: get
integration: lambda
method: post
cors: true
privateEndpoint:
handler: handler.privateEndpoint
events:
- http:
path: api/private
method: get
integration: lambda
authorizer: auth # See custom authorizer docs here: http://bit.ly/2gXw9pO
cors:
origins:
- '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
method: post
# See custom authorizer docs here: http://bit.ly/2gXw9pO
authorizer: auth
cors: true

resources:
Resources:
# This response is needed for custom authorizer failures cors support ¯\_(ツ)_/¯
GatewayResponse:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseType: EXPIRED_TOKEN
RestApiId:
Ref: 'ApiGatewayRestApi'
StatusCode: '401'
AuthFailureGatewayResponse:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseType: UNAUTHORIZED
RestApiId:
Ref: 'ApiGatewayRestApi'
StatusCode: '401'