├── README.md ├── 5.3_adding_lambda_authorizers ├── requirements.txt ├── frontend │ ├── auth_config.json │ ├── css │ │ └── main.css │ ├── js │ │ ├── auth0.js │ │ └── app.js │ └── index.html ├── package.json ├── backend │ ├── get_song_vote_counts.py │ ├── record_song_vote.py │ ├── get_token.py │ ├── auth.py │ └── verify_token.py └── serverless.yml ├── 5.4_checking_for_api_scopes ├── requirements.txt ├── frontend │ ├── auth_config.json │ ├── css │ │ └── main.css │ ├── js │ │ ├── auth0.js │ │ └── app.js │ └── index.html ├── package.json ├── backend │ ├── get_song_vote_counts.py │ ├── get_token.py │ ├── record_song_vote.py │ ├── scope_check.py │ ├── auth.py │ └── verify_token.py └── serverless.yml ├── 4.1_adding_dynamodb_table_resource ├── frontend │ └── index.html ├── package.json ├── handler.py └── serverless.yml ├── 3.2_deploying_websites_with_serverless-framework_plugins ├── frontend │ └── index.html ├── package.json ├── serverless.yml └── handler.py ├── 4.2_adding_record_song_vote_function ├── record-song-vote-test-event.json ├── package.json ├── backend │ └── record_song_vote.py └── serverless.yml ├── 5.2_integrating_auth0_into_frontend ├── frontend │ ├── auth_config.json │ ├── css │ │ └── main.css │ ├── js │ │ ├── app.js │ │ └── auth0.js │ └── index.html ├── package.json ├── backend │ ├── get_song_vote_counts.py │ └── record_song_vote.py └── serverless.yml ├── 4.4_adding_http_api_endpoint ├── package.json ├── backend │ ├── get_song_vote_counts.py │ └── record_song_vote.py └── serverless.yml ├── 4.5_integrating_api_frontend ├── package.json ├── frontend │ ├── css │ │ └── main.css │ ├── js │ │ └── app.js │ └── index.html ├── backend │ ├── get_song_vote_counts.py │ └── record_song_vote.py └── serverless.yml └── 4.3_adding_get_song_votes_function ├── package.json ├── backend ├── get_song_vote_counts.py └── record_song_vote.py └── serverless.yml /README.md: -------------------------------------------------------------------------------- 1 | # serverless-learn-serverlessjams 2 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/requirements.txt: -------------------------------------------------------------------------------- 1 | python-jose -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/requirements.txt: -------------------------------------------------------------------------------- 1 | python-jose 2 | requests -------------------------------------------------------------------------------- /4.1_adding_dynamodb_table_resource/frontend/index.html: -------------------------------------------------------------------------------- 1 | Hello again serverless! -------------------------------------------------------------------------------- /3.2_deploying_websites_with_serverless-framework_plugins/frontend/index.html: -------------------------------------------------------------------------------- 1 | Hello again serverless! -------------------------------------------------------------------------------- /4.2_adding_record_song_vote_function/record-song-vote-test-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "{\"songName\": \"Fernando's Song\"}" 3 | } -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/frontend/auth_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "EXAMPLE_REPLACE_ME.auth0.com", 3 | "clientId": "REPLACE_ME" 4 | } -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/frontend/auth_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "EXAMPLE_REPLACE_ME.auth0.com", 3 | "clientId": "REPLACE_ME", 4 | "audience": "REPLACE_ME" 5 | } -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/frontend/auth_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "EXAMPLE_REPLACE_ME.auth0.com", 3 | "clientId": "REPLACE_ME", 4 | "audience": "REPLACE_ME" 5 | } -------------------------------------------------------------------------------- /4.4_adding_http_api_endpoint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /4.5_integrating_api_frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/frontend/css/main.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | 5 | label { 6 | margin-bottom: 10px; 7 | display: block; 8 | } 9 | 10 | body { 11 | background: #1B1C1D; 12 | } -------------------------------------------------------------------------------- /4.5_integrating_api_frontend/frontend/css/main.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | 5 | label { 6 | margin-bottom: 10px; 7 | display: block; 8 | } 9 | 10 | body { 11 | background: #1B1C1D; 12 | } -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/frontend/css/main.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | 5 | label { 6 | margin-bottom: 10px; 7 | display: block; 8 | } 9 | 10 | body { 11 | background: #1B1C1D; 12 | } -------------------------------------------------------------------------------- /4.1_adding_dynamodb_table_resource/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /4.2_adding_record_song_vote_function/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /4.3_adding_get_song_votes_function/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/frontend/css/main.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | 5 | label { 6 | margin-bottom: 10px; 7 | display: block; 8 | } 9 | 10 | body { 11 | background: #1B1C1D; 12 | } -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /3.2_deploying_websites_with_serverless-framework_plugins/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | }, 8 | "devDependencies": { 9 | "serverless-finch": "^2.5.2", 10 | "serverless-python-requirements": "^5.0.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-jams", 3 | "description": "", 4 | "version": "0.1.0", 5 | "dependencies": { 6 | "serverless-finch": "^2.5.2" 7 | }, 8 | "devDependencies": { 9 | "serverless-finch": "^2.5.2", 10 | "serverless-python-requirements": "^5.0.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /3.2_deploying_websites_with_serverless-framework_plugins/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.7 8 | 9 | functions: 10 | hello: 11 | handler: handler.hello 12 | 13 | plugins: 14 | - serverless-finch 15 | 16 | custom: 17 | client: 18 | bucketName: fernandotest-12trafsd213 19 | distributionFolder: frontend 20 | errorDocument: index.html 21 | -------------------------------------------------------------------------------- /4.1_adding_dynamodb_table_resource/handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def hello(event, context): 5 | body = { 6 | "message": "Go Serverless v1.0! Your function executed successfully!", 7 | "input": event 8 | } 9 | 10 | response = { 11 | "statusCode": 200, 12 | "body": json.dumps(body) 13 | } 14 | 15 | return response 16 | 17 | # Use this code if you don't use the http event with the LAMBDA-PROXY 18 | # integration 19 | """ 20 | return { 21 | "message": "Go Serverless v1.0! Your function executed successfully!", 22 | "event": event 23 | } 24 | """ 25 | -------------------------------------------------------------------------------- /3.2_deploying_websites_with_serverless-framework_plugins/handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def hello(event, context): 5 | body = { 6 | "message": "Go Serverless v1.0! Your function executed successfully!", 7 | "input": event 8 | } 9 | 10 | response = { 11 | "statusCode": 200, 12 | "body": json.dumps(body) 13 | } 14 | 15 | return response 16 | 17 | # Use this code if you don't use the http event with the LAMBDA-PROXY 18 | # integration 19 | """ 20 | return { 21 | "message": "Go Serverless v1.0! Your function executed successfully!", 22 | "event": event 23 | } 24 | """ 25 | -------------------------------------------------------------------------------- /4.4_adding_http_api_endpoint/backend/get_song_vote_counts.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | result = dynamodb.scan( 9 | TableName=os.environ['DYNAMODB_TABLE'], 10 | ) 11 | song_votes = [] 12 | for item in result["Items"]: 13 | song_votes.append({ 14 | "songName": item["songName"]["S"], 15 | "votes": item["votes"]["N"] 16 | }) 17 | response = { 18 | "statusCode": 200, 19 | "headers": {"Access-Control-Allow-Origin": "*"}, 20 | "body": json.dumps(song_votes) 21 | } 22 | return response -------------------------------------------------------------------------------- /4.5_integrating_api_frontend/backend/get_song_vote_counts.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | result = dynamodb.scan( 9 | TableName=os.environ['DYNAMODB_TABLE'], 10 | ) 11 | song_votes = [] 12 | for item in result["Items"]: 13 | song_votes.append({ 14 | "songName": item["songName"]["S"], 15 | "votes": item["votes"]["N"] 16 | }) 17 | response = { 18 | "statusCode": 200, 19 | "headers": {"Access-Control-Allow-Origin": "*"}, 20 | "body": json.dumps(song_votes) 21 | } 22 | return response -------------------------------------------------------------------------------- /4.3_adding_get_song_votes_function/backend/get_song_vote_counts.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | result = dynamodb.scan( 9 | TableName=os.environ['DYNAMODB_TABLE'], 10 | ) 11 | song_votes = [] 12 | for item in result["Items"]: 13 | song_votes.append({ 14 | "songName": item["songName"]["S"], 15 | "votes": item["votes"]["N"] 16 | }) 17 | response = { 18 | "statusCode": 200, 19 | "headers": {"Access-Control-Allow-Origin": "*"}, 20 | "body": json.dumps(song_votes) 21 | } 22 | return response -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/backend/get_song_vote_counts.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | result = dynamodb.scan( 9 | TableName=os.environ['DYNAMODB_TABLE'], 10 | ) 11 | song_votes = [] 12 | for item in result["Items"]: 13 | song_votes.append({ 14 | "songName": item["songName"]["S"], 15 | "votes": item["votes"]["N"] 16 | }) 17 | response = { 18 | "statusCode": 200, 19 | "headers": {"Access-Control-Allow-Origin": "*"}, 20 | "body": json.dumps(song_votes) 21 | } 22 | return response -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/backend/get_song_vote_counts.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | 8 | def handler(event, context): 9 | result = dynamodb.scan( 10 | TableName=os.environ['DYNAMODB_TABLE'] 11 | ) 12 | song_votes = [] 13 | for item in result["Items"]: 14 | song_votes.append({ 15 | "songName": item["songName"]["S"], 16 | "votes": item["votes"]["N"] 17 | }) 18 | response = { 19 | "statusCode": 200, 20 | "headers": {"Access-Control-Allow-Origin":"*"}, 21 | "body": json.dumps(song_votes) 22 | } 23 | return response 24 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/backend/get_song_vote_counts.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | 8 | def handler(event, context): 9 | result = dynamodb.scan( 10 | TableName=os.environ['DYNAMODB_TABLE'] 11 | ) 12 | song_votes = [] 13 | for item in result["Items"]: 14 | song_votes.append({ 15 | "songName": item["songName"]["S"], 16 | "votes": item["votes"]["N"] 17 | }) 18 | response = { 19 | "statusCode": 200, 20 | "headers": {"Access-Control-Allow-Origin":"*"}, 21 | "body": json.dumps(song_votes) 22 | } 23 | return response 24 | -------------------------------------------------------------------------------- /4.2_adding_record_song_vote_function/backend/record_song_vote.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | song_name = json.loads(event['body'])['songName'] 9 | result = dynamodb.update_item( 10 | TableName=os.environ['DYNAMODB_TABLE'], 11 | Key={ 12 | 'songName':{'S': song_name} 13 | }, 14 | UpdateExpression='ADD votes :inc', 15 | ExpressionAttributeValues={ 16 | ':inc': {'N': '1'} 17 | }, 18 | ReturnValues="UPDATED_NEW" 19 | ) 20 | response = { 21 | "statusCode": 200, 22 | "headers": {"Access-Control-Allow-Origin": "*"}, 23 | "body": json.dumps({"votes": result["Attributes"]["votes"]["N"]}) 24 | } 25 | return response -------------------------------------------------------------------------------- /4.4_adding_http_api_endpoint/backend/record_song_vote.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | song_name = json.loads(event['body'])['songName'] 9 | result = dynamodb.update_item( 10 | TableName=os.environ['DYNAMODB_TABLE'], 11 | Key={ 12 | 'songName':{'S': song_name} 13 | }, 14 | UpdateExpression='ADD votes :inc', 15 | ExpressionAttributeValues={ 16 | ':inc': {'N': '1'} 17 | }, 18 | ReturnValues="UPDATED_NEW" 19 | ) 20 | response = { 21 | "statusCode": 200, 22 | "headers": {"Access-Control-Allow-Origin": "*"}, 23 | "body": json.dumps({"votes": result["Attributes"]["votes"]["N"]}) 24 | } 25 | return response 26 | -------------------------------------------------------------------------------- /4.5_integrating_api_frontend/backend/record_song_vote.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | song_name = json.loads(event['body'])['songName'] 9 | result = dynamodb.update_item( 10 | TableName=os.environ['DYNAMODB_TABLE'], 11 | Key={ 12 | 'songName':{'S': song_name} 13 | }, 14 | UpdateExpression='ADD votes :inc', 15 | ExpressionAttributeValues={ 16 | ':inc': {'N': '1'} 17 | }, 18 | ReturnValues="UPDATED_NEW" 19 | ) 20 | response = { 21 | "statusCode": 200, 22 | "headers": {"Access-Control-Allow-Origin": "*"}, 23 | "body": json.dumps({"votes": result["Attributes"]["votes"]["N"]}) 24 | } 25 | return response 26 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/backend/record_song_vote.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | song_name = json.loads(event['body'])['songName'] 9 | result = dynamodb.update_item( 10 | TableName=os.environ['DYNAMODB_TABLE'], 11 | Key={ 12 | 'songName':{'S': song_name} 13 | }, 14 | UpdateExpression='ADD votes :inc', 15 | ExpressionAttributeValues={ 16 | ':inc': {'N': '1'} 17 | }, 18 | ReturnValues="UPDATED_NEW" 19 | ) 20 | response = { 21 | "statusCode": 200, 22 | "headers": {"Access-Control-Allow-Origin": "*"}, 23 | "body": json.dumps({"votes": result["Attributes"]["votes"]["N"]}) 24 | } 25 | return response 26 | -------------------------------------------------------------------------------- /4.3_adding_get_song_votes_function/backend/record_song_vote.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | song_name = json.loads(event['body'])['songName'] 9 | result = dynamodb.update_item( 10 | TableName=os.environ['DYNAMODB_TABLE'], 11 | Key={ 12 | 'songName':{'S': song_name} 13 | }, 14 | UpdateExpression='ADD votes :inc', 15 | ExpressionAttributeValues={ 16 | ':inc': {'N': '1'} 17 | }, 18 | ReturnValues="UPDATED_NEW" 19 | ) 20 | response = { 21 | "statusCode": 200, 22 | "headers": {"Access-Control-Allow-Origin": "*"}, 23 | "body": json.dumps({"votes": result["Attributes"]["votes"]["N"]}) 24 | } 25 | return response 26 | -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/backend/record_song_vote.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handler(event, context): 8 | song_name = json.loads(event['body'])['songName'] 9 | result = dynamodb.update_item( 10 | TableName=os.environ['DYNAMODB_TABLE'], 11 | Key={ 12 | 'songName':{'S': song_name} 13 | }, 14 | UpdateExpression='ADD votes :inc', 15 | ExpressionAttributeValues={ 16 | ':inc': {'N': '1'} 17 | }, 18 | ReturnValues="UPDATED_NEW" 19 | ) 20 | response = { 21 | "statusCode": 200, 22 | "headers": {"Access-Control-Allow-Origin": "*"}, 23 | "body": json.dumps({"votes": result["Attributes"]["votes"]["N"]}) 24 | } 25 | return response 26 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/backend/get_token.py: -------------------------------------------------------------------------------- 1 | def get_token(event): 2 | # whole_auth_token should look like: 3 | # "Bearer SOME_CODE_GIBBERISH6r712fyasd.othergibberish.finalgibberish" 4 | whole_auth_token = event.get('authorizationToken') 5 | print('Client token: ' + whole_auth_token) 6 | print('Method ARN: ' + event['methodArn']) 7 | if not whole_auth_token: 8 | raise Exception('Unauthorized') 9 | token_parts = whole_auth_token.split(' ') 10 | auth_token = token_parts[1] 11 | token_method = token_parts[0] 12 | if not (token_method.lower() == 'bearer' and auth_token): 13 | print("Failing due to invalid token_method or missing auth_token") 14 | raise Exception('Unauthorized') 15 | # At this point we've confirmed the token format looks ok 16 | # So return the unverified token 17 | return auth_token 18 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/backend/get_token.py: -------------------------------------------------------------------------------- 1 | def get_token(event): 2 | # whole_auth_token should look like: 3 | # "Bearer SOME_CODE_GIBBERISH6r712fyasd.othergibberish.finalgibberish" 4 | whole_auth_token = event.get('authorizationToken') 5 | print('Client token: ' + whole_auth_token) 6 | print('Method ARN: ' + event['methodArn']) 7 | if not whole_auth_token: 8 | raise Exception('Unauthorized') 9 | token_parts = whole_auth_token.split(' ') 10 | auth_token = token_parts[1] 11 | token_method = token_parts[0] 12 | if not (token_method.lower() == 'bearer' and auth_token): 13 | print("Failing due to invalid token_method or missing auth_token") 14 | raise Exception('Unauthorized') 15 | # At this point we've confirmed the token format looks ok 16 | # So return the unverified token 17 | return auth_token 18 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/backend/record_song_vote.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import os 3 | import json 4 | 5 | from scope_check import scope_check 6 | 7 | dynamodb = boto3.client('dynamodb') 8 | 9 | def handler(event, context): 10 | scope_check(event, required_scopes='write:votes') 11 | song_name = json.loads(event['body'])['songName'] 12 | result = dynamodb.update_item( 13 | TableName=os.environ['DYNAMODB_TABLE'], 14 | Key={ 15 | 'songName':{'S': song_name} 16 | }, 17 | UpdateExpression='ADD votes :inc', 18 | ExpressionAttributeValues={ 19 | ':inc': {'N': '1'} 20 | }, 21 | ReturnValues="UPDATED_NEW" 22 | ) 23 | response = { 24 | "statusCode": 200, 25 | "headers": {"Access-Control-Allow-Origin": "*"}, 26 | "body": json.dumps({"votes": result["Attributes"]["votes"]["N"]}) 27 | } 28 | return response 29 | -------------------------------------------------------------------------------- /4.1_adding_dynamodb_table_resource/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.7 8 | environment: 9 | DYNAMODB_TABLE: serverlessjams-voteCounts 10 | iamRoleStatements: 11 | - Effect: "Allow" 12 | Action: 13 | - dynamodb:Scan 14 | - dynamodb:UpdateItem 15 | - dynamodb:PutItem 16 | Resource: "arn:aws:dynamodb:*:*:table/${self:provider.environment.DYNAMODB_TABLE}" 17 | 18 | functions: 19 | hello: 20 | handler: handler.hello 21 | 22 | resources: 23 | Resources: 24 | songsTable: 25 | Type: AWS::DynamoDB::Table 26 | Properties: 27 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 28 | AttributeDefinitions: 29 | - AttributeName: songName 30 | AttributeType: S 31 | KeySchema: 32 | - AttributeName: songName 33 | KeyType: HASH 34 | ProvisionedThroughput: 35 | ReadCapacityUnits: 1 36 | WriteCapacityUnits: 1 37 | 38 | plugins: 39 | - serverless-finch 40 | 41 | custom: 42 | client: 43 | bucketName: fernandotest-12trafsd213 44 | distributionFolder: frontend 45 | errorDocument: index.html 46 | -------------------------------------------------------------------------------- /4.2_adding_record_song_vote_function/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.7 8 | environment: 9 | DYNAMODB_TABLE: serverlessjams-voteCounts 10 | iamRoleStatements: 11 | - Effect: "Allow" 12 | Action: 13 | - dynamodb:Scan 14 | - dynamodb:UpdateItem 15 | - dynamodb:PutItem 16 | Resource: "arn:aws:dynamodb:*:*:table/${self:provider.environment.DYNAMODB_TABLE}" 17 | 18 | functions: 19 | recordSongVote: 20 | handler: backend/record_song_vote.handler 21 | 22 | resources: 23 | Resources: 24 | songsTable: 25 | Type: AWS::DynamoDB::Table 26 | Properties: 27 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 28 | AttributeDefinitions: 29 | - AttributeName: songName 30 | AttributeType: S 31 | KeySchema: 32 | - AttributeName: songName 33 | KeyType: HASH 34 | ProvisionedThroughput: 35 | ReadCapacityUnits: 1 36 | WriteCapacityUnits: 1 37 | 38 | plugins: 39 | - serverless-finch 40 | 41 | custom: 42 | client: 43 | bucketName: fernandotest-12trafsd213 44 | distributionFolder: frontend 45 | errorDocument: index.html 46 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/backend/scope_check.py: -------------------------------------------------------------------------------- 1 | 2 | def scope_check(event, required_scopes=None): 3 | print(event) 4 | if not required_scopes: 5 | raise Exception('No expected scopes specified') 6 | if not event['requestContext']['authorizer']['scopes']: 7 | raise Exception('No scopes provided') 8 | provided_scopes = event['requestContext']['authorizer']['scopes'] 9 | if isinstance(required_scopes, str): 10 | # There is only one required_scopes scope to check 11 | if required_scopes in provided_scopes: 12 | print('Scope check passed for: ' + required_scopes) 13 | else: 14 | raise Exception('Scope check failed for: ' + required_scopes) 15 | if isinstance(required_scopes, list): 16 | # There are one or more scopes to check in an array 17 | # If there is more than one provided scope it should 18 | # be separated with a pipe character. 19 | provided_scopes = provided_scopes.split('|') 20 | if set(required_scopes).issubset(provided_scopes): 21 | # If the required scopes are all included in the provided scopes 22 | print('Scope check passed for: ' + required_scopes) 23 | else: 24 | raise Exception('Scope check failed for: ' + required_scopes) 25 | 26 | -------------------------------------------------------------------------------- /4.3_adding_get_song_votes_function/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.7 8 | environment: 9 | DYNAMODB_TABLE: serverlessjams-voteCounts 10 | iamRoleStatements: 11 | - Effect: "Allow" 12 | Action: 13 | - dynamodb:Scan 14 | - dynamodb:UpdateItem 15 | - dynamodb:PutItem 16 | Resource: "arn:aws:dynamodb:*:*:table/${self:provider.environment.DYNAMODB_TABLE}" 17 | 18 | functions: 19 | recordSongVote: 20 | handler: backend/record_song_vote.handler 21 | getSongVoteCounts: 22 | handler: backend/get_song_vote_counts.handler 23 | 24 | resources: 25 | Resources: 26 | songsTable: 27 | Type: AWS::DynamoDB::Table 28 | Properties: 29 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 30 | AttributeDefinitions: 31 | - AttributeName: songName 32 | AttributeType: S 33 | KeySchema: 34 | - AttributeName: songName 35 | KeyType: HASH 36 | ProvisionedThroughput: 37 | ReadCapacityUnits: 1 38 | WriteCapacityUnits: 1 39 | 40 | plugins: 41 | - serverless-finch 42 | 43 | custom: 44 | client: 45 | bucketName: fernandotest-12trafsd213 46 | distributionFolder: frontend 47 | errorDocument: index.html 48 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/backend/auth.py: -------------------------------------------------------------------------------- 1 | from get_token import get_token 2 | from verify_token import verify_token 3 | 4 | def handler(event, context): 5 | print(event) 6 | print(context) 7 | token = get_token(event) 8 | id_token = verify_token(token) 9 | print(id_token) 10 | if id_token and id_token.get('permissions'): 11 | scopes = '|'.join(id_token['permissions']) 12 | policy = generate_policy( 13 | id_token['sub'], 14 | 'Allow', 15 | event['methodArn'], 16 | scopes=scopes 17 | ) 18 | return policy 19 | else: 20 | policy = generate_policy( 21 | id_token['sub'], 22 | "Deny", 23 | event['methodArn'] 24 | ) 25 | return policy 26 | 27 | def generate_policy(principal_id, effect, resource, scopes=None): 28 | policy = { 29 | 'principalId': principal_id, 30 | 'policyDocument': { 31 | 'Version': '2012-10-17', 32 | 'Statement': [ 33 | { 34 | "Action": "execute-api:Invoke", 35 | "Effect": effect, 36 | "Resource": resource 37 | } 38 | ] 39 | } 40 | } 41 | if scopes: 42 | policy['context'] = {'scopes': scopes} 43 | return policy 44 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/backend/auth.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | 4 | from get_token import get_token 5 | from verify_token import verify_token 6 | 7 | AUTH0_DOMAIN = os.environ.get("AUTH0_DOMAIN") 8 | 9 | 10 | def handler(event, context): 11 | print(event) 12 | print(context) 13 | token = get_token(event) 14 | id_token = verify_token(token) 15 | print(id_token) 16 | userinfo = requests.get( 17 | 'https://' + AUTH0_DOMAIN + '/userinfo', 18 | headers={"Authorization": "Bearer " + token} 19 | ).json() 20 | if id_token and userinfo['email_verified']: 21 | policy = generate_policy( 22 | id_token['sub'], 23 | 'Allow', 24 | event['methodArn'] 25 | ) 26 | return policy 27 | else: 28 | policy = generate_policy( 29 | id_token['sub'], 30 | "Deny", 31 | event['methodArn'] 32 | ) 33 | return policy 34 | 35 | def generate_policy(principal_id, effect, resource, scopes=None): 36 | policy = { 37 | 'principalId': principal_id, 38 | 'policyDocument': { 39 | 'Version': '2012-10-17', 40 | 'Statement': [ 41 | { 42 | "Action": "execute-api:Invoke", 43 | "Effect": effect, 44 | "Resource": resource 45 | } 46 | ] 47 | } 48 | } 49 | if scopes: 50 | policy['context'] = {'scopes': scopes} 51 | return policy 52 | -------------------------------------------------------------------------------- /4.4_adding_http_api_endpoint/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.7 8 | environment: 9 | DYNAMODB_TABLE: serverlessjams-voteCounts 10 | iamRoleStatements: 11 | - Effect: "Allow" 12 | Action: 13 | - dynamodb:Scan 14 | - dynamodb:UpdateItem 15 | - dynamodb:PutItem 16 | Resource: "arn:aws:dynamodb:*:*:table/${self:provider.environment.DYNAMODB_TABLE}" 17 | 18 | functions: 19 | recordSongVote: 20 | handler: backend/record_song_vote.handler 21 | events: 22 | - http: 23 | path: song/vote 24 | method: post 25 | cors: true 26 | getSongVoteCounts: 27 | handler: backend/get_song_vote_counts.handler 28 | events: 29 | - http: 30 | path: votes 31 | method: get 32 | cors: true 33 | resources: 34 | Resources: 35 | songsTable: 36 | Type: AWS::DynamoDB::Table 37 | Properties: 38 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 39 | AttributeDefinitions: 40 | - AttributeName: songName 41 | AttributeType: S 42 | KeySchema: 43 | - AttributeName: songName 44 | KeyType: HASH 45 | ProvisionedThroughput: 46 | ReadCapacityUnits: 1 47 | WriteCapacityUnits: 1 48 | 49 | plugins: 50 | - serverless-finch 51 | 52 | custom: 53 | client: 54 | bucketName: fernandotest-12trafsd213 55 | distributionFolder: frontend 56 | errorDocument: index.html 57 | -------------------------------------------------------------------------------- /4.5_integrating_api_frontend/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.7 8 | environment: 9 | DYNAMODB_TABLE: serverlessjams-voteCounts 10 | iamRoleStatements: 11 | - Effect: "Allow" 12 | Action: 13 | - dynamodb:Scan 14 | - dynamodb:UpdateItem 15 | - dynamodb:PutItem 16 | Resource: "arn:aws:dynamodb:*:*:table/${self:provider.environment.DYNAMODB_TABLE}" 17 | 18 | functions: 19 | recordSongVote: 20 | handler: backend/record_song_vote.handler 21 | events: 22 | - http: 23 | path: song/vote 24 | method: post 25 | cors: true 26 | getSongVoteCounts: 27 | handler: backend/get_song_vote_counts.handler 28 | events: 29 | - http: 30 | path: votes 31 | method: get 32 | cors: true 33 | resources: 34 | Resources: 35 | songsTable: 36 | Type: AWS::DynamoDB::Table 37 | Properties: 38 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 39 | AttributeDefinitions: 40 | - AttributeName: songName 41 | AttributeType: S 42 | KeySchema: 43 | - AttributeName: songName 44 | KeyType: HASH 45 | ProvisionedThroughput: 46 | ReadCapacityUnits: 1 47 | WriteCapacityUnits: 1 48 | 49 | plugins: 50 | - serverless-finch 51 | 52 | custom: 53 | client: 54 | bucketName: fernandotest-12trafsd213 55 | distributionFolder: frontend 56 | errorDocument: index.html 57 | -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | provider: 6 | name: aws 7 | runtime: python3.7 8 | environment: 9 | DYNAMODB_TABLE: serverlessjams-voteCounts 10 | iamRoleStatements: 11 | - Effect: "Allow" 12 | Action: 13 | - dynamodb:Scan 14 | - dynamodb:UpdateItem 15 | - dynamodb:PutItem 16 | Resource: "arn:aws:dynamodb:*:*:table/${self:provider.environment.DYNAMODB_TABLE}" 17 | 18 | functions: 19 | recordSongVote: 20 | handler: backend/record_song_vote.handler 21 | events: 22 | - http: 23 | path: song/vote 24 | method: post 25 | cors: true 26 | getSongVoteCounts: 27 | handler: backend/get_song_vote_counts.handler 28 | events: 29 | - http: 30 | path: votes 31 | method: get 32 | cors: true 33 | resources: 34 | Resources: 35 | songsTable: 36 | Type: AWS::DynamoDB::Table 37 | Properties: 38 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 39 | AttributeDefinitions: 40 | - AttributeName: songName 41 | AttributeType: S 42 | KeySchema: 43 | - AttributeName: songName 44 | KeyType: HASH 45 | ProvisionedThroughput: 46 | ReadCapacityUnits: 1 47 | WriteCapacityUnits: 1 48 | 49 | plugins: 50 | - serverless-finch 51 | 52 | custom: 53 | client: 54 | bucketName: fernandotest-12trafsd213 55 | distributionFolder: frontend 56 | errorDocument: index.html 57 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/backend/verify_token.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from six.moves.urllib.request import urlopen 5 | from jose import jwt 6 | 7 | AUTH0_DOMAIN = os.environ.get("AUTH0_DOMAIN") 8 | AUTH0_API_ID = os.environ.get("AUTH0_API_ID") 9 | 10 | def verify_token(token): 11 | # Validate the token to make sure it's authentic 12 | jsonurl = urlopen("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json") 13 | jwks = json.loads(jsonurl.read()) 14 | # This currently expects the token to have three distinct sections 15 | # each separated by a period. 16 | unverified_header = jwt.get_unverified_header(token) 17 | rsa_key = {} 18 | for key in jwks["keys"]: 19 | if key["kid"] == unverified_header["kid"]: 20 | rsa_key = { 21 | "kty": key["kty"], 22 | "kid": key["kid"], 23 | "use": key["use"], 24 | "n": key["n"], 25 | "e": key["e"] 26 | } 27 | if rsa_key: 28 | try: # to validate the jwt 29 | payload = jwt.decode( 30 | token, 31 | rsa_key, 32 | algorithms=["RS256"], 33 | audience=AUTH0_API_ID, 34 | issuer="https://"+AUTH0_DOMAIN+"/" 35 | ) 36 | print("token validated successfully") 37 | return payload 38 | except jwt.ExpiredSignatureError: 39 | print("Token is expired") 40 | raise Exception('Unauthorized') 41 | except jwt.JWTClaimsError: 42 | print("Token has invalid claims") 43 | raise Exception('Unauthorized') 44 | except Exception: 45 | print("Unable to parse token") 46 | raise Exception('Unauthorized') 47 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/backend/verify_token.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from six.moves.urllib.request import urlopen 5 | from jose import jwt 6 | 7 | AUTH0_DOMAIN = os.environ.get("AUTH0_DOMAIN") 8 | AUTH0_API_ID = os.environ.get("AUTH0_API_ID") 9 | 10 | def verify_token(token): 11 | # Validate the token to make sure it's authentic 12 | jsonurl = urlopen("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json") 13 | jwks = json.loads(jsonurl.read()) 14 | # This currently expects the token to have three distinct sections 15 | # each separated by a period. 16 | unverified_header = jwt.get_unverified_header(token) 17 | rsa_key = {} 18 | for key in jwks["keys"]: 19 | if key["kid"] == unverified_header["kid"]: 20 | rsa_key = { 21 | "kty": key["kty"], 22 | "kid": key["kid"], 23 | "use": key["use"], 24 | "n": key["n"], 25 | "e": key["e"] 26 | } 27 | if rsa_key: 28 | try: # to validate the jwt 29 | payload = jwt.decode( 30 | token, 31 | rsa_key, 32 | algorithms=["RS256"], 33 | audience=AUTH0_API_ID, 34 | issuer="https://"+AUTH0_DOMAIN+"/" 35 | ) 36 | print("token validated successfully") 37 | return payload 38 | except jwt.ExpiredSignatureError: 39 | print("Token is expired") 40 | raise Exception('Unauthorized') 41 | except jwt.JWTClaimsError: 42 | print("Token has invalid claims") 43 | raise Exception('Unauthorized') 44 | except Exception: 45 | print("Unable to parse token") 46 | raise Exception('Unauthorized') 47 | -------------------------------------------------------------------------------- /4.5_integrating_api_frontend/frontend/js/app.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $('.ui.dropdown').dropdown(); 3 | refreshVoteCounts() 4 | document.getElementsByClassName("ui red button")[0].addEventListener("click", recordVote); 5 | }); 6 | 7 | var vote_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/song/vote" 8 | var get_votes_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/votes" 9 | 10 | function setVotes(songName, voteCount) { 11 | // Get div containing vote count and set the new voteCount 12 | document.getElementsByName(songName)[0].innerHTML = voteCount; 13 | } 14 | 15 | async function refreshVoteCounts() { 16 | // Get the vote counts 17 | const response = await fetch(get_votes_endpoint); 18 | const songs = await response.json(); 19 | // Iterate over all three songs and update the divs 20 | var i; 21 | for (i = 0; i < songs.length; i++){ 22 | var featured_songs = ["coderitis", "stateless", "dynamo"]; 23 | var song = songs[i] 24 | if (featured_songs.includes(song["songName"])){ 25 | console.log(song) 26 | setVotes(song["songName"], song["votes"]) 27 | } 28 | } 29 | } 30 | 31 | async function voteForSong(songName) { 32 | const response = await fetch(vote_endpoint, { 33 | method: "POST", 34 | mode: 'cors', 35 | headers: {'Content-Type': 'application/json'}, 36 | body: JSON.stringify({"songName": songName}) 37 | }) 38 | const result_json = await response.json() 39 | setVotes(songName, result_json["votes"]) 40 | } 41 | 42 | function recordVote() { 43 | if (document.getElementsByClassName("item active selected")[0]) { 44 | var selectedSong = document.getElementsByClassName("item active selected")[0].getAttribute('data-value') 45 | if (selectedSong) { 46 | voteForSong(selectedSong) 47 | console.log("voted for " + selectedSong) 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/frontend/js/app.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $('.ui.dropdown').dropdown(); 3 | refreshVoteCounts() 4 | document.getElementsByClassName("ui red button")[0].addEventListener("click", recordVote); 5 | }); 6 | 7 | var vote_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/song/vote" 8 | var get_votes_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/votes" 9 | 10 | function setVotes(songName, voteCount) { 11 | // Get div containing vote count and set the new voteCount 12 | document.getElementsByName(songName)[0].innerHTML = voteCount; 13 | } 14 | 15 | async function refreshVoteCounts() { 16 | // Get the vote counts 17 | const response = await fetch(get_votes_endpoint); 18 | const songs = await response.json(); 19 | // Iterate over all three songs and update the divs 20 | var i; 21 | for (i = 0; i < songs.length; i++){ 22 | var featured_songs = ["coderitis", "stateless", "dynamo"]; 23 | var song = songs[i] 24 | if (featured_songs.includes(song["songName"])){ 25 | console.log(song) 26 | setVotes(song["songName"], song["votes"]) 27 | } 28 | } 29 | } 30 | 31 | async function voteForSong(songName) { 32 | const response = await fetch(vote_endpoint, { 33 | method: "POST", 34 | mode: 'cors', 35 | headers: {'Content-Type': 'application/json'}, 36 | body: JSON.stringify({"songName": songName}) 37 | }) 38 | const result_json = await response.json() 39 | setVotes(songName, result_json["votes"]) 40 | } 41 | 42 | function recordVote() { 43 | if (document.getElementsByClassName("item active selected")[0]) { 44 | var selectedSong = document.getElementsByClassName("item active selected")[0].getAttribute('data-value') 45 | if (selectedSong) { 46 | voteForSong(selectedSong) 47 | console.log("voted for " + selectedSong) 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /5.2_integrating_auth0_into_frontend/frontend/js/auth0.js: -------------------------------------------------------------------------------- 1 | let auth0 = null; 2 | const fetchAuthConfig = () => fetch("/auth_config.json"); 3 | 4 | const configureClient = async () => { 5 | const response = await fetchAuthConfig(); 6 | const config = await response.json(); 7 | 8 | auth0 = await createAuth0Client({ 9 | domain: config.domain, 10 | client_id: config.clientId, 11 | }); 12 | }; 13 | 14 | const updateUI = async () => { 15 | const isAuthenticated = await auth0.isAuthenticated(); 16 | 17 | document.getElementById("btn-logout").disabled = !isAuthenticated; 18 | document.getElementById("btn-login").disabled = isAuthenticated; 19 | 20 | if (isAuthenticated) { 21 | document.getElementById("gated-content-1").classList.remove("hidden"); 22 | document.getElementById("gated-content-2").classList.remove("hidden"); 23 | 24 | const claims = await auth0.getIdTokenClaims() 25 | const pictureUrl = claims.picture 26 | 27 | document.getElementById("avatar-img").src = pictureUrl || 'https://icon-library.net/images/icon-of-music/icon-of-music-8.jpg'; 28 | document.getElementById("avatar-img-div").classList.remove("hidden") 29 | 30 | } else { 31 | document.getElementById("gated-content-1").classList.add("hidden"); 32 | document.getElementById("gated-content-2").classList.add("hidden"); 33 | } 34 | }; 35 | 36 | window.onload = async () => { 37 | await configureClient(); 38 | updateUI(); 39 | const query = window.location.search; 40 | if (query.includes("code=") && query.includes("state=")) { 41 | // Process the login state 42 | await auth0.handleRedirectCallback(); 43 | updateUI(); 44 | // Use replaceState to redirect the user away and remove the querystring parameters 45 | window.history.replaceState({}, document.title, "/"); 46 | } 47 | }; 48 | 49 | const login = async () => { 50 | await auth0.loginWithRedirect({ 51 | redirect_uri: window.location.origin 52 | }); 53 | }; 54 | 55 | const logout = () => { 56 | auth0.logout({ 57 | returnTo: window.location.origin 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | frameworkVersion: ">=1.53.0 <2.0.0" 6 | 7 | provider: 8 | name: aws 9 | runtime: python3.7 10 | region: us-east-1 11 | environment: 12 | DYNAMODB_TABLE: serverlessjams-voteCounts 13 | AUTH0_DOMAIN: EXAMPLE_REPLACE_ME.auth0.com 14 | AUTH0_API_ID: EXAMPLE_REPLACE_ME 15 | iamRoleStatements: 16 | - Effect: Allow 17 | Action: 18 | - dynamodb:Scan 19 | - dynamodb:PutItem 20 | - dynamodb:UpdateItem 21 | Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}" 22 | functions: 23 | auth: 24 | handler: backend/auth.handler 25 | recordSongVote: 26 | handler: backend/record_song_vote.handler 27 | events: 28 | - http: 29 | path: song/vote 30 | method: post 31 | authorizer: 32 | name: auth 33 | cors: true 34 | getSongVoteCounts: 35 | handler: backend/get_song_vote_counts.handler 36 | events: 37 | - http: 38 | path: votes 39 | method: get 40 | cors: true 41 | 42 | resources: 43 | Resources: 44 | usersTable: 45 | Type: AWS::DynamoDB::Table 46 | Properties: 47 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 48 | AttributeDefinitions: 49 | - AttributeName: songName 50 | AttributeType: S 51 | KeySchema: 52 | - AttributeName: songName 53 | KeyType: HASH 54 | ProvisionedThroughput: 55 | ReadCapacityUnits: 1 56 | WriteCapacityUnits: 1 57 | 58 | plugins: 59 | - serverless-finch 60 | - serverless-python-requirements 61 | 62 | custom: 63 | client: 64 | bucketName: fernandotest-12trafsd213 65 | distributionFolder: frontend 66 | errorDocument: index.html 67 | pythonRequirements: 68 | dockerizePip: non-linux 69 | 70 | package: 71 | exclude: 72 | - node_modules/** 73 | - frontend/** 74 | - package-lock.json 75 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/serverless.yml: -------------------------------------------------------------------------------- 1 | org: yourorg 2 | app: serverlessjams 3 | service: serverlessjams 4 | 5 | frameworkVersion: ">=1.53.0 <2.0.0" 6 | 7 | provider: 8 | name: aws 9 | runtime: python3.7 10 | region: us-east-1 11 | environment: 12 | DYNAMODB_TABLE: serverlessjams-voteCounts 13 | AUTH0_DOMAIN: EXAMPLE_REPLACE_ME.auth0.com 14 | AUTH0_API_ID: EXAMPLE_REPLACE_ME 15 | iamRoleStatements: 16 | - Effect: Allow 17 | Action: 18 | - dynamodb:Scan 19 | - dynamodb:PutItem 20 | - dynamodb:UpdateItem 21 | Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}" 22 | functions: 23 | auth: 24 | handler: backend/auth.handler 25 | recordSongVote: 26 | handler: backend/record_song_vote.handler 27 | events: 28 | - http: 29 | path: song/vote 30 | method: post 31 | authorizer: 32 | name: auth 33 | cors: true 34 | getSongVoteCounts: 35 | handler: backend/get_song_vote_counts.handler 36 | events: 37 | - http: 38 | path: votes 39 | method: get 40 | cors: true 41 | 42 | resources: 43 | Resources: 44 | usersTable: 45 | Type: AWS::DynamoDB::Table 46 | Properties: 47 | TableName: ${self:provider.environment.DYNAMODB_TABLE} 48 | AttributeDefinitions: 49 | - AttributeName: songName 50 | AttributeType: S 51 | KeySchema: 52 | - AttributeName: songName 53 | KeyType: HASH 54 | ProvisionedThroughput: 55 | ReadCapacityUnits: 1 56 | WriteCapacityUnits: 1 57 | 58 | plugins: 59 | - serverless-finch 60 | - serverless-python-requirements 61 | 62 | custom: 63 | client: 64 | bucketName: fernandotest-12trafsd213 65 | distributionFolder: frontend 66 | errorDocument: index.html 67 | pythonRequirements: 68 | dockerizePip: non-linux 69 | 70 | package: 71 | exclude: 72 | - node_modules/** 73 | - frontend/** 74 | - package-lock.json 75 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/frontend/js/auth0.js: -------------------------------------------------------------------------------- 1 | let auth0 = null; 2 | const fetchAuthConfig = () => fetch("/auth_config.json"); 3 | 4 | const configureClient = async () => { 5 | const response = await fetchAuthConfig(); 6 | const config = await response.json(); 7 | 8 | auth0 = await createAuth0Client({ 9 | domain: config.domain, 10 | audience: config.audience, 11 | client_id: config.clientId, 12 | }); 13 | }; 14 | 15 | const updateUI = async () => { 16 | const isAuthenticated = await auth0.isAuthenticated(); 17 | 18 | document.getElementById("btn-logout").disabled = !isAuthenticated; 19 | document.getElementById("btn-login").disabled = isAuthenticated; 20 | 21 | if (isAuthenticated) { 22 | document.getElementById("gated-content-1").classList.remove("hidden"); 23 | document.getElementById("gated-content-2").classList.remove("hidden"); 24 | 25 | const claims = await auth0.getIdTokenClaims() 26 | const pictureUrl = claims.picture 27 | 28 | document.getElementById("avatar-img").src = pictureUrl || 'https://icon-library.net/images/icon-of-music/icon-of-music-8.jpg'; 29 | document.getElementById("avatar-img-div").classList.remove("hidden") 30 | 31 | } else { 32 | document.getElementById("gated-content-1").classList.add("hidden"); 33 | document.getElementById("gated-content-2").classList.add("hidden"); 34 | } 35 | }; 36 | 37 | window.onload = async () => { 38 | await configureClient(); 39 | updateUI(); 40 | const query = window.location.search; 41 | if (query.includes("code=") && query.includes("state=")) { 42 | // Process the login state 43 | await auth0.handleRedirectCallback(); 44 | updateUI(); 45 | // Use replaceState to redirect the user away and remove the querystring parameters 46 | window.history.replaceState({}, document.title, "/"); 47 | } 48 | }; 49 | 50 | const login = async () => { 51 | await auth0.loginWithRedirect({ 52 | redirect_uri: window.location.origin 53 | }); 54 | }; 55 | 56 | const logout = () => { 57 | auth0.logout({ 58 | returnTo: window.location.origin 59 | }); 60 | }; 61 | -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/frontend/js/auth0.js: -------------------------------------------------------------------------------- 1 | let auth0 = null; 2 | const fetchAuthConfig = () => fetch("/auth_config.json"); 3 | 4 | const configureClient = async () => { 5 | const response = await fetchAuthConfig(); 6 | const config = await response.json(); 7 | 8 | auth0 = await createAuth0Client({ 9 | domain: config.domain, 10 | audience: config.audience, 11 | client_id: config.clientId, 12 | }); 13 | }; 14 | 15 | const updateUI = async () => { 16 | const isAuthenticated = await auth0.isAuthenticated(); 17 | 18 | document.getElementById("btn-logout").disabled = !isAuthenticated; 19 | document.getElementById("btn-login").disabled = isAuthenticated; 20 | 21 | if (isAuthenticated) { 22 | document.getElementById("gated-content-1").classList.remove("hidden"); 23 | document.getElementById("gated-content-2").classList.remove("hidden"); 24 | 25 | const claims = await auth0.getIdTokenClaims() 26 | const pictureUrl = claims.picture 27 | 28 | document.getElementById("avatar-img").src = pictureUrl || 'https://icon-library.net/images/icon-of-music/icon-of-music-8.jpg'; 29 | document.getElementById("avatar-img-div").classList.remove("hidden") 30 | 31 | } else { 32 | document.getElementById("gated-content-1").classList.add("hidden"); 33 | document.getElementById("gated-content-2").classList.add("hidden"); 34 | } 35 | }; 36 | 37 | window.onload = async () => { 38 | await configureClient(); 39 | updateUI(); 40 | const query = window.location.search; 41 | if (query.includes("code=") && query.includes("state=")) { 42 | // Process the login state 43 | await auth0.handleRedirectCallback(); 44 | updateUI(); 45 | // Use replaceState to redirect the user away and remove the querystring parameters 46 | window.history.replaceState({}, document.title, "/"); 47 | } 48 | }; 49 | 50 | const login = async () => { 51 | await auth0.loginWithRedirect({ 52 | redirect_uri: window.location.origin 53 | }); 54 | }; 55 | 56 | const logout = () => { 57 | auth0.logout({ 58 | returnTo: window.location.origin 59 | }); 60 | }; 61 | -------------------------------------------------------------------------------- /5.4_checking_for_api_scopes/frontend/js/app.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $('.ui.dropdown').dropdown(); 3 | refreshVoteCounts() 4 | document.getElementsByClassName("ui red button")[0].addEventListener("click", recordVote); 5 | }); 6 | 7 | var vote_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/song/vote" 8 | var get_votes_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/votes" 9 | 10 | function setVotes(songName, voteCount) { 11 | // Get div containing vote count and set the new voteCount 12 | document.getElementsByName(songName)[0].innerHTML = voteCount; 13 | } 14 | 15 | async function refreshVoteCounts() { 16 | // Get the vote counts 17 | const response = await fetch(get_votes_endpoint); 18 | const songs = await response.json(); 19 | // Iterate over all three songs and update the divs 20 | var i; 21 | for (i = 0; i < songs.length; i++){ 22 | var featured_songs = ["coderitis", "stateless", "dynamo"]; 23 | var song = songs[i] 24 | if (featured_songs.includes(song["songName"])){ 25 | console.log(song) 26 | setVotes(song["songName"], song["votes"]) 27 | } 28 | } 29 | } 30 | 31 | async function voteForSong(songName) { 32 | const id_token = await auth0.getTokenSilently(); 33 | console.log(id_token) 34 | const response = await fetch(vote_endpoint, { 35 | method: "POST", 36 | mode: 'cors', 37 | headers: { 38 | 'Content-Type': 'application/json', 39 | 'Authorization': 'Bearer ' + id_token 40 | }, 41 | body: JSON.stringify({"songName": songName}) 42 | }) 43 | const result_json = await response.json() 44 | setVotes(songName, result_json["votes"]) 45 | } 46 | 47 | function recordVote() { 48 | if (document.getElementsByClassName("item active selected")[0]) { 49 | var selectedSong = document.getElementsByClassName("item active selected")[0].getAttribute('data-value') 50 | if (selectedSong) { 51 | voteForSong(selectedSong) 52 | console.log("voted for " + selectedSong) 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /5.3_adding_lambda_authorizers/frontend/js/app.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $('.ui.dropdown').dropdown(); 3 | refreshVoteCounts() 4 | document.getElementsByClassName("ui red button")[0].addEventListener("click", recordVote); 5 | }); 6 | 7 | var vote_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/song/vote" 8 | var get_votes_endpoint = "https://EXAMPLE_REPLACE_ME.execute-api.us-east-1.amazonaws.com/dev/votes" 9 | 10 | function setVotes(songName, voteCount) { 11 | // Get div containing vote count and set the new voteCount 12 | document.getElementsByName(songName)[0].innerHTML = voteCount; 13 | } 14 | 15 | async function refreshVoteCounts() { 16 | // Get the vote counts 17 | const response = await fetch(get_votes_endpoint); 18 | const songs = await response.json(); 19 | // Iterate over all three songs and update the divs 20 | var i; 21 | for (i = 0; i < songs.length; i++){ 22 | var featured_songs = ["coderitis", "stateless", "dynamo"]; 23 | var song = songs[i] 24 | if (featured_songs.includes(song["songName"])){ 25 | console.log(song) 26 | setVotes(song["songName"], song["votes"]) 27 | } 28 | } 29 | } 30 | 31 | async function voteForSong(songName) { 32 | const id_token = await auth0.getTokenSilently(); 33 | console.log(id_token) 34 | const response = await fetch(vote_endpoint, { 35 | method: "POST", 36 | mode: 'cors', 37 | headers: { 38 | 'Content-Type': 'application/json', 39 | 'Authorization': 'Bearer ' + id_token 40 | }, 41 | body: JSON.stringify({"songName": songName}) 42 | }) 43 | const result_json = await response.json() 44 | setVotes(songName, result_json["votes"]) 45 | } 46 | 47 | function recordVote() { 48 | if (document.getElementsByClassName("item active selected")[0]) { 49 | var selectedSong = document.getElementsByClassName("item active selected")[0].getAttribute('data-value') 50 | if (selectedSong) { 51 | voteForSong(selectedSong) 52 | console.log("voted for " + selectedSong) 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /4.5_integrating_api_frontend/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |