├── .env.sample ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── LICENSE ├── README.md ├── dynamo.json.sample ├── event.json.sample ├── index.js ├── lib.js ├── package-lock.json ├── package.json └── policyDocument.json.sample /.env.sample: -------------------------------------------------------------------------------- 1 | # copy this file to .env and fill in the 2 | AUTH0_DOMAIN=.auth0.com 3 | AUTH0_DOMAIN_PARAMETER=ssm-parameter-name 4 | AUTH0_CLIENTID= 5 | AUTH0_CLIENTID_PARAMETER=ssm-parameter-name 6 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [8.x, 10.x, 12.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: npm install, build, and test 21 | run: | 22 | npm ci 23 | npm run build --if-present 24 | npm test 25 | env: 26 | CI: true 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Visual Studio Code 7 | .vscode 8 | 9 | tmp 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directory 32 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 33 | node_modules 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | 41 | # project files that contain sensitive data 42 | .env 43 | event.json 44 | policyDocument.json 45 | *.zip 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lambda-auth0-authorizer 2 | 3 | An AWS Custom Authorizer for AWS API Gateway that support Auth0 JWT Bearer tokens. 4 | 5 | > **⚠⚠ WARNING ⚠⚠** 6 | > This project is no longer supported. 7 | > 8 | > For many use cases, you are better off using the AWS HTTP API Gateway with native [JWT authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-jwt-authorizer.html) instead. See the Auth0 blog post [Securing AWS HTTP APIs with JWT Authorizers](https://auth0.com/blog/securing-aws-http-apis-with-jwt-authorizers/#Create-an-HTTP-API-on-AWS) (Oct 2021) 9 | > 10 | > If you insist on using the AWS REST API Gateway, consider one of the alternatives below 11 | 12 | ## Alternatives to lambda-auth0-authorizer 13 | 14 | The fine folks at Auth0 (somewhat sneakily) [forked this repo](https://github.com/auth0-samples/jwt-rsa-aws-custom-authorizer) to: 15 | 16 | * 17 | 18 | You can find it referenced in their documentation [Secure AWS API Gateway Endpoints Using Custom Authorizers](https://auth0.com/docs/customize/integrations/aws/aws-api-gateway-custom-authorizers). 19 | 20 | Auth0 uses JWTs. There are several Custom Authorizers for JWTs: 21 | 22 | * 23 | * 24 | 25 | 26 | ## About 27 | 28 | ### What is AWS API Gateway? 29 | API Gateway is an AWS service that allows for the definition, configuration and deployment of REST API interfaces. 30 | These interfaces can connect to a number of backend systems. 31 | One popular use case is to provide an interface to AWS Lambda functions to deliver a so-called 'serverless' architecture. 32 | 33 | ### What are Custom Authorizers? 34 | In February 2016 Amazon 35 | [announced](https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/) 36 | a new feature for API Gateway - 37 | [Custom Authorizers](http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html). 38 | 39 | This allows a Lambda function to be invoked prior to an API Gateway execution to perform authentication and authorization of the request and caching of the result. 40 | This code can then be isolated to a single function rather than replicated across every backend Lambda function. 41 | 42 | ### What is Auth0? 43 | Auth0 is a 3rd party single-sign on service that provides single sign-on services, abstracting various login and identity services. 44 | Auth0 offers a number of SDKs as well as integrations with AWS. 45 | 46 | ### What is lambda-auth0-authorizer? 47 | 48 | This package gives you the code for a Custom Authorizer that will, with a little configuration, perform Auth0 authentication on API Gateway requests. 49 | 50 | 51 | 52 | ## Configuration 53 | 54 | ### node modules 55 | 56 | Run `npm install` to download all the dependent modules. This is a prerequisite for deployment as AWS Lambda requires these files to be included in the bundle. 57 | 58 | ### .env 59 | 60 | Copy .env.sample to .env 61 | 62 | Values specified in this file will set the corresponding environment variables. 63 | 64 | You will need to set: 65 | 66 | AUTH0_DOMAIN=mydomain.auth0.com 67 | # or: 68 | AUTH0_DOMAIN_PARAMETER=ssm-parameter-name 69 | 70 | AUTH0_CLIENTID=MyClientId 71 | # or: 72 | AUTH0_CLIENTID_PARAMETER=ssm-parameter-name 73 | 74 | You can obtain these values from your Auth0 [Application settings](https://manage.auth0.com/#/applications). 75 | 76 | ### policyDocument.json 77 | 78 | Copy policyDocument.json.sample to policyDocument.json 79 | 80 | This AWS Policy document is returned by the authorizer and is the permission granted to the invoke of the API Gateway. 81 | 82 | You will need to edit it to give sufficient access for all the API Gateway functions it will use 83 | 84 | The general form an API Gateway ARN is: 85 | 86 | "arn:aws:execute-api:::///" 87 | 88 | To grant access to ALL your API Gateways you can use: 89 | 90 | "arn:aws:execute-api:*" 91 | 92 | ### dynamo.json 93 | 94 | lambda-auth0-autorizer can optionally store the auth0 user info into an AWS DynamoDB table. 95 | 96 | To do this copy dynamo.json.sample to dynamo.json. If present in the deployment bundle, it will be used as the template for the DynamoDoc Put operation. 97 | 98 | You should only change: 99 | * The value for "TableName", e.g. "Users" 100 | * The key of the first "Items" value, which is the HashKey of that table, e.g. "userId" 101 | 102 | ## Local testing 103 | 104 | ### Bearer token 105 | 106 | You will need to obtain a test Bearer Token. 107 | This is the [id_token](https://auth0.com/docs/tokens/id_token) that is provided by a successful Auth0 authentication. 108 | The id_token is valid for 10 hours (36000 seconds) by default. 109 | 110 | You can check your id_token by passing it to Auth0's API test interface: 111 | https://auth0.com/docs/auth-api#post--tokeninfo 112 | 113 | lamda-auth0-authorizer also will also auto-detect and support Auth0's depcreated 16-character v1 [access_token](https://auth0.com/docs/tokens/access_token). 114 | 115 | ### event.json 116 | 117 | Copy event.json.sample to event.json. Provide the id_token from the previous step. 118 | 119 | "authorizationToken" : "Bearer ", 120 | 121 | ### Local DynamoDB connection testing 122 | 123 | If you are using the DynamoDB configuration described above, check that your local environment is configured for DynamoDB 124 | 125 | Using the [AWS CLI](https://aws.amazon.com/cli/), run the following command with the you configured in dynamo.json. 126 | 127 | $ aws dynamodb describe-table --table-name 128 | 129 | The result should run without error and show the same value for TableName.KeySchema.AttributeName which the table key configured in dynamo.json. 130 | 131 | ### lambda-local 132 | 133 | Run `npm test` to use lambda-local test harness 134 | 135 | A successful run will look something like: 136 | 137 | $ npm test 138 | 139 | > lambda-auth0-authenticator@0.0.1 test ~/lambda-auth0-authorizer 140 | > lambda-local --timeout 300 --lambdapath index.js --eventpath event.json 141 | 142 | Logs 143 | ---- 144 | START RequestId: bcb21d17-c3f8-2299-58d9-0400adcfe921 145 | Auth0 authentication successful for user_id oauth|1234567890 146 | END 147 | 148 | 149 | Message 150 | ------ 151 | { 152 | "principalId": "oauth|1234567890", 153 | "policyDocument": { 154 | "Version": "2012-10-17", 155 | "Statement": [ 156 | { 157 | "Sid": "Stmt1459758003000", 158 | "Effect": "Allow", 159 | "Action": [ 160 | "execute-api:Invoke" 161 | ], 162 | "Resource": [ 163 | "arn:aws:execute-api:*" 164 | ] 165 | } 166 | ] 167 | } 168 | } 169 | 170 | The Message is the authorization data that the Lambda function returns to API Gateway. 171 | 172 | ## Deployment 173 | 174 | ### Create bundle 175 | 176 | You can create the bundle using `npm run zip`. This creates a lambda-auth0-authorizer.zip deployment package with all the source, configuration and node modules AWS Lambda needs. 177 | 178 | ### Create Lambda function 179 | 180 | From the AWS console https://console.aws.amazon.com/lambda/home#/create?step=2 181 | 182 | * Name : auth0_authorizer 183 | * Description: Auth0 authorizer for API Gateway 184 | * Runtime: Node.js 4.3 185 | * Code entry type: Upload a .ZIP file 186 | * Upload : < select lambda-auth0-authorizer.zip we created in the previous step > 187 | * Handler : index.handler 188 | * Role : Basic with DynamoDB 189 | * If you aren't using DynamoDB (see above), you can also pick Basic execution role 190 | * Memory (MB) : 128 191 | * Timeout : 30 seconds 192 | * VPC : No VPC 193 | 194 | Click Next and Create 195 | 196 | #### Testing Lambda 197 | 198 | In the Lambda console, select Actions -> Configure Test event. 199 | 200 | Use the following JSON as the test event data. The id_token is the same format we used in event.json above. Click Save and Test to run the Lambda. 201 | 202 | { 203 | "type": "TOKEN", 204 | "authorizationToken": "Bearer ", 205 | "methodArn":"arn:aws:execute-api:us-east-1:1234567890:apiId/stage/method/resourcePath" 206 | } 207 | 208 | ### Create IAM Role 209 | 210 | You will need to create an IAM Role that has permissions to invoke the Lambda function we created above. 211 | 212 | That Role will need to have a Policy similar to the following: 213 | 214 | { 215 | "Version": "2012-10-17", 216 | "Statement": [ 217 | { 218 | "Effect": "Allow", 219 | "Resource": [ 220 | "*" 221 | ], 222 | "Action": [ 223 | "lambda:InvokeFunction" 224 | ] 225 | } 226 | ] 227 | } 228 | 229 | You will also need to set a "Trust Relationship for the role". This will allow the API Gateway permission to assume the role and run the lambda function. The trust relationship can be set in a separate tab in the AWS console. Click the "Edit Trust Relationship button". It should look similar to the following: 230 | 231 | { 232 | "Version": "2012-10-17", 233 | "Statement": [ 234 | { 235 | "Effect": "Allow", 236 | "Principal": { 237 | "Service": [ 238 | "apigateway.amazonaws.com", 239 | "lambda.amazonaws.com" 240 | ] 241 | }, 242 | "Action": "sts:AssumeRole" 243 | } 244 | ] 245 | } 246 | 247 | 248 | ### Configure API Gateway 249 | 250 | From the AWS console https://console.aws.amazon.com/apigateway/home 251 | 252 | Open your API, or Create a new one. 253 | 254 | In the left panel, under your API name, click on **Custom Authorizers**. Click on **Create** 255 | 256 | * Name : auth0_authorizer 257 | * Lambda region : < from previous step > 258 | * Execution role : < the ARN of the Role we created in the previous step > 259 | * Identity token source : method.request.header.Authorization 260 | * Token validation expression : ```^Bearer [-0-9a-zA-z\.]*$``` 261 | ** Cut-and-paste this regular expression from ^ to $ inclusive 262 | * Result TTL in seconds : 3600 263 | 264 | Click **Create** 265 | 266 | ### Testing 267 | 268 | You can test the authorizer by supplying an Identity token and clicking **Test** 269 | 270 | The id_token is the same format we used in event.json above. 271 | 272 | Bearer 273 | 274 | A successful test will look something like: 275 | 276 | Latency: 2270 ms 277 | Principal Id: oauth|1234567890 278 | Policy 279 | { 280 | "Version": "2012-10-17", 281 | "Statement": [ 282 | { 283 | "Sid": "Stmt1459758003000", 284 | "Effect": "Allow", 285 | "Action": [ 286 | "execute-api:Invoke" 287 | ], 288 | "Resource": [ 289 | "arn:aws:execute-api:*" 290 | ] 291 | } 292 | ] 293 | } 294 | 295 | ### Configure API Gateway Methods to use the Authorizer 296 | 297 | In the left panel, under your API name, click on **Resources**. 298 | Under the Resource tree, select one of your Methods (POST, GET etc.) 299 | 300 | Select **Method Request**. Under **Authorization Settings** change: 301 | 302 | * Authorizer : auth0_authorizer 303 | 304 | Make sure that: 305 | 306 | * API Key Required : false 307 | 308 | Click the tick to save the changes. 309 | 310 | ### Deploy the API 311 | 312 | You need to Deploy the API to make the changes public. 313 | 314 | Select **Action** and **Deploy API**. Select your **Stage**. 315 | 316 | ### Test your endpoint remotely 317 | 318 | #### With Postman 319 | 320 | You can use Postman to test the REST API 321 | 322 | * Method: < matching the Method in API Gateway > 323 | * URL `https://.execute-api..amazonaws.com//` 324 | * The base URL you can see in the Stages section of the API 325 | * Append the Resource name to get the full URL 326 | * Header - add an Authorization key 327 | * Authorization : Bearer 328 | 329 | #### With curl from the command line 330 | 331 | $ curl -X POST -H 'Authorization: Bearer ' 332 | 333 | #### In (modern) browsers console with fetch 334 | 335 | fetch( '', { method: 'POST', headers: { Authorization : 'Bearer ' }}).then(response => { console.log( response );}); 336 | 337 | -------------------------------------------------------------------------------- /dynamo.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "TableName" : "Users", 3 | "Item" : { 4 | "userId" : "" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /event.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "TOKEN", 3 | "authorizationToken" : "Bearer ", 4 | "methodArn":"arn:aws:execute-api:us-east-1:1234567890:apiId/stage/method/resourcePath" 5 | } 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var lib = require('./lib'); 4 | 5 | // Lambda function index.handler - thin wrapper around lib.authenticate 6 | module.exports.handler = function( event, context ) { 7 | lib.authenticate( event ) 8 | .then( context.succeed ) 9 | .catch( err => { 10 | if ( ! err ) context.fail( "Unhandled error case" ); 11 | context.fail( err ); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /lib.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // static setup that can be done at load-time 4 | 5 | var ACCESS_TOKEN_LENGTH = 16; // (apparent) length of an Autho0 access_token 6 | 7 | // Lambda now supports environment variables - http://docs.aws.amazon.com/lambda/latest/dg/tutorial-env_cli.html 8 | // a .env file can be used as a development convenience. Real environment variables can be used in deployment and 9 | // will override anything loaded by dotenv. 10 | require('dotenv').config(); 11 | 12 | var fs = require('fs'); 13 | var Promise = require('bluebird'); 14 | Promise.longStackTraces(); 15 | 16 | var AWS = require('aws-sdk'); 17 | AWS.config.apiVersions = { dynamodb: '2012-08-10' }; 18 | if ( process.env.AWS_REGION ) { 19 | AWS.config.update( { region: process.env.AWS_REGION } ); 20 | } 21 | var dynamo = new AWS.DynamoDB.DocumentClient(); 22 | var ssm = new AWS.SSM(); 23 | Promise.promisifyAll( Object.getPrototypeOf( dynamo )); 24 | Promise.promisifyAll( Object.getPrototypeOf( ssm )); 25 | 26 | 27 | ///// TODO : use promises to load these asynchronously 28 | ///// return Promise.resolve to return cached values 29 | ///// see : http://bluebirdjs.com/docs/api/promise.method.html 30 | 31 | 32 | 33 | var policyDocumentFilename = 'policyDocument.json'; 34 | var policyDocument; 35 | try { 36 | policyDocument = JSON.parse(fs.readFileSync( __dirname + '/' + policyDocumentFilename, 'utf8')); 37 | } catch (e) { 38 | if (e.code === 'ENOENT') { 39 | console.error('Expected ' + policyDocumentFilename + ' to be included in Lambda deployment package'); 40 | // fallthrough 41 | } 42 | throw e; 43 | } 44 | 45 | var dynamoParametersFilename = 'dynamo.json'; 46 | var dynamoParameters = null; 47 | try { 48 | dynamoParameters = JSON.parse(fs.readFileSync( __dirname + '/' + dynamoParametersFilename, 'utf8')); 49 | } catch (e) { 50 | if (e.code !== 'ENOENT') { 51 | throw e; 52 | } 53 | // otherwise fallthrough 54 | } 55 | 56 | var AuthenticationClient = require('auth0').AuthenticationClient; 57 | 58 | var testClientOptions = function( domain, clientId ) { 59 | if ( typeof domain === "undefined" || ! domain.match( /\.auth0\.com$/ ) ) { 60 | throw new Error( "Expected AUTHO_DOMAIN or AUTH0_DOMAIN_PARAMETER environment variable to be set in .env file. See https://manage.auth0.com/#/applications" ) 61 | } 62 | 63 | if ( typeof clientId === "undefined" || clientId.length === 0 ) { 64 | throw new Error( "Expected AUTH0_CLIENTID or AUTH0_CLIENTID_PARAMETER environment variable to be set in .env file. See https://manage.auth0.com/#/applications" ) 65 | } 66 | } 67 | 68 | var getClient = function() { 69 | if ( process.env.AUTH0_DOMAIN && process.env.AUTH0_DOMAIN_PARAMETER ) { 70 | throw new Error( "Expected only one of AUTH0_DOMAIN and AUTH0_DOMAIN_PARAMETER environment variable to be set in .env file." ) 71 | } 72 | if ( process.env.AUTH0_CLIENTID && process.env.AUTH0_CLIENTID_PARAMETER ) { 73 | throw new Error( "Expected only one of AUTH0_CLIENTID and AUTH0_CLIENTID_PARAMETER environment variable to be set in .env file." ) 74 | } 75 | 76 | var domain, clientId 77 | var promise = Promise.resolve() 78 | 79 | if ( process.env.AUTH0_DOMAIN_PARAMETER ) { 80 | promise = Promise.all( [ 81 | promise, 82 | ssm.getParameterAsync( { Name: process.env.AUTH0_DOMAIN_PARAMETER, WithDecryption: true } ) 83 | .then( (data) => { 84 | domain = data.Parameter.Value 85 | } ) 86 | ] ) 87 | } else { 88 | domain = process.env.AUTH0_DOMAIN 89 | } 90 | 91 | if ( process.env.AUTH0_CLIENTID_PARAMETER ) { 92 | promise = Promise.all( [ 93 | promise, 94 | ssm.getParameterAsync( { Name: process.env.AUTH0_CLIENTID_PARAMETER, WithDecryption: true } ) 95 | .then( (data) => { 96 | clientId = data.Parameter.Value 97 | } ) 98 | ] ) 99 | } else { 100 | clientId = process.env.AUTH0_CLIENTID 101 | } 102 | 103 | return promise.then( () => { 104 | testClientOptions( domain, clientId ) 105 | return new AuthenticationClient( { 106 | domain : domain, 107 | clientId : clientId 108 | } ) 109 | } ) 110 | } 111 | 112 | 113 | // extract and return the Bearer Token from the Lambda event parameters 114 | var getToken = function( params ) { 115 | if ( ! params.type || params.type !== 'TOKEN' ) { 116 | throw new Error( "Expected 'event.type' parameter to have value TOKEN" ); 117 | } 118 | 119 | var tokenString = params.authorizationToken; 120 | if ( !tokenString ) { 121 | throw new Error( "Expected 'event.authorizationToken' parameter to be set" ); 122 | } 123 | 124 | var match = tokenString.match( /^Bearer (.*)$/ ); 125 | if ( ! match || match.length < 2 ) { 126 | throw new Error( "Invalid Authorization token - '" + tokenString + "' does not match 'Bearer .*'" ); 127 | } 128 | return match[1]; 129 | } 130 | 131 | var returnAuth0UserInfo = function( auth0return ) { 132 | if ( ! auth0return ) throw new Error( 'Auth0 empty return' ); 133 | if ( auth0return === 'Unauthorized') { 134 | throw new Error( 'Auth0 reports Unauthorized' ) 135 | } 136 | 137 | return auth0return 138 | } 139 | 140 | // if dynamo.json is included in the package, save the userInfo to DynamoDB 141 | var saveUserInfo = function( userInfo ) { 142 | if ( ! userInfo ) throw new Error( 'saveUserInfo - expected userInfo parameter' ); 143 | if ( ! userInfo.user_id ) throw new Error( 'saveUserInfo - expected userInfo.user_id parameter' ); 144 | 145 | if ( dynamoParameters ) { 146 | var putParams = Object.assign({}, dynamoParameters); 147 | var hashkeyName = Object.keys( putParams.Item )[0]; 148 | putParams.Item = userInfo; 149 | putParams.Item[ hashkeyName ] = userInfo.user_id; 150 | return dynamo.putAsync( putParams ) 151 | .then( () => userInfo ); 152 | } else { 153 | return userInfo; 154 | } 155 | 156 | } 157 | 158 | // extract user_id from the autho0 userInfo and return it for AWS principalId 159 | var getPrincipalId = function( userInfo ) { 160 | if ( ! userInfo || ! userInfo.user_id ) { 161 | throw new Error( "No user_id returned from Auth0" ); 162 | } 163 | console.log( 'Auth0 authentication successful for user_id ' + userInfo.user_id ); 164 | 165 | return userInfo.user_id; 166 | } 167 | 168 | // return the expected Custom Authorizaer JSON object 169 | var getAuthentication = function( principalId ) { 170 | return { 171 | principalId : principalId, 172 | policyDocument : policyDocument 173 | } 174 | } 175 | 176 | module.exports.authenticate = function (params) { 177 | return getClient() 178 | .then( ( auth0 ) => { 179 | var token = getToken(params); 180 | 181 | if ( token.length === ACCESS_TOKEN_LENGTH ) { // Auth0 v1 access_token (deprecated) 182 | return auth0.users.getInfo( token ); 183 | } else if ( token.length > ACCESS_TOKEN_LENGTH ) { // (probably) Auth0 id_token 184 | return auth0.tokens.getInfo( token ); 185 | } else { 186 | throw new TypeError( "Bearer token too short - expected >= 16 charaters" ); 187 | } 188 | } ) 189 | .then( returnAuth0UserInfo ) 190 | .then( saveUserInfo ) 191 | .then( getPrincipalId ) 192 | .then( getAuthentication ); 193 | } 194 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-auth0-authenticator", 3 | "version": "0.0.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "async": { 8 | "version": "2.6.4", 9 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", 10 | "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", 11 | "dev": true, 12 | "requires": { 13 | "lodash": "^4.17.14" 14 | } 15 | }, 16 | "auth0": { 17 | "version": "4.1.0", 18 | "resolved": "https://registry.npmjs.org/auth0/-/auth0-4.1.0.tgz", 19 | "integrity": "sha512-1CpjWPOuWPAhQZy46/T/jOViy1WXhytmdlZji693ZpBfugYw181+JXfKLzjea59oKmo4HFctD05cec7xGdysfQ==", 20 | "requires": { 21 | "jose": "^4.13.2", 22 | "uuid": "^9.0.0" 23 | }, 24 | "dependencies": { 25 | "uuid": { 26 | "version": "9.0.1", 27 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", 28 | "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" 29 | } 30 | } 31 | }, 32 | "available-typed-arrays": { 33 | "version": "1.0.5", 34 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", 35 | "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", 36 | "dev": true 37 | }, 38 | "aws-sdk": { 39 | "version": "2.1354.0", 40 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1354.0.tgz", 41 | "integrity": "sha512-3aDxvyuOqMB9DqJguCq6p8momdsz0JR1axwkWOOCzHA7a35+Bw+WLmqt3pWwRjR1tGIwkkZ2CvGJObYHsOuw3w==", 42 | "dev": true, 43 | "requires": { 44 | "buffer": "4.9.2", 45 | "events": "1.1.1", 46 | "ieee754": "1.1.13", 47 | "jmespath": "0.16.0", 48 | "querystring": "0.2.0", 49 | "sax": "1.2.1", 50 | "url": "0.10.3", 51 | "util": "^0.12.4", 52 | "uuid": "8.0.0", 53 | "xml2js": "0.5.0" 54 | } 55 | }, 56 | "base64-js": { 57 | "version": "1.5.1", 58 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 59 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 60 | "dev": true 61 | }, 62 | "bluebird": { 63 | "version": "3.7.2", 64 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", 65 | "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" 66 | }, 67 | "buffer": { 68 | "version": "4.9.2", 69 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 70 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 71 | "dev": true, 72 | "requires": { 73 | "base64-js": "^1.0.2", 74 | "ieee754": "^1.1.4", 75 | "isarray": "^1.0.0" 76 | } 77 | }, 78 | "call-bind": { 79 | "version": "1.0.2", 80 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 81 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 82 | "dev": true, 83 | "requires": { 84 | "function-bind": "^1.1.1", 85 | "get-intrinsic": "^1.0.2" 86 | } 87 | }, 88 | "color": { 89 | "version": "3.0.0", 90 | "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", 91 | "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", 92 | "dev": true, 93 | "requires": { 94 | "color-convert": "^1.9.1", 95 | "color-string": "^1.5.2" 96 | } 97 | }, 98 | "color-convert": { 99 | "version": "1.9.3", 100 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 101 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 102 | "dev": true, 103 | "requires": { 104 | "color-name": "1.1.3" 105 | } 106 | }, 107 | "color-name": { 108 | "version": "1.1.3", 109 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 110 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 111 | "dev": true 112 | }, 113 | "color-string": { 114 | "version": "1.6.0", 115 | "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", 116 | "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", 117 | "dev": true, 118 | "requires": { 119 | "color-name": "^1.0.0", 120 | "simple-swizzle": "^0.2.2" 121 | } 122 | }, 123 | "colornames": { 124 | "version": "1.1.1", 125 | "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", 126 | "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", 127 | "dev": true 128 | }, 129 | "colors": { 130 | "version": "1.4.0", 131 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", 132 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", 133 | "dev": true 134 | }, 135 | "colorspace": { 136 | "version": "1.1.2", 137 | "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", 138 | "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", 139 | "dev": true, 140 | "requires": { 141 | "color": "3.0.x", 142 | "text-hex": "1.0.x" 143 | } 144 | }, 145 | "commander": { 146 | "version": "2.20.3", 147 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 148 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 149 | "dev": true 150 | }, 151 | "core-util-is": { 152 | "version": "1.0.2", 153 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 154 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 155 | "dev": true 156 | }, 157 | "diagnostics": { 158 | "version": "1.1.1", 159 | "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", 160 | "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", 161 | "dev": true, 162 | "requires": { 163 | "colorspace": "1.1.x", 164 | "enabled": "1.0.x", 165 | "kuler": "1.0.x" 166 | } 167 | }, 168 | "dotenv": { 169 | "version": "2.0.0", 170 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-2.0.0.tgz", 171 | "integrity": "sha1-vXWcNXqqcDZeAclrewvsCKbg2Uk=" 172 | }, 173 | "enabled": { 174 | "version": "1.0.2", 175 | "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", 176 | "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", 177 | "dev": true, 178 | "requires": { 179 | "env-variable": "0.0.x" 180 | } 181 | }, 182 | "env-variable": { 183 | "version": "0.0.5", 184 | "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", 185 | "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==", 186 | "dev": true 187 | }, 188 | "events": { 189 | "version": "1.1.1", 190 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 191 | "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", 192 | "dev": true 193 | }, 194 | "fast-safe-stringify": { 195 | "version": "2.0.7", 196 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", 197 | "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", 198 | "dev": true 199 | }, 200 | "fecha": { 201 | "version": "2.3.3", 202 | "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", 203 | "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==", 204 | "dev": true 205 | }, 206 | "for-each": { 207 | "version": "0.3.3", 208 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 209 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 210 | "dev": true, 211 | "requires": { 212 | "is-callable": "^1.1.3" 213 | } 214 | }, 215 | "function-bind": { 216 | "version": "1.1.1", 217 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 218 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 219 | "dev": true 220 | }, 221 | "get-intrinsic": { 222 | "version": "1.1.3", 223 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 224 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 225 | "dev": true, 226 | "requires": { 227 | "function-bind": "^1.1.1", 228 | "has": "^1.0.3", 229 | "has-symbols": "^1.0.3" 230 | } 231 | }, 232 | "gopd": { 233 | "version": "1.0.1", 234 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 235 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 236 | "dev": true, 237 | "requires": { 238 | "get-intrinsic": "^1.1.3" 239 | } 240 | }, 241 | "has": { 242 | "version": "1.0.3", 243 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 244 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 245 | "dev": true, 246 | "requires": { 247 | "function-bind": "^1.1.1" 248 | } 249 | }, 250 | "has-symbols": { 251 | "version": "1.0.3", 252 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 253 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 254 | "dev": true 255 | }, 256 | "has-tostringtag": { 257 | "version": "1.0.0", 258 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 259 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 260 | "dev": true, 261 | "requires": { 262 | "has-symbols": "^1.0.2" 263 | } 264 | }, 265 | "ieee754": { 266 | "version": "1.1.13", 267 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 268 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", 269 | "dev": true 270 | }, 271 | "inherits": { 272 | "version": "2.0.4", 273 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 274 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 275 | "dev": true 276 | }, 277 | "is-arguments": { 278 | "version": "1.1.1", 279 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 280 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 281 | "dev": true, 282 | "requires": { 283 | "call-bind": "^1.0.2", 284 | "has-tostringtag": "^1.0.0" 285 | } 286 | }, 287 | "is-arrayish": { 288 | "version": "0.3.2", 289 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 290 | "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", 291 | "dev": true 292 | }, 293 | "is-callable": { 294 | "version": "1.2.7", 295 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", 296 | "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", 297 | "dev": true 298 | }, 299 | "is-generator-function": { 300 | "version": "1.0.10", 301 | "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", 302 | "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", 303 | "dev": true, 304 | "requires": { 305 | "has-tostringtag": "^1.0.0" 306 | } 307 | }, 308 | "is-stream": { 309 | "version": "1.1.0", 310 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 311 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 312 | "dev": true 313 | }, 314 | "is-typed-array": { 315 | "version": "1.1.10", 316 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", 317 | "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", 318 | "dev": true, 319 | "requires": { 320 | "available-typed-arrays": "^1.0.5", 321 | "call-bind": "^1.0.2", 322 | "for-each": "^0.3.3", 323 | "gopd": "^1.0.1", 324 | "has-tostringtag": "^1.0.0" 325 | } 326 | }, 327 | "isarray": { 328 | "version": "1.0.0", 329 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 330 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 331 | "dev": true 332 | }, 333 | "jmespath": { 334 | "version": "0.16.0", 335 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", 336 | "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", 337 | "dev": true 338 | }, 339 | "jose": { 340 | "version": "4.15.4", 341 | "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", 342 | "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==" 343 | }, 344 | "kuler": { 345 | "version": "1.0.1", 346 | "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", 347 | "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", 348 | "dev": true, 349 | "requires": { 350 | "colornames": "^1.1.1" 351 | } 352 | }, 353 | "lambda-local": { 354 | "version": "1.6.3", 355 | "resolved": "https://registry.npmjs.org/lambda-local/-/lambda-local-1.6.3.tgz", 356 | "integrity": "sha512-vyPKCs/DuR2ArScqhASN7WZ4rFxuWIS/Rml/EE7dowzhBwv/lsZGc/KAEtQATEma8IV+5bk0kFKxlAmTjScPVA==", 357 | "dev": true, 358 | "requires": { 359 | "aws-sdk": "^2.488.0", 360 | "commander": "^2.20.0", 361 | "dotenv": "^8.0.0", 362 | "mute": "^2.0.6", 363 | "winston": "^3.2.1" 364 | }, 365 | "dependencies": { 366 | "dotenv": { 367 | "version": "8.2.0", 368 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 369 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", 370 | "dev": true 371 | } 372 | } 373 | }, 374 | "lodash": { 375 | "version": "4.17.21", 376 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 377 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 378 | "dev": true 379 | }, 380 | "logform": { 381 | "version": "2.1.2", 382 | "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", 383 | "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", 384 | "dev": true, 385 | "requires": { 386 | "colors": "^1.2.1", 387 | "fast-safe-stringify": "^2.0.4", 388 | "fecha": "^2.3.3", 389 | "ms": "^2.1.1", 390 | "triple-beam": "^1.3.0" 391 | } 392 | }, 393 | "ms": { 394 | "version": "2.1.2", 395 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 396 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 397 | "dev": true 398 | }, 399 | "mute": { 400 | "version": "2.0.6", 401 | "resolved": "https://registry.npmjs.org/mute/-/mute-2.0.6.tgz", 402 | "integrity": "sha1-i/EPHyheo4ydsmML/2dcEUyXPtU=", 403 | "dev": true 404 | }, 405 | "one-time": { 406 | "version": "0.0.4", 407 | "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", 408 | "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", 409 | "dev": true 410 | }, 411 | "process-nextick-args": { 412 | "version": "2.0.1", 413 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 414 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 415 | "dev": true 416 | }, 417 | "punycode": { 418 | "version": "1.3.2", 419 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 420 | "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", 421 | "dev": true 422 | }, 423 | "querystring": { 424 | "version": "0.2.0", 425 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 426 | "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", 427 | "dev": true 428 | }, 429 | "readable-stream": { 430 | "version": "2.3.6", 431 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 432 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 433 | "dev": true, 434 | "requires": { 435 | "core-util-is": "~1.0.0", 436 | "inherits": "~2.0.3", 437 | "isarray": "~1.0.0", 438 | "process-nextick-args": "~2.0.0", 439 | "safe-buffer": "~5.1.1", 440 | "string_decoder": "~1.1.1", 441 | "util-deprecate": "~1.0.1" 442 | }, 443 | "dependencies": { 444 | "safe-buffer": { 445 | "version": "5.1.2", 446 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 447 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 448 | "dev": true 449 | } 450 | } 451 | }, 452 | "sax": { 453 | "version": "1.2.1", 454 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 455 | "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", 456 | "dev": true 457 | }, 458 | "simple-swizzle": { 459 | "version": "0.2.2", 460 | "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 461 | "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", 462 | "dev": true, 463 | "requires": { 464 | "is-arrayish": "^0.3.1" 465 | } 466 | }, 467 | "stack-trace": { 468 | "version": "0.0.10", 469 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 470 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", 471 | "dev": true 472 | }, 473 | "string_decoder": { 474 | "version": "1.1.1", 475 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 476 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 477 | "dev": true, 478 | "requires": { 479 | "safe-buffer": "~5.1.0" 480 | }, 481 | "dependencies": { 482 | "safe-buffer": { 483 | "version": "5.1.2", 484 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 485 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 486 | "dev": true 487 | } 488 | } 489 | }, 490 | "text-hex": { 491 | "version": "1.0.0", 492 | "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", 493 | "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", 494 | "dev": true 495 | }, 496 | "triple-beam": { 497 | "version": "1.3.0", 498 | "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", 499 | "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", 500 | "dev": true 501 | }, 502 | "url": { 503 | "version": "0.10.3", 504 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 505 | "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", 506 | "dev": true, 507 | "requires": { 508 | "punycode": "1.3.2", 509 | "querystring": "0.2.0" 510 | } 511 | }, 512 | "util": { 513 | "version": "0.12.5", 514 | "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", 515 | "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", 516 | "dev": true, 517 | "requires": { 518 | "inherits": "^2.0.3", 519 | "is-arguments": "^1.0.4", 520 | "is-generator-function": "^1.0.7", 521 | "is-typed-array": "^1.1.3", 522 | "which-typed-array": "^1.1.2" 523 | } 524 | }, 525 | "util-deprecate": { 526 | "version": "1.0.2", 527 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 528 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 529 | "dev": true 530 | }, 531 | "uuid": { 532 | "version": "8.0.0", 533 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", 534 | "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", 535 | "dev": true 536 | }, 537 | "which-typed-array": { 538 | "version": "1.1.9", 539 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", 540 | "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", 541 | "dev": true, 542 | "requires": { 543 | "available-typed-arrays": "^1.0.5", 544 | "call-bind": "^1.0.2", 545 | "for-each": "^0.3.3", 546 | "gopd": "^1.0.1", 547 | "has-tostringtag": "^1.0.0", 548 | "is-typed-array": "^1.1.10" 549 | } 550 | }, 551 | "winston": { 552 | "version": "3.2.1", 553 | "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", 554 | "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", 555 | "dev": true, 556 | "requires": { 557 | "async": "^2.6.1", 558 | "diagnostics": "^1.1.1", 559 | "is-stream": "^1.1.0", 560 | "logform": "^2.1.1", 561 | "one-time": "0.0.4", 562 | "readable-stream": "^3.1.1", 563 | "stack-trace": "0.0.x", 564 | "triple-beam": "^1.3.0", 565 | "winston-transport": "^4.3.0" 566 | }, 567 | "dependencies": { 568 | "readable-stream": { 569 | "version": "3.4.0", 570 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", 571 | "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", 572 | "dev": true, 573 | "requires": { 574 | "inherits": "^2.0.3", 575 | "string_decoder": "^1.1.1", 576 | "util-deprecate": "^1.0.1" 577 | } 578 | } 579 | } 580 | }, 581 | "winston-transport": { 582 | "version": "4.3.0", 583 | "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", 584 | "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", 585 | "dev": true, 586 | "requires": { 587 | "readable-stream": "^2.3.6", 588 | "triple-beam": "^1.2.0" 589 | } 590 | }, 591 | "xml2js": { 592 | "version": "0.5.0", 593 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", 594 | "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", 595 | "dev": true, 596 | "requires": { 597 | "sax": ">=0.6.0", 598 | "xmlbuilder": "~11.0.0" 599 | } 600 | }, 601 | "xmlbuilder": { 602 | "version": "11.0.1", 603 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 604 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", 605 | "dev": true 606 | } 607 | } 608 | } 609 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-auth0-authenticator", 3 | "version": "0.0.2", 4 | "description": "An AWS Lambda function to provide an Auth0 Custom Authenticator for AWS API Gateway", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/lambda-local --timeout 300 --lambdapath index.js --eventpath event.json", 8 | "zip": "rm -f lambda-auth0-authorizer.zip ; zip lambda-auth0-authorizer.zip -r *.js *.json .env node_modules/" 9 | }, 10 | "author": "Jason Haines", 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "auth0": "^4.1.0", 14 | "bluebird": "^3.4.6", 15 | "dotenv": "^2.0.0" 16 | }, 17 | "devDependencies": { 18 | "aws-sdk": "^2.1354.0", 19 | "lambda-local": "^1.6.3" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/jghaines/lambda-auth0-authorizer.git" 24 | }, 25 | "keywords": [ 26 | "aws", 27 | "api-gateway", 28 | "auth0", 29 | "custom-authorizer", 30 | "authentication", 31 | "lambda" 32 | ], 33 | "bugs": { 34 | "url": "https://github.com/jghaines/lambda-auth0-authorizer/issues" 35 | }, 36 | "homepage": "https://github.com/jghaines/lambda-auth0-authorizer#readme" 37 | } 38 | -------------------------------------------------------------------------------- /policyDocument.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "Stmt1459758003000", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "execute-api:Invoke" 9 | ], 10 | "Resource": [ 11 | "arn:aws:execute-api:::///" 12 | ] 13 | } 14 | ] 15 | } 16 | --------------------------------------------------------------------------------