├── .DS_Store ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SupportCommands.md ├── cfn-templates └── devbox.yml ├── images ├── apigwcognito.png ├── apigwdeploy.png ├── apigwoauth.png ├── apigwstagev1.png ├── apigwxray.png ├── appclientsettings.png ├── aspneturlresult.png ├── cloudformationresources.png ├── cognitohostedui.png ├── customerlistpage.png ├── customerlistterminal.png ├── devbox.png ├── devboxoutputs.png ├── diagram.jpeg ├── dotnetlambda.png ├── iam-user-policies.png ├── indexjwt.png ├── newremotevs.png ├── newterminal.png ├── postmannoauth.png ├── postmanwithauth.png ├── putty_connect.png ├── putty_key.png ├── putty_tunnel.png ├── serverless.template.png ├── token.png ├── unicornrentals.png ├── vs1.png ├── vs2.png ├── vs3.png ├── vs4.png ├── vscodeaws1.png ├── vscodeextensions1.png ├── vscodeextensions2.png ├── vscodenewfile.png ├── vscodenuget.png ├── vscodeterminal.png ├── vscodewebapp.png ├── webappfolder.png └── xrayfilter.png ├── lab-0-tools-and-cred └── README.md ├── lab-1-aspnetcore └── README.md ├── lab-2-openid ├── README.md └── Startup.cs ├── lab-3-backend ├── Function.cs ├── README.md └── cfn-sam-deployment.yaml ├── lab-4-alltogether ├── Customers.cshtml ├── Customers.cshtml.cs ├── Index.cshtml.cs └── README.md ├── lab-5-xray └── README.md └── lab-99-cleanup └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/.DS_Store -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Build and monitor a secure Serverless app powered by AspNetCore WebApp with Amazon Cognito and AWS X-Ray 2 | 3 | In this workshop, you'll deploy a serverless web application based on AspNetCore that leverages the Amazon Cognito Hosted UI for sign-up and sign-in. During the sign-in process, the AspNetCore application receives an identity token from Amazon Cognito, which is processed by the standard DotNetCore OpenIdConnect library. The AspNetCore WebApp will interface with a serverless DotNet backend via a RESTful web service call. The erverless DotNet backend is exposed via Amazon API Gateway. Its authentication expects the same identity token of the signed-in user who logged into the AspNetCore WebApp, hence providing authentication enforcement and seamless integration with Amazon Cognito. 4 | 5 | See the diagram below for a depiction of the complete architecture. 6 | 7 | drawing 8 | 9 | ## Initial environment setup 10 | 11 | ### Prerequisites 12 | 13 | #### Laptop Machine 14 | 15 | A laptop with Wi-Fi running Microsoft Windows or Mac OS X with the following software installed: 16 | 17 | - Visual Studio Code (its installation is covered during Lab 0) 18 | - An Internet browser such as Chrome, Firefox, Safari, or Edge. 19 | - Ability to SSH with port forwarding (ssh or Putty) 20 | 21 | #### AWS Account 22 | 23 | - Ability to create AWS resources including EC2, Amazon Cognito, AWS Lambda, Amazon Api Gateway, Amazon S3 and IAM Roles. 24 | - Amazon EC2 key pair created in the AWS region you are working on. Learn how to [create a key pair](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html) 25 | 26 | :warning: **Important Note: This guide creates chargeable AWS resources.** You may wish to remove these resources upon completion of the guide to avoid incurring future charges to your account. To do this, be sure to follow the instructions in *Clean up* module. 27 | 28 | ## Labs 29 | 30 | Follow the modules to configure and deploy the AspNetCore application to AWS successfully. 31 | 32 | | Labs | 33 | | ------------- | 34 | | [0 - Setting up AWS Tools and credentials](lab-0-tools-and-cred/) | 35 | | [1 - Creating the AspNetCore](lab-1-aspnetcore/) | 36 | | [2 - Adding OpenId to AspNetCore WebApp](lab-2-openid/) | 37 | | [3 - Creating the backend serverless dotnet app](lab-3-backend/) | 38 | | [4 - Putting it all together](lab-4-alltogether/) | 39 | | [5 - (Optional) Improving monitoring with AWS X-Ray](lab-5-xray/) | 40 | | [99- Clean up](lab-99-clean-up/) | -------------------------------------------------------------------------------- /SupportCommands.md: -------------------------------------------------------------------------------- 1 | # Supporting commands 2 | This section provides a list of useful commands that help you identify the names of the resources created in the labs. Most of the commands executed in the labs require you to know the resources, like S3 bucket and Api Gateway Id. 3 | 4 | ## Amazon S3 bucket name 5 | To obtain the Amazon S3 bucket name required for deploying the packages, query the resources created for the *first initial* + *last initial* + *-dotnetcore-devbox*, by executing ```aws cloudformation describe-stack-resources --stack-name + + -dotnetcore-devbox --query 'StackResources[*].{Type:ResourceType,Id:PhysicalResourceId}' --output text```. Look for the *AWS::S3::Bucket resource*. 6 | 7 | ## Amazon Api Gateway 8 | You can find the **restApiId** for AspNetCoreWebApp and CustomersList by running ```aws apigateway get-rest-apis --query 'items[*].{name:name,restApiId:id}'``` and look at the name parameter. 9 | 10 | ## Amazon Cognito 11 | List the userPools Id. Your user should be name like *first initial* + *last initial* + *-CognitoUserPool* 12 | ``` 13 | aws cognito-idp list-user-pools --max-results 10 14 | ``` 15 | 16 | Once you have the Amazon Cognito user pool Id, you can list the user-pool-clients by executing: 17 | ``` 18 | aws cognito-idp list-user-pool-clients --user-pool-id 19 | ``` 20 | 21 | If you need to obtail the Amazon Cognito ClientId and Client Password, please execute the following command: 22 | ``` 23 | aws cognito-idp describe-user-pool-client --user-pool-id --client-id 24 | ``` 25 | 26 | ## Lambda Functions 27 | List all the lambda functions names that you have deploy by running: 28 | ``` 29 | aws lambda list-functions --query 'Functions[].FunctionName' 30 | ``` 31 | -------------------------------------------------------------------------------- /cfn-templates/devbox.yml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Parameters: 3 | KeyName: 4 | Description: Name of an existing EC2 KeyPair to enable SSH access to the instance 5 | Type: AWS::EC2::KeyPair::KeyName 6 | ConstraintDescription: must be the name of an existing EC2 KeyPair. 7 | InstanceType: 8 | Description: Developer EC2 instance type 9 | Type: String 10 | Default: t3.medium 11 | AllowedValues: [t2.medium, t2.large, t3.medium, t3.large] 12 | ConstraintDescription: must be a valid EC2 instance type. 13 | SSHLocation: 14 | Description: The IP address range that can be used to SSH to the EC2 instances 15 | Type: String 16 | MinLength: 9 17 | MaxLength: 18 18 | Default: 0.0.0.0/0 19 | AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) 20 | ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. 21 | LatestAmiId: 22 | Type: 'AWS::SSM::Parameter::Value' 23 | Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' 24 | VpcCIDR: 25 | Description: Please enter the IP range (CIDR notation) for this VPC 26 | Type: String 27 | Default: 172.17.0.0/16 28 | PublicSubnet1CIDR: 29 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone 30 | Type: String 31 | Default: 172.17.2.0/24 32 | EnvironmentName: 33 | Description: An environment name that will be prefixed to resource names 34 | Type: String 35 | Default: DotNet-Workshop 36 | Resources: 37 | S3Deploy: 38 | Type: AWS::S3::Bucket 39 | 40 | VPC: 41 | Type: AWS::EC2::VPC 42 | Properties: 43 | CidrBlock: !Ref VpcCIDR 44 | EnableDnsSupport: true 45 | EnableDnsHostnames: true 46 | Tags: 47 | - Key: Name 48 | Value: !Ref EnvironmentName 49 | InternetGateway: 50 | Type: AWS::EC2::InternetGateway 51 | Properties: 52 | Tags: 53 | - Key: Name 54 | Value: !Ref EnvironmentName 55 | InternetGatewayAttachment: 56 | Type: AWS::EC2::VPCGatewayAttachment 57 | Properties: 58 | InternetGatewayId: !Ref InternetGateway 59 | VpcId: !Ref VPC 60 | PublicSubnet1: 61 | Type: AWS::EC2::Subnet 62 | Properties: 63 | VpcId: !Ref VPC 64 | AvailabilityZone: !Select [ 0, !GetAZs '' ] 65 | CidrBlock: !Ref PublicSubnet1CIDR 66 | MapPublicIpOnLaunch: true 67 | Tags: 68 | - Key: Name 69 | Value: !Sub ${EnvironmentName} Public Subnet (AZ1) 70 | PublicRouteTable: 71 | Type: AWS::EC2::RouteTable 72 | Properties: 73 | VpcId: !Ref VPC 74 | Tags: 75 | - Key: Name 76 | Value: !Sub ${EnvironmentName} Public Routes 77 | DefaultPublicRoute: 78 | Type: AWS::EC2::Route 79 | DependsOn: InternetGatewayAttachment 80 | Properties: 81 | RouteTableId: !Ref PublicRouteTable 82 | DestinationCidrBlock: 0.0.0.0/0 83 | GatewayId: !Ref InternetGateway 84 | PublicSubnet1RouteTableAssociation: 85 | Type: AWS::EC2::SubnetRouteTableAssociation 86 | Properties: 87 | RouteTableId: !Ref PublicRouteTable 88 | SubnetId: !Ref PublicSubnet1 89 | 90 | InstanceRole: 91 | Type: AWS::IAM::Role 92 | Properties: 93 | AssumeRolePolicyDocument: 94 | Version: 2012-10-17 95 | Statement: 96 | - Action: sts:AssumeRole 97 | Effect: Allow 98 | Principal: 99 | Service: ec2.amazonaws.com 100 | Sid: '' 101 | Path: / 102 | Policies: 103 | - 104 | PolicyName: InstanceS3InstancePolicy 105 | PolicyDocument: 106 | Version: 2012-10-17 107 | Statement: 108 | - 109 | Effect: Allow 110 | Action: 's3:*' 111 | Resource: 112 | - !GetAtt S3Deploy.Arn 113 | - !Sub "arn:aws:s3:::${S3Deploy}/*" 114 | - 115 | PolicyName: IAMRole 116 | PolicyDocument: 117 | Version: 2012-10-17 118 | Statement: 119 | - 120 | Effect: Allow 121 | Action: 122 | - 'iam:CreateRole' 123 | - 'iam:AttachRolePolicy' 124 | - 'iam:PutRolePolicy' 125 | - 'iam:DetachRolePolicy' 126 | Resource: '*' 127 | ManagedPolicyArns: 128 | - arn:aws:iam::aws:policy/AWSCloudFormationFullAccess 129 | - arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator 130 | - arn:aws:iam::aws:policy/AWSLambdaFullAccess 131 | - arn:aws:iam::aws:policy/AmazonCognitoPowerUser 132 | - arn:aws:iam::aws:policy/AWSXrayFullAccess 133 | - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess 134 | 135 | InstanceProfile: 136 | Type: AWS::IAM::InstanceProfile 137 | DependsOn: [ InstanceRole ] 138 | Properties: 139 | Roles: [ !Ref InstanceRole ] 140 | 141 | EC2Instance: 142 | Type: AWS::EC2::Instance 143 | Properties: 144 | InstanceType: !Ref 'InstanceType' 145 | KeyName: !Ref 'KeyName' 146 | ImageId: !Ref 'LatestAmiId' 147 | IamInstanceProfile: !Ref InstanceProfile 148 | NetworkInterfaces: 149 | - AssociatePublicIpAddress: 'true' 150 | DeviceIndex: '0' 151 | GroupSet: 152 | - !Ref 'InstanceSecurityGroup' 153 | SubnetId: !Ref 'PublicSubnet1' 154 | BlockDeviceMappings: 155 | - DeviceName: '/dev/xvda' 156 | Ebs: 157 | VolumeType: 'gp2' 158 | VolumeSize: 20 159 | Tags: 160 | - Key: 'Name' 161 | Value: 'dotnet-devbox' 162 | UserData: 163 | Fn::Base64: !Sub | 164 | #!/bin/bash -xe 165 | yum update -y 166 | # START 167 | amazon-linux-extras install -y python3 168 | pip3 install --upgrade pip 169 | pip3 install aws-sam-cli 170 | cat > /tmp/subscript.sh << EOF 171 | wget -O /tmp/dotnet-sdk-3.1.301-linux-x64.tar.gz https://download.visualstudio.microsoft.com/download/pr/8db2b522-7fa2-4903-97ec-d6d04d297a01/f467006b9098c2de256e40d2e2f36fea/dotnet-sdk-3.1.301-linux-x64.tar.gz 172 | mkdir -p /home/ec2-user/.dotnet && tar zxf /tmp/dotnet-sdk-3.1.301-linux-x64.tar.gz -C /home/ec2-user/.dotnet 173 | export DOTNET_ROOT=/home/ec2-user/.dotnet 174 | export PATH=$PATH:/home/ec2-user/.dotnet:/home/ec2-user/.dotnet/tools 175 | echo 'export PATH=$PATH:/home/ec2-user/.dotnet:/home/ec2-user/.dotnet/tools' >> /home/ec2-user/.bash_profile 176 | echo 'export DOTNET_ROOT=/home/ec2-user/.dotnet' >> /home/ec2-user/.bash_profile 177 | 178 | EOF 179 | 180 | chown ec2-user:ec2-user /tmp/subscript.sh && chmod a+x /tmp/subscript.sh 181 | sleep 1; su - ec2-user -c "/tmp/subscript.sh" 182 | 183 | /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} \ 184 | --resource EC2Instance --region ${AWS::Region} 185 | 186 | CreationPolicy: 187 | ResourceSignal: 188 | Count: 1 189 | Timeout: "PT15M" 190 | InstanceSecurityGroup: 191 | Type: AWS::EC2::SecurityGroup 192 | Properties: 193 | GroupDescription: Enable SSH access via port 22 194 | VpcId: !Ref 'VPC' 195 | SecurityGroupIngress: 196 | - IpProtocol: tcp 197 | FromPort: 22 198 | ToPort: 22 199 | CidrIp: !Ref 'SSHLocation' 200 | Outputs: 201 | InstanceId: 202 | Description: InstanceId of the newly created EC2 instance 203 | Value: !Ref 'EC2Instance' 204 | PublicIP: 205 | Description: Public IP address of the newly created EC2 instance 206 | Value: !GetAtt [EC2Instance, PublicIp] 207 | S3BucketName: 208 | Description: S3 Bucket used for deployment 209 | Value: !Ref S3Deploy -------------------------------------------------------------------------------- /images/apigwcognito.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/apigwcognito.png -------------------------------------------------------------------------------- /images/apigwdeploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/apigwdeploy.png -------------------------------------------------------------------------------- /images/apigwoauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/apigwoauth.png -------------------------------------------------------------------------------- /images/apigwstagev1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/apigwstagev1.png -------------------------------------------------------------------------------- /images/apigwxray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/apigwxray.png -------------------------------------------------------------------------------- /images/appclientsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/appclientsettings.png -------------------------------------------------------------------------------- /images/aspneturlresult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/aspneturlresult.png -------------------------------------------------------------------------------- /images/cloudformationresources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/cloudformationresources.png -------------------------------------------------------------------------------- /images/cognitohostedui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/cognitohostedui.png -------------------------------------------------------------------------------- /images/customerlistpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/customerlistpage.png -------------------------------------------------------------------------------- /images/customerlistterminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/customerlistterminal.png -------------------------------------------------------------------------------- /images/devbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/devbox.png -------------------------------------------------------------------------------- /images/devboxoutputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/devboxoutputs.png -------------------------------------------------------------------------------- /images/diagram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/diagram.jpeg -------------------------------------------------------------------------------- /images/dotnetlambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/dotnetlambda.png -------------------------------------------------------------------------------- /images/iam-user-policies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/iam-user-policies.png -------------------------------------------------------------------------------- /images/indexjwt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/indexjwt.png -------------------------------------------------------------------------------- /images/newremotevs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/newremotevs.png -------------------------------------------------------------------------------- /images/newterminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/newterminal.png -------------------------------------------------------------------------------- /images/postmannoauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/postmannoauth.png -------------------------------------------------------------------------------- /images/postmanwithauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/postmanwithauth.png -------------------------------------------------------------------------------- /images/putty_connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/putty_connect.png -------------------------------------------------------------------------------- /images/putty_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/putty_key.png -------------------------------------------------------------------------------- /images/putty_tunnel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/putty_tunnel.png -------------------------------------------------------------------------------- /images/serverless.template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/serverless.template.png -------------------------------------------------------------------------------- /images/token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/token.png -------------------------------------------------------------------------------- /images/unicornrentals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/unicornrentals.png -------------------------------------------------------------------------------- /images/vs1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vs1.png -------------------------------------------------------------------------------- /images/vs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vs2.png -------------------------------------------------------------------------------- /images/vs3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vs3.png -------------------------------------------------------------------------------- /images/vs4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vs4.png -------------------------------------------------------------------------------- /images/vscodeaws1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vscodeaws1.png -------------------------------------------------------------------------------- /images/vscodeextensions1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vscodeextensions1.png -------------------------------------------------------------------------------- /images/vscodeextensions2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vscodeextensions2.png -------------------------------------------------------------------------------- /images/vscodenewfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vscodenewfile.png -------------------------------------------------------------------------------- /images/vscodenuget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vscodenuget.png -------------------------------------------------------------------------------- /images/vscodeterminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vscodeterminal.png -------------------------------------------------------------------------------- /images/vscodewebapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/vscodewebapp.png -------------------------------------------------------------------------------- /images/webappfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/webappfolder.png -------------------------------------------------------------------------------- /images/xrayfilter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-aspnetcore-webapp-for-serverless/fa69ed31d5245c93fdb3610a6a28845e6f10b920/images/xrayfilter.png -------------------------------------------------------------------------------- /lab-0-tools-and-cred/README.md: -------------------------------------------------------------------------------- 1 | # Lab 0: Setting up this workshop tools and credentials 2 | 3 | The steps in this module will walk you through setting up the [Visual Studio Code](https://code.visualstudio.com/) that will remote connect to an Amazon EC2 Linux machine to perform all the labs. 4 | 5 | ## Step 1: Deploy the Dev Environment 6 | In this step, we will deploy an Amazon EC2 Linux Machine that has installed: 7 | - The AWSCLI 8 | - The AWS [SAM CLI](https://github.com/awslabs/aws-sam-cli) 9 | - The Long Term Support (LTS) [.NET Core](https://dotnet.microsoft.com/platform/support/policy/dotnet-core) v3.1. This version is the currently supported LTS version by [AWS Lambda for .NET Core](https://github.com/aws/aws-lambda-dotnet). 10 | :notebook: **Note:** It is possible to execute non-LTS .NET Core on AWS, as per this [blog](https://aws.amazon.com/blogs/developer/announcing-amazon-lambda-runtimesupport/). For simplicity, this workshop is based on the LTS .NET Core version. 11 | 12 | --- 13 | 14 | 1. Download the [devbox.yml](../cfn-templates/devbox.yml) located at *serverless-aspnet-donet-cognito/cfn-templates* 15 | 2. Log into the [CloudFormation Management Console](https://console.aws.amazon.com/cloudformation/home). 16 | 3. Select **Create stack** with the *With new resources* option. 17 | 4. Click *Upload a template file*, and then **Choose file** and select the **devbox.yml** 18 | 5. Click *Next*. 19 | 20 | 6. Give the **Stack name** a name (e.g. **DotNetLab**). Select a key-pair and leave all the other fields with the default values. If you don't have any Amazon EC2 key-pair available [Create-your-key-pair](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair), and repeat this step. 21 | 7. Click Next twice. Don't forget to check the checkbox for **I acknowledge that AWS CloudFormation might create IAM resources.** as the cloudformation creates a role for the EC2 instance that grants you access to all resources/services required during the workshop. 22 | 23 | :notebook: **Note**: While the stack is deploying, which takes approximately ~5 minutes to finish, we carry on to the next step. 24 | 25 | ## Step 2: Installing Visual Studio Code and configuring its extensions 26 | 27 | In this step, you will install the Visual Studio Code, along with useful extensions. Visual Studio Code is compatible with Windows, Mac, and Linux. 28 | 29 | 1. Install Visual Studio Code (VSCode) - https://code.visualstudio.com/ 30 | 2. Start Visual Studio Code. At the lower-left corner, click on the *Manage* icon, then *Extensions*. 31 | 32 | 33 | 34 | 3. In the Search Extensions field type the packages names below and Click on *install*. For more information about how to manage Visual Studio Code Extensions, click [here](https://code.visualstudio.com/docs/editor/extension-gallery) 35 | ``` 36 | ms-vscode-remote.remote-ssh 37 | ms-vscode.csharp 38 | ``` 39 | 40 | 4. Click on the *Manage* icon again, and check for Updates or install a pending Update. Restart VSCode if needed. 41 | 42 | ### Setting the Remove Developer Extension 43 | 44 | 1. Log into the [CloudFormation Management Console](https://console.aws.amazon.com/cloudformation/home). 45 | 2. Check the status of the CloudFormation stack you have created at step1. Wait until the Status is complete. 46 | 3. Go to the Outputs tab, and make a note of the **S3BucketName** and **PublicIP**, as you will need this information for the next steps. 47 | 48 | 49 | 50 | 4. Login to the development box. Specify the appropriate private key and ``PublicIP`` from the EC2 instance. See below ways to connect. 51 | 52 | #### Visual Studio Code (Mac or Windows) - The recommended way for this workshop 53 | 54 | 1. Open Visual Studio Code. If the **Remote Developer Extension** is not installed, go back to **Step 1** to perform its installation. 55 | 56 | 2. Navigate to the Remote Explorer extension. 57 | 58 | 59 | 60 | 3. Click on the **gear icon** next to the SSH Targets to create a configuration file. It will generate a new file with default values. 61 | 62 | 63 | 64 | 4. Provide the host entry with values from the template below. Replace the **Host**, **HostName**, and **IdentityFile** as shown below. After saving the config, an SSH Target will appear. If not, close and reopen Visual Studio Code. 65 | 66 | #### Mac 67 | ``` 68 | Host devbox-dotnet 69 | HostName 70 | IdentityFile "~/path/to/EC2PrivateKey.pem" 71 | User ec2-user 72 | LocalForward localhost:3000 localhost:3000 73 | ``` 74 | #### Windows 75 | ``` 76 | Host devbox-dotnet 77 | HostName 78 | IdentityFile "C:\Users\Administrator\path\to\EC2PrivateKey.pem" 79 | User ec2-user 80 | LocalForward localhost:3000 localhost:3000 81 | ``` 82 | 83 | 84 | 85 | 1. Click the **Connect to Host in New Window** icon next to the listed SSH Target. After connecting, select **New Terminal** from the **Terminal** menu in the Visual Studio Code. This action will open a terminal session with the devbox EC2 Linux instance created by the cloudFormation script. 86 | 87 | 90 | 91 | #### Other options to connect to the dotnetcore-devbox 92 | These options are available for you to connect directly to the Amazon EC2 instance to execute the commands required from this workshop. Hence, the files need to be directly edited on the Amazon EC2 Linux instance using a text editor like **vi**, **vim** or **nano**, which is not in the scope of this workshop. 93 | 94 |
95 | Mac (SSH)(expand for details)

96 | ``` 97 | ssh -i path/to/EC2PrivateKey ec2-user@PublicIp -L localhost:3000:localhost:3000 98 | ``` 99 | 100 |

101 | 102 |
103 | Windows (Putty)(expand for details)

104 | 105 | 1. Convert your EC2 Private Key. Follow the steps [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/putty.html) if necessary. 106 | 107 | 2. Specify the ``ec2-user@PublicIP`` in the Host Name field. 108 | 109 | 110 | 111 | 3. Specify the converted .ppk file in the SSH/Auth ``Private key file for authentication`` field. 112 | 113 | 114 | 115 | 4. Configure tunneling for localhost:3000 in the SSH/Tunnels. Specify the ``Source port`` and ``Destination`` and click **Add**. 116 | 117 | 118 | 119 |

120 | 121 | ***You have now completed this lab and can move onto [Lab 1](../lab-1-aspnetcore/).*** 122 | -------------------------------------------------------------------------------- /lab-1-aspnetcore/README.md: -------------------------------------------------------------------------------- 1 | # Lab 1: Creating the AspNetCore application 2 | 3 | The steps in this Lab will create a new AWS lambda AspNetCore to act as your main website. We will also install all the required nuget packages and add/change the code for the workshop. 4 | 5 | ## Step 1: using dotnet cli to create a new project 6 | 7 | 1. Go to **Terminal** menu and select **New Terminal** 8 | 9 | 10 | 11 | 1. Configure the AWS region in your terminal by executing ```aws configure```. Provide information solely to the **Default region name**. In the example below, the *Default region name* was set to Oregon (us-west-2). 12 | ``` 13 | [ec2-user@ip-172-17-2-121 WebApp]$ aws configure 14 | AWS Access Key ID [None]: 15 | AWS Secret Access Key [None]: 16 | Default region name [None]: us-west-2 17 | Default output format [None]: 18 | ``` 19 | 2. Install the AWS extensions for the dotnet CLI. 20 | ```bash 21 | dotnet tool install -g Amazon.Lambda.Tools 22 | ``` 23 | 3. Install the Amazon.Lambda.Templates blueprints by executing the following command : 24 | ```bash 25 | dotnet new -i "Amazon.Lambda.Templates::*" 26 | ``` 27 | 4. To see a list of the Lambda templates execute ```dotnet new lambda --list``` 28 | 5. Create the *WebApp* project running the following command. 29 | 30 | :warning: **The project name has to be named WebApp (capital W and capital A) as all the workshop modules are based on this name**. 31 | 32 | ```bash 33 | dotnet new serverless.AspNetCoreWebApp --name WebApp 34 | ``` 35 | 36 | 6. You now have a directory called WebApp with a C# project template for AspNetCore application. 37 | 38 | 7. Go to File -> *Open* and select **/home/ec2-user/WebApp/src/WebApp/**. It will load all the sub-directories and files for you. 39 | 40 | 41 | 42 | ### Step 2: Publishing the serverless.AspNetCoreWebApp to AWS 43 | Using the dotnet cli we will deploy our AspNet Core WebApp to AWS. 44 | 45 | 1. Edit the **serverless.template** file. We are going to add Add the lines ```"Tracing": "Active"``` and the Policies: ```"Policies": [ "AWSLambdaFullAccess", "AmazonSSMFullAccess" ],```. Replace the **serverless.template** content with the content below. Don't forget to save the file before moving to the next step. 46 | 47 | ```json 48 | { 49 | "AWSTemplateFormatVersion": "2010-09-09", 50 | "Transform": "AWS::Serverless-2016-10-31", 51 | "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.", 52 | "Parameters": {}, 53 | "Conditions": {}, 54 | "Resources": { 55 | "AspNetCoreFunction": { 56 | "Type": "AWS::Serverless::Function", 57 | "Properties": { 58 | "Handler": "WebApp::WebApp.LambdaEntryPoint::FunctionHandlerAsync", 59 | "Runtime": "dotnetcore3.1", 60 | "CodeUri": "", 61 | "MemorySize": 512, 62 | "Timeout": 30, 63 | "Role": null, 64 | "Policies": [ "AWSLambdaFullAccess", "AmazonSSMFullAccess" ], 65 | "Tracing": "Active", 66 | "Environment": { 67 | "Variables": {} 68 | }, 69 | "Events": { 70 | "ProxyResource": { 71 | "Type": "Api", 72 | "Properties": { 73 | "Path": "/{proxy+}", 74 | "Method": "ANY" 75 | } 76 | }, 77 | "RootResource": { 78 | "Type": "Api", 79 | "Properties": { 80 | "Path": "/", 81 | "Method": "ANY" 82 | } 83 | } 84 | } 85 | } 86 | } 87 | }, 88 | "Outputs": { 89 | "ApiURL": { 90 | "Description": "API endpoint URL for Prod environment", 91 | "Value": { 92 | "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" 93 | } 94 | } 95 | } 96 | } 97 | ``` 98 | 99 | 2. Execute the following command: 100 | 101 | :notebook: **Reminder**: To see all the resources created : ```aws cloudformation describe-stack-resources --stack-name DotNetLab --query 'StackResources[*].{Type:ResourceType,Id:PhysicalResourceId}' --output text``` to see all the resources created. The [Support Commands Page](/SupportCommands.md) provides a list of useful commands that help you identify the names of the resources created in the labs. 102 | 103 | ```bash 104 | dotnet lambda deploy-serverless --template serverless.template --s3-bucket --s3-prefix "aspnetcorewebapp/" --stack-name AspNetCoreWebApp 105 | ``` 106 | 107 | 1. When the deployment finishes, you will be able to see all the resources that were created and the URL of your AspNetCore webapplication. Something like: 108 | 109 | ``` 110 | Stack finished updating with status: CREATE_COMPLETE 111 | 112 | Output Name Value 113 | ------------------------------ -------------------------------------------------- 114 | ApiURL https://.execute-api..amazonaws.com/Prod/ 115 | ``` 116 | 4. Copy and past the URL into a browser to make sure that your AspNet Core is working. 117 | 118 | 119 | 5. Let's check the cloudformation stack resources that were created for the AspNetCoreWebApp. The dotnet lambda blueprint took care of creating an Amazon API Gateway, IAM Roles and its policies and the Lambda itself. The following command shows the resource types and their Ids. 120 | 121 | ```bash 122 | aws cloudformation describe-stack-resources --stack-name AspNetCoreWebApp --query 'StackResources[*].{Type:ResourceType,Id:PhysicalResourceId}' --output text 123 | ``` 124 | 125 | ### Step 3: Installing the Nuget packages 126 | Now it is time to prepare our application to integrate with the AWS resources we want by adding the AWS libraries to our project. 127 | 128 | 1. From the Visual Code terminal you will install the nuget packages, like at the screenshot below: 129 | 130 | 131 | 132 | :notebook: **Note:** *You can copy and paste all the lines below into the terminal prompt. The last line pasted still need to you press ENTER to install it*. 133 | 134 | ```bash 135 | dotnet add package Newtonsoft.Json --version 12.0.3 136 | dotnet add package AWSSDK.Core --version 3.3.107.9 137 | dotnet add package AWSSDK.Lambda --version 3.3.109.39 138 | dotnet add package Amazon.Lambda.Core --version 1.1.0 139 | dotnet add package AWSSDK.Extensions.NETCore.Setup --version 3.3.101 140 | dotnet add package AWS.Logger.Core --version 1.6.0 141 | dotnet add package AWSSDK.S3 --version 3.3.111.10 142 | dotnet add package AWSSDK.SimpleSystemsManagement --version 3.3.125.2 143 | dotnet add package Amazon.Lambda.APIGatewayEvents --version 2.1.0 144 | dotnet add package AWSSDK.CognitoIdentityProvider --version 3.3.109.49 145 | dotnet add package Amazon.AspNetCore.DataProtection.SSM --version 1.1.0 146 | dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect --version 3.1.5 147 | dotnet add package Microsoft.AspNetCore.Authentication.Cookies --version 2.2.0 148 | ``` 149 | 150 | :warning: if you copy and past the code above into the terminal you may have to hit **ENTER** one more time to install the last line. 151 | 152 | 153 | 1. You can verify if all the packages were installed by looking at the WebApp.csproj 154 | ```bash 155 | cat WebApp.csproj 156 | ``` 157 | 3. At the terminal windows, let's confirm that our code compiles with no errors 158 | ```bash 159 | dotnet publish -c Release 160 | ``` 161 | 162 | ***You have now completed this lab and can move onto [Lab 2](../lab-2-openid/).*** 163 | -------------------------------------------------------------------------------- /lab-2-openid/README.md: -------------------------------------------------------------------------------- 1 | # Lab 2: Adding OpenId to AspNetCore 2 | In this Lab, the AWS Lambda AspNetCoreWebApp is going to be integrated with Amazon Cognito by using OpenID. 3 | 4 | ### Step 1: Creating a User Pool 5 | This step will create an Amazon Cognito user pool. A user pool is a user directory in Amazon Cognito. With a user pool, your users can sign in to your web or mobile app through Amazon Cognito. 6 | 7 | 1. Using the Visual Code terminal, executing the following cli command to create an Amazon Cognito user pool. 8 | ```bash 9 | aws cognito-idp create-user-pool --pool-name DotNetCognitoUserPool --auto-verified-attributes email --username-attributes email --mfa-configuration OFF --schema '[{"Name":"given_name","Required":true},{"Name":"family_name","Required":true},{"Name":"email","Required":true}]' 10 | ``` 11 | :exclamation: Don't forget to take a note of the **PoolId** and **Pool ARN**. You will need these information to set up the OpenId library. The PoolID and Pool Arn can be copied from the command output. They appear at the **Id** and **Arn** fields, as the example below: 12 | 13 | ```json 14 | "CreationDate": 1583628374.102, 15 | "EstimatedNumberOfUsers": 0, 16 | "Id": "us-west-2_abcdef123", 17 | "Arn": "arn:aws:cognito-idp:us-west-2:000000000000:userpool/us-west-2_abcdef123", 18 | "LambdaConfig": {} 19 | ``` 20 | 21 | :notebook: **Note** In this workshop, we won't create an Identity Pool. With an identity pool, your users from the user pool can obtain temporary AWS credentials to access AWS services, such as Amazon S3 and DynamoDB. 22 | 23 | 2. An app is an entity within a user pool that has permission to call unauthenticated APIs (APIs that do not have an authenticated user), such as APIs to register, sign in, and handle forgotten passwords. To call these APIs, you need an app client ID and an optional client secret. The aws cli command below creates an user pool client. Make sure to replace the **PoolId**, the URL prefix created for your AspNetWebApp lambda on the previous lab, and the **AWS Region** in the command. 24 | 25 | :notebook: **Note** You can find **restApiId** by running ```aws apigateway get-rest-apis --query 'items[*].{name:name,restApiId:id}'```. The output will shows all the restApiId. Copy the one that contains **AspNetCoreWebApp** 26 | 27 | ```json 28 | [ 29 | { 30 | "restApiId": "aws43ro5672", 31 | "name": "ApiTest" 32 | }, 33 | { 34 | "restApiId": "aws57ro2934", 35 | "name": "AspNetCoreWebApp" 36 | } 37 | ] 38 | ``` 39 | 40 | :warning: **Attention** The command below requires you to replace **poolId**, **awsRegion** and **restApiId** 41 | 42 | ```bash 43 | aws cognito-idp create-user-pool-client --user-pool-id --client-name WebApp --generate-secret --refresh-token-validity 30 --allowed-o-auth-scopes openid email profile --supported-identity-providers COGNITO --allowed-o-auth-flows-user-pool-client --allowed-o-auth-flows code --callback-urls '["https://.execute-api..amazonaws.com/Prod/signin-oidc"]' --logout-urls '["https://.execute-api..amazonaws.com/Prod/"]' 44 | ``` 45 | 46 | 3. The command output from the command above provides the Cognitp **ClientId** and **ClientSecret**. These keys will be used shortly in this lab. Please, keep a note of them. The output looks like the json below: 47 | 48 | ```json 49 | { 50 | "UserPoolClient": { 51 | "ClientSecret": "1kd3eqiif72cjkjkse54554555raerefefd", 52 | "CallbackURLs": [ 53 | "https://.execute-api..amazonaws.com/Prod/signin-oidc" 54 | ], 55 | "AllowedOAuthScopes": [ 56 | "openid", 57 | "profile", 58 | "email" 59 | ], 60 | "UserPoolId": "us-west-2_abcdef123", 61 | "AllowedOAuthFlowsUserPoolClient": false, 62 | "LastModifiedDate": 1583685892.599, 63 | "ClientId": "5o442jj89nkauhjknu3bk", 64 | "AllowedOAuthFlows": [ 65 | "code" 66 | ], 67 | "LogoutURLs": [ 68 | "https://.execute-api..amazonaws.com/Prod/" 69 | ], 70 | "SupportedIdentityProviders": [ 71 | "COGNITO" 72 | ], 73 | "RefreshTokenValidity": 30, 74 | "CreationDate": 1583685892.599, 75 | "ClientName": "WebApp" 76 | } 77 | } 78 | ``` 79 | 80 | 4. After setting up an app client, you can configure the address of your sign-up and sign-in webpages. You can use an Amazon Cognito hosted domain and choose an available domain prefix. For this workshop, use *first initial* + *last initial* + *-dotnetcore-cognito*. For John Smith: js-dotnetcore-cognito. If you receive an error saying **Domain already exists** try a different domain name. 81 | 82 | ```bash 83 | aws cognito-idp create-user-pool-domain --user-pool-id --domain + + -dotnetcore 84 | ``` 85 | 86 | ### Step 2: Setting up Amazon Api Gateway Authorizer 87 | 88 | 1. Open the file **Startup.cs** on Visual Studio Code, by clicking on the filename at the left-panel. 89 | 2. Replace its entire content with [Startup.cs](Startup.cs). If you have cloned this repo, the file is located under lab-2-openid. 90 | 91 | :notebook: **Note** The Startup.cs script initializes the [OpenIdConnectOptions](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.openidconnectoptions) by using the Amazon Cognito information defined at **appsettings.json**. It also instructs the [DataProtection](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/introduction) to utilize AWS [SSM Provider](https://github.com/aws/aws-ssm-data-protection-provider-for-aspnet) for the encryptions keys management and rotation. The [AddRazorPagesOptions](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/razor-pages-authorization) defines what pages require Authentication. And, lastly, we configure the [LambdaLoggerOptions](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Logging.AspNetCore/LambdaLoggerOptions.cs) for better integration/experience with AWS CloudlogWatch. 92 | 93 | 3. Replace the **appsetting.json** with the following: 94 | 95 | :warning: Don't forget replace the **Client Id**, **Client Secret**, **domain**, **restApiId** and **awsRegion** with the proper values. 96 | 97 | ```json 98 | { 99 | "AWS": { 100 | "Cognito": { 101 | "PoolId": "", 102 | "SignedOutRedirectUri": "https://.auth..amazoncognito.com/logout?client_id=&response_type=code&logout_uri=https://.execute-api..amazonaws.com/Prod/" 103 | }, 104 | "OpenId": { 105 | "ClientId": "", 106 | "ClientSecret": "", 107 | "IncludeErrorDetails": true, 108 | "MetadataAddress": "https://cognito-idp..amazonaws.com//.well-known/openid-configuration", 109 | "RequireHttpsMetadata": false, 110 | "ResponseType": "code", 111 | "SaveTokens": true, 112 | "TokenValidationParameters": { 113 | "ValidateIssuer": true 114 | } 115 | } 116 | }, 117 | "Logging": { 118 | "IncludeScopes": false, 119 | "LogLevel": { 120 | "Default": "Warning" 121 | } 122 | } 123 | } 124 | ``` 125 | 126 | 4. Make sure you project still compiles correctly. 127 | ```bash 128 | dotnet publish -c Release 129 | ``` 130 | 131 | 5. Re-publish the project by executing the following command: 132 | 133 | :notebook: **Note**: The [Support Commands Page](/SupportCommands.md) provides a list of useful commands that helps you identify the resources' names created in the labs; like the Amazon S3 bucked required for deployment. 134 | 135 | ```bash 136 | dotnet lambda deploy-serverless --template serverless.template --s3-bucket --s3-prefix "aspnetcorewebapp/" --stack-name AspNetCoreWebApp 137 | ``` 138 | 139 | 6. Wait until the result ```Stack finished updating with status: UPDATE_COMPLETE```. Copy the ApiURL to a browser. This time the application will ask for a Jwt Token; hence will redirect you to the Amazon Cognito hosted UI for authentication. 140 | 141 | 142 | 143 | 7. Click on **Sign up** at the login prompt. 144 | 8. Fill up the **Sign up with a new account** fields accordingly (using a valid email address) and create the user by clicking on **Sign up** button. 145 | 9. Check your email inbox for a message from *no-reply@verificationemail.com*, which contains your verification code. Type it in the verification code prompt and **Confirm Account**. 146 | 10. You should now be able to access the AspNetCore WebApp. 147 | 148 | :notebook: **Note:** for more information about ASP.NET Core web applications using ASP.NET Core Identity with Amazon Cognito access [here](https://github.com/aws/aws-aspnet-cognito-identity-provider). 149 | 150 | ***You have now completed this lab and can move onto [Lab 3](../lab-3-backend/).*** 151 | -------------------------------------------------------------------------------- /lab-2-openid/Startup.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Authentication.Cookies; 9 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 10 | using Microsoft.AspNetCore.Builder; 11 | using Microsoft.AspNetCore.Hosting; 12 | using Microsoft.AspNetCore.Http; 13 | using Microsoft.AspNetCore.Mvc; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Options; 17 | using Microsoft.IdentityModel.Tokens; 18 | using Amazon.CognitoIdentityProvider; 19 | using Amazon.Lambda.Core; 20 | using Microsoft.Extensions.Logging; 21 | using Amazon.S3; 22 | using Amazon.Lambda; 23 | using System.IO; 24 | 25 | namespace WebApp 26 | { 27 | public class Startup 28 | { 29 | public Startup(IConfiguration configuration) 30 | { 31 | Configuration = configuration; 32 | } 33 | 34 | public IConfiguration Configuration { get; } 35 | 36 | public void ConfigureServices(IServiceCollection services) 37 | { 38 | 39 | services.Configure(options => 40 | { 41 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 42 | options.CheckConsentNeeded = context => true; 43 | options.MinimumSameSitePolicy = SameSiteMode.None; 44 | }); 45 | 46 | services.AddDataProtection() 47 | .PersistKeysToAWSSystemsManager("/AspNetCoreWebApp/DataProtection"); 48 | 49 | services.AddRazorPages() 50 | .AddRazorPagesOptions(options => 51 | { 52 | options.Conventions.AuthorizePage("/Index"); 53 | options.Conventions.AuthorizePage("/Home"); 54 | }); 55 | 56 | services.AddAuthentication(options => 57 | { 58 | options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; 59 | options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; 60 | options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; 61 | }) 62 | .AddCookie() 63 | .AddOpenIdConnect(options => 64 | { 65 | options.ResponseType = Configuration["AWS:OpenId:ResponseType"]; 66 | options.MetadataAddress = Configuration["AWS:OpenId:MetadataAddress"]; 67 | options.ClientId = Configuration["AWS:OpenId:ClientId"]; 68 | options.ClientSecret = Configuration["AWS:OpenId:ClientSecret"]; 69 | options.SaveTokens = Convert.ToBoolean(Configuration["AWS:OpenId:SaveTokens"]); 70 | options.TokenValidationParameters = new TokenValidationParameters 71 | { 72 | ValidateIssuer = Convert.ToBoolean(Configuration["AWS:OpenId:TokenValidationParameters:ValidateIssuer"]) 73 | }; 74 | options.Events = new OpenIdConnectEvents() 75 | { 76 | OnRedirectToIdentityProvider = context => 77 | { 78 | context.ProtocolMessage.SetParameter("pfidpadapterid", Configuration["oidc:PingProtocolMessage"]); 79 | return Task.FromResult(0); 80 | }, 81 | // handle the logout redirection 82 | OnRedirectToIdentityProviderForSignOut = context => 83 | { 84 | var logoutUri = Configuration["AWS:Cognito:SignedOutRedirectUri"]; 85 | context.Response.Redirect(logoutUri); 86 | context.HandleResponse(); 87 | 88 | return Task.CompletedTask; 89 | } 90 | }; 91 | }); 92 | 93 | services.AddDefaultAWSOptions(Configuration.GetAWSOptions()); 94 | services.AddAWSService(); 95 | services.AddAWSService(); 96 | services.AddAWSService(); 97 | } 98 | 99 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 100 | { 101 | var loggerOptions = new LambdaLoggerOptions 102 | { 103 | IncludeCategory = false, 104 | IncludeLogLevel = false, 105 | IncludeNewline = true, 106 | 107 | // Configure Filter to only log some 108 | Filter = (category, logLevel) => 109 | { 110 | // For some categories, only log events with minimum LogLevel 111 | if (string.Equals(category, "Default", StringComparison.Ordinal)) 112 | { 113 | return (logLevel >= LogLevel.Debug); 114 | } 115 | if (string.Equals(category, "Microsoft", StringComparison.Ordinal)) 116 | { 117 | return (logLevel >= LogLevel.Information); 118 | } 119 | return true; 120 | } 121 | }; 122 | 123 | // Configure Lambda logging 124 | loggerFactory 125 | .AddLambdaLogger(loggerOptions); 126 | 127 | app.UseStaticFiles(); 128 | 129 | app.UseRouting(); 130 | 131 | app.UseAuthentication(); 132 | app.UseAuthorization(); 133 | 134 | app.UseEndpoints(endpoints => 135 | { 136 | endpoints.MapRazorPages(); 137 | }); 138 | } 139 | 140 | } 141 | } -------------------------------------------------------------------------------- /lab-3-backend/Function.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | using System; 5 | using System.Text; 6 | using System.Linq; 7 | using System.Collections; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | using Amazon.Lambda.APIGatewayEvents; 11 | using Amazon.Lambda.Core; 12 | using Newtonsoft.Json; 13 | using System.Net; 14 | 15 | // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. 16 | [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] 17 | 18 | namespace CustomerList 19 | { 20 | public class User 21 | { 22 | public string name { get; set; } 23 | public string surname { get; set; } 24 | public int age { get; set; } 25 | public string gender { get; set; } 26 | public string phone { get; set; } 27 | public string timestamp { get; set; } 28 | } 29 | 30 | public class Functions 31 | { 32 | static Random rnd = new Random(); 33 | static string[] male = { "Michael","Patrick","Stefan","Daniel","Thomas","Christoph","Dominik","Lukas","Philip","Florian","Manuel","Andreas","Alexander","Markus","Martin","Matthias","Christian","Mario","Bernhard","Johannes","Maximilian","Benjamin","Raphael","Peter","Christopher","René","Simon","Marco","Fabian","Julian","Marcel","Georg","Jakob","Tobias","Clemens","Robert","Oliver","Paul","Jürgen","Wolfgang","Felix","Josef","Hannes","Roman","Gerald","Sascha","Franz","Klaus","Pascal","Roland","Richard","Gregor","Harald","Gerhard","Armin","Gabriel","Marc","Alex","Alexis","Antonio","Austin","Beau","Beckett","Bentley","Brayden","Bryce","Caden","Caleb","Camden","Cameron","Carter","Casey","Cash","Charles","Charlie","Chase","Clark","Cohen","Connor","Cooper","David","Dawson","Declan","Dominic","Drake","Drew","Dylan","Edward","Eli","Elijah","Elliot","Emerson","Emmett","Ethan","Evan","Ezra","Felix","Gage","Gavin","Gus","Harrison","Hayden","Henry","Hudson","Hunter","Isaac","Jace","Jack","Jackson","Jacob","James","Jase","Jayden","John","Jonah","Joseph","Kai","Kaiden","Kingston","Levi","Liam","Logan","Lucas","Luke","Marcus","Mason","Matthew","Morgan","Nate","Nathan","Noah","Nolan","Oliver","Owen","Parker","Raphaël","Riley","Ryan","Samuel","Sebastian","Seth","Simon","Tanner","Taylor","Theo","Tristan","Turner","Ty","William","Wyatt" }; 34 | static string[] female = { "Julia","Lisa","Stefanie","Katharina","Melanie","Christina","Sabrina","Sarah","Anna","Sandra","Katrin","Carina","Bianca","Nicole","Jasmin","Kerstin","Tanja","Jennifer","Verena","Daniela","Theresa","Viktoria","Elisabeth","Nadine","Nina","Tamara","Madalena","Claudia","Jacquelina","Machaela","Martina","Denise","Barbara","Bettina","Alexandra","Cornelia","Maria","Vanessa","Andrea","Johanna","Eva","Natalie","Sabine","Isabella","Anja","Simone","Janine","Marlene","Patricia","Petra","Laura","Yvonne","Manuela","Karin","Birgit","Caroline","Tine","Carmen","Abigail","Adalyn","Aleah","Alexa","Alexis","Alice","Alyson","Amelia","Amy","Anabelle","Anna","Annie","Aria","Aubree","Ava","Ayla","Brielle","Brooke","Brooklyn","Callie","Camille","Casey","Charlie","Charlotte","Chloe","Claire","Danica","Elizabeth","Ella","Ellie","Elly","Emersyn","Emily","Emma","Evelyn","Felicity","Fiona","Florence","Georgia","Hailey","Haley","Isla","Jessica","Jordyn","Juliette","Kate","Katherine","Kayla","Keira","Kinsley","Kyleigh","Lauren","Layla","Lea","Leah","Lexi","Lily","Lydia","Lylah","Léa","Macie","Mackenzie","Madelyn","Madison","Maggie","Marley","Mary","Maya","Meredith","Mila","Molly","Mya","Olivia","Paige","Paisley","Peyton","Piper","Quinn","Rebekah","Rosalie","Ruby","Sadie","Samantha","Savannah","Scarlett","Selena","Serena","Sofia","Sophia","Sophie","Stella","Summer","Taylor","Tessa","Victoria","Violet","Zoey","Zoé" }; 35 | static string[] surname = { "Gruber","Huber","Bauer","Wagner","Müller","Pichler","Steiner","Moser","Mayer","Hofer","Leitner","Berger","Fuchs","Eder","Fischer","Schmid","Winkler","Weber","Schwarz","Maier","Schneider","Reiter","Mayr","Schmidt","Wimmer","Egger","Brunner","Lang","Baumgartner","Auer","Binder","Lechner","Wolf","Wallner","Aigner","Ebner","Koller","Lehner","Haas","Schuster","Anderson","Bergeron","Bouchard","Boucher ","Brown","Bélanger","Campbell","Chan","Clark","Cote","Fortin","Gagnon","Gagné","Gauthier","Girard","Johnson","Jones","Lam","Lavoie","Lavoie","Leblanc","Lee","Li","Lévesque","Martin","Morin","Ouellet","Paquette","Patel","Pelletier","Roy","Simard","Smith","Taylor","Thompson","Tremblay","White","Williams","Wilson","Wong" }; 36 | 37 | public User GetUser() 38 | { 39 | DateTime currentDate = DateTime.Now; 40 | var user = new User(); 41 | 42 | // Generate random indexes for pet names. 43 | if (Convert.ToBoolean(rnd.Next(0, 2))) 44 | { 45 | user.name = male[rnd.Next(male.Length)]; 46 | user.gender = "male"; 47 | } 48 | else 49 | { 50 | user.name = female[rnd.Next(female.Length)]; 51 | user.gender = "female"; 52 | } 53 | 54 | user.surname = surname[rnd.Next(surname.Length)]; 55 | 56 | user.phone = GetRandomTelNo(); 57 | user.age = rnd.Next(21, 69); 58 | user.timestamp = currentDate.ToString("s"); 59 | 60 | return user; 61 | } 62 | 63 | static string GetRandomTelNo() 64 | { 65 | StringBuilder telNo = new StringBuilder(12); 66 | int number; 67 | for (int i = 0; i < 3; i++) 68 | { 69 | number = rnd.Next(0, 8); // digit between 0 (incl) and 8 (excl) 70 | telNo = telNo.Append(number.ToString()); 71 | } 72 | telNo = telNo.Append("-"); 73 | number = rnd.Next(0, 743); // number between 0 (incl) and 743 (excl) 74 | telNo = telNo.Append(String.Format("{0:D3}", number)); 75 | telNo = telNo.Append("-"); 76 | number = rnd.Next(0, 10000); // number between 0 (incl) and 10000 (excl) 77 | telNo = telNo.Append(String.Format("{0:D4}", number)); 78 | return telNo.ToString(); 79 | } 80 | 81 | /// 82 | /// Default constructor that Lambda will invoke. 83 | /// 84 | public Functions() 85 | { 86 | } 87 | 88 | /// 89 | /// A Lambda function to respond to HTTP Get methods from API Gateway 90 | /// 91 | /// 92 | /// The list of blogs 93 | public APIGatewayProxyResponse Handler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext apigProxyContext) 94 | { 95 | try 96 | { 97 | var users = new List(); 98 | 99 | int counter = 0; 100 | while (counter < 25) 101 | { 102 | users.Add(GetUser()); 103 | counter++; 104 | } 105 | 106 | return JsonResponse.Send(true, "Success", users); 107 | 108 | } 109 | catch (Exception e) 110 | { 111 | LambdaLogger.Log("Handler Error - " + e.Message); 112 | return JsonResponse.Send(false, e.Message); 113 | } 114 | } 115 | 116 | public class BodyResponse 117 | { 118 | public string msg { get; set; } 119 | public IEnumerable data { get; set; } 120 | } 121 | 122 | public static class JsonResponse 123 | { 124 | public static APIGatewayProxyResponse Send(bool result, string msg, IEnumerable data = null) 125 | { 126 | BodyResponse body = new BodyResponse(); 127 | int statusCode = 0; 128 | 129 | body.msg = msg; 130 | if (data != null) 131 | body.data = data; 132 | else 133 | body.data = null; 134 | 135 | if (result == false) 136 | { 137 | statusCode = (int)HttpStatusCode.InternalServerError; 138 | } 139 | else 140 | { 141 | statusCode = (int)HttpStatusCode.OK; 142 | } 143 | 144 | return new APIGatewayProxyResponse 145 | { 146 | StatusCode = statusCode, 147 | Headers = new Dictionary { 148 | { "Content-Type", "application/json" }, 149 | { "Access-Control-Allow-Origin", "*" }, 150 | { "Access-Control-Allow-Credentials", "true" } 151 | }, 152 | Body = JsonConvert.SerializeObject(body) 153 | }; 154 | 155 | } 156 | } 157 | 158 | } 159 | } -------------------------------------------------------------------------------- /lab-3-backend/README.md: -------------------------------------------------------------------------------- 1 | # Lab 3: Adding a serverless dotnet backend 2 | 3 | The steps in this Lab will create a new AWS Lambda to act as one of many microservices that supports your web application. This microservices will be exposed via a new Amazon API Gateway that will be integrated with Amazon Cognito. During the REST API call, the web application will provide the JWT token to authenticate into the backend microservices. 4 | 5 | ## Step 1: using dotnet cli to create a new project 6 | 7 | 1. Open a new remote host Visual Studio Code by clicking on the (+) icon on the left side of the devbox-donet. 8 | 9 | 10 | 11 | 2. Go to **Terminal** menu and select **New Terminal**. In the terminal, execute the command below: 12 | 13 | :warning: **The project name has to be named CustomerList (capital C and capital L) as all the workshop modules are based on this name**. 14 | 15 | ``` 16 | dotnet new lambda.EmptyFunction --name CustomerList 17 | ``` 18 | 19 | 3. You now have a directory called **CustomerList** with a C# project template for a NetCore application. 20 | 21 | 4. Go to File -> *Open* and select the **/home/ec2-user/CustomerList/src/CustomerList** folder. It will load all the sub-directories and files for you. 22 | 23 | 24 | 25 | :exclamation: We will run all the commands using this terminal window. Keep it opened. 26 | 27 | ### Step 2: Publishing the serverless.AspNetCoreWebApp to AWS 28 | We will create an AWS Serverless Application Model [SAM](https://github.com/awslabs/serverless-application-model) template to instruct the dotnet cli to provision our microservices. 29 | 30 | 1. From the Visual Code terminal windows execute the following commands: 31 | 32 | :notebook: **Note:** *You can copy and paste all the lines below into the terminal prompt. The last line pasted still need a ENTER to be installed*. 33 | 34 | ```bash 35 | dotnet add package Newtonsoft.Json --version 12.0.3 36 | dotnet add package AWSSDK.Core --version 3.3.107.9 37 | dotnet add package AWSSDK.Lambda --version 3.3.109.39 38 | dotnet add package Amazon.Lambda.Core --version 1.1.0 39 | dotnet add package AWSSDK.Extensions.NETCore.Setup --version 3.3.101 40 | dotnet add package AWS.Logger.Core --version 1.6.0 41 | dotnet add package AWSSDK.S3 --version 3.3.111.10 42 | dotnet add package Amazon.Lambda.APIGatewayEvents --version 2.1.0 43 | dotnet add package AWSSDK.CognitoIdentityProvider --version 3.3.109.49 44 | ``` 45 | 46 | 1. Replace the entire **Function.cs** code with the following [Function.cs](Function.cs). This file is available in this repo at the lab-3-backend. 47 | 48 | :notebook: **Note:** This is a simple code that only makes a RestApi call to a [on-line tool](https://uinames.com/) to generate fake names. It defines the POCO classes to map API call results. The lambda function replies back to Amazon ApiGateway using a json structure based on the [APIGatewayProxyResponse](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayProxyResponse.cs), which is the required format for Lambda Proxy Integration. More information at [Set up Lambda Proxy Integration in API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html) 49 | 50 | 2. At the left panel of the **Visual Studio Code** right-click on the **CustomerList** directory and select New File, name it [cfn-sam-deployment.yaml](cfn-sam-deployment.yaml). Its content can be downloaded by clicking on cfn-sam-deployment.yaml. The file is available in this repo at the lab-3-backend. 51 | 52 | :notebook: **Note:** the cfn-sam-deployment.yaml is a [SAM](https://github.com/awslabs/serverless-application-model) cloudformation file, that creates the Amazon API Gateway and its integration with an existent Amazon Cognito. It also creates and deploys the lambda function with all permissions and integrations required for the function to work. 53 | 54 | 3. Compile the new code to create the binaries that will be deployed. 55 | ```bash 56 | dotnet publish -c Release 57 | ``` 58 | 59 | 4. Execute the following command to deploy the new function to AWS. **Use the same backet bucket from lab1**. Don't forget to replace the **Amazon Cognito User Pool Id** on the command: 60 | 61 | :notebook: **Note**: The [Support Commands Page](/SupportCommands.md) provides a list of useful commands that helps you identify the resources' names created during the labs executions; like the Amazon S3 bucked required for deployment and the Amazon Cognito User Pool Id. 62 | ```bash 63 | dotnet lambda deploy-serverless --template cfn-sam-deployment.yaml --s3-bucket --s3-prefix "customerlist/" --stack-name CustomerList --template-parameters UserPoolId= 64 | ``` 65 | 5. When the deployment finishes tiy will the following message: 66 | ```bash 67 | Stack finished updating with status: CREATE_COMPLETE 68 | ``` 69 | 6. List all the lambda functions names that you have deploy by running: 70 | ```bash 71 | aws lambda list-functions --query 'Functions[].FunctionName' 72 | ``` 73 | 7. Copy the lambda function name that starts with **CustomerList-Worker** and execute the follow aws cli command with the Lambda name. This command will invoke the CustomerList AWS Lambda function, saving the results into **response.json** file: 74 | ```bash 75 | aws lambda invoke --function-name response.json 76 | ``` 77 | 8. You should receive the following output: 78 | ```json 79 | { 80 | "StatusCode": 200, 81 | "ExecutedVersion": "$LATEST" 82 | } 83 | ``` 84 | 9. Check the response sent by the lambda function by executing ```more response.json```. You will encouter a json strucutre that contains : *{"statusCode":200,"headers":{"Content-Type":"application/json","Access-Control-Allow-Origin":"\*","Access-Control-Allow-Credentials":"true"},"multiValueHeaders":null,"body": **randon information**,"isBase64Encoded": false}* 85 | 86 | 10. Let's check the cloudformation stack resources that were created for the solution. The dotnet lambda blueprint took care of creating an Amazon API Gateway, IAM Roles and its policies and the Lambda itself. The following command shows the resource types and their Ids. 87 | 88 | ```bash 89 | aws cloudformation describe-stack-resources --stack-name CustomerList --query 'StackResources[*].{Type:ResourceType,Id:PhysicalResourceId}' 90 | ``` 91 | 92 | ### Step 2: Adding OAuth Scopes to the Amazon API Gateway that exposes the CustomerList microservices 93 | 94 | 1. Open the [Amazon API Gateway](https://console.aws.amazon.com/apigateway/) console. 95 | 2. Select the **CustomerList** Api. 96 | 3. Select the method **GET**. 97 | 4. Select **Method Request**. 98 | 5. Add **email**, **openid** and **profile** to the *OAuth Scopes* and click on update (The check mark icon at the end of the field). 99 | 100 | 101 | 102 | 6. Deploy a new version of Amazon API Gateway by going to **Actions** at the top left -> **Deploy API**. 103 | 104 | 7. Select **v1** for the **Deployment stage** and click on Deploy. 105 | 106 | 107 | 108 | 8. After successfully deploying the API, select **Authorizers**. You will notice that Amazon API Gateway is integrated with Amazon Cognito for authentication. When calling a REST API that requires authentication, you must use the **Authorization** key in the header of the call, with the JWT token provided by Amazon Cognito. 109 | 110 | 111 | 112 | ***You have now completed this Lab and can move onto [Lab 4](../lab-4-alltogether/).*** 113 | -------------------------------------------------------------------------------- /lab-3-backend/cfn-sam-deployment.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Sample SAM Template for DotNet Lambda Functions 4 | Parameters: 5 | ASPNETCOREENVIRONMENT: 6 | Type: String 7 | Default: Development 8 | UserPoolId: 9 | Type: String 10 | 11 | Resources: 12 | 13 | ServiceApi: 14 | Type: AWS::Serverless::Api 15 | Properties: 16 | StageName: v1 17 | Cors: 18 | AllowMethods: "'*'" 19 | AllowHeaders: "'*'" 20 | AllowOrigin: "'*'" 21 | Auth: 22 | Authorizers: 23 | CognitoAuthorizer: 24 | UserPoolArn: 25 | Fn::Sub: arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPoolId} 26 | 27 | Worker: 28 | Type: AWS::Serverless::Function 29 | Properties: 30 | Handler: CustomerList::CustomerList.Functions::Handler 31 | Runtime: dotnetcore3.1 32 | Timeout: 10 33 | MemorySize: 256 34 | Tracing: Active 35 | AutoPublishAlias: live 36 | CodeUri: ./bin/Release/netcoreapp3.1/publish 37 | Events: 38 | Get: 39 | Type: Api 40 | Properties: 41 | Path: / 42 | RestApiId: !Ref ServiceApi 43 | Method: GET 44 | Auth: 45 | Authorizer: CognitoAuthorizer 46 | Environment: 47 | Variables: 48 | ASPNETCORE_ENVIRONMENT: 49 | Fn::Sub: "${ASPNETCOREENVIRONMENT}" 50 | Policies: 51 | - Version: '2012-10-17' 52 | Statement: 53 | - Action: cognito-idp:* 54 | Effect: Allow 55 | Resource: 56 | - Fn::Sub: arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPoolId} -------------------------------------------------------------------------------- /lab-4-alltogether/Customers.cshtml: -------------------------------------------------------------------------------- 1 | @* 2 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: MIT-0 4 | *@ 5 | 6 | @page 7 | @model CustomersModel 8 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 9 | @{ 10 | Layout = "_Layout"; 11 | ViewData["Title"] = "Customers List"; 12 | } 13 | 14 |

@ViewData["Title"]

15 | 16 |
17 |
18 | Table update at: @DateTime.UtcNow UTC 19 |
20 |
21 |
22 | @ViewData["Message"] 23 | 24 | @section Scripts 25 | { 26 | 27 | 28 | 29 | 56 | } -------------------------------------------------------------------------------- /lab-4-alltogether/Customers.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IdentityModel.Tokens.Jwt; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Threading.Tasks; 10 | using Amazon.CognitoIdentityProvider; 11 | using Amazon.S3; 12 | using Microsoft.AspNetCore.Authentication; 13 | using Microsoft.AspNetCore.Authentication.Cookies; 14 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 15 | using Microsoft.AspNetCore.Mvc; 16 | using Microsoft.AspNetCore.Mvc.RazorPages; 17 | using Microsoft.AspNetCore.Mvc.Rendering; 18 | using Microsoft.Extensions.Configuration; 19 | using Microsoft.Extensions.Logging; 20 | using Microsoft.Extensions.Logging.Debug; 21 | 22 | 23 | namespace WebApp.Pages 24 | { 25 | public class CustomersModel : PageModel 26 | { 27 | private readonly ILogger _logger; 28 | 29 | public CustomersModel(ILogger logger) 30 | { 31 | _logger = logger; 32 | } 33 | 34 | public async Task OnGetAsync(int id) 35 | { 36 | if (!User.Identity.IsAuthenticated) 37 | { 38 | _logger.LogWarning("User not Authenticated!!!"); 39 | } 40 | 41 | string accessToken = await HttpContext.GetTokenAsync("access_token"); 42 | 43 | var JwtHandler = new JwtSecurityTokenHandler(); 44 | var jsonToken = JwtHandler.ReadToken(accessToken) as JwtSecurityToken; 45 | string exp = jsonToken.Claims.FirstOrDefault(c => c.Type == "exp").Value; 46 | string username = jsonToken.Claims.FirstOrDefault(c => c.Type == "username").Value; 47 | DateTime expDate = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(Double.Parse(exp)); 48 | string expDateStr = expDate.ToString(); 49 | 50 | if (expDate < DateTime.Now) 51 | { 52 | _logger.LogWarning("Token Expired!!!"); 53 | await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); 54 | _logger.LogInformation("Logging out successfuly Cookie"); 55 | await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); 56 | _logger.LogInformation("Logging out successfuly OpenId"); 57 | } 58 | 59 | ViewData["Token"] = accessToken; 60 | ViewData["Message"] = "Your token is valid until " + expDateStr; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /lab-4-alltogether/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IdentityModel.Tokens.Jwt; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Threading.Tasks; 10 | using Amazon.CognitoIdentityProvider; 11 | using Amazon.S3; 12 | using Microsoft.AspNetCore.Authentication; 13 | using Microsoft.AspNetCore.Authentication.Cookies; 14 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 15 | using Microsoft.AspNetCore.Mvc; 16 | using Microsoft.AspNetCore.Mvc.RazorPages; 17 | using Microsoft.AspNetCore.Mvc.Rendering; 18 | using Microsoft.Extensions.Configuration; 19 | using Microsoft.Extensions.Logging; 20 | using Microsoft.Extensions.Logging.Debug; 21 | 22 | 23 | namespace WebApp.Pages 24 | { 25 | public class IndexModel : PageModel 26 | { 27 | private readonly ILogger _logger; 28 | 29 | public IndexModel(ILogger logger) 30 | { 31 | _logger = logger; 32 | } 33 | 34 | public async Task OnGetAsync(int id) 35 | { 36 | if (!User.Identity.IsAuthenticated) 37 | { 38 | _logger.LogWarning("User not Authenticated!!!"); 39 | } 40 | 41 | string accessToken = await HttpContext.GetTokenAsync("access_token"); 42 | 43 | var JwtHandler = new JwtSecurityTokenHandler(); 44 | var jsonToken = JwtHandler.ReadToken(accessToken) as JwtSecurityToken; 45 | string exp = jsonToken.Claims.FirstOrDefault(c => c.Type == "exp").Value; 46 | string username = jsonToken.Claims.FirstOrDefault(c => c.Type == "username").Value; 47 | DateTime expDate = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddSeconds(Double.Parse(exp)); 48 | string expDateStr = expDate.ToString(); 49 | 50 | if (expDate < DateTime.Now) 51 | { 52 | _logger.LogWarning("Token Expired!!!"); 53 | await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); 54 | _logger.LogInformation("Logging out successfuly Cookie"); 55 | await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); 56 | _logger.LogInformation("Logging out successfuly OpenId"); 57 | } 58 | 59 | ViewData["Token"] = accessToken; 60 | ViewData["Message"] = "Your token is valid until " + expDateStr; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /lab-4-alltogether/README.md: -------------------------------------------------------------------------------- 1 | # Lab 4: Putting it all together 2 | 3 | Let's now stitch together the ASPNetCore WebApp with the CustomerList microservices. Both lambdas integrate with Amazon Cognito, using distinct methods. 4 | 5 | The AspNetCore WebApp connects with Amazon Cognito via OpenId library. On the other hand, the CustomerList microservice relies on the Amazon API Gateway Authorizer, which requires a valid identity token issued by the same Amazon Cognito that authenticated the user at the Hosted UI sign-in page. 6 | 7 | ## Step 1: Creating a new AspNetCore Razor page to reference the CustomerList microservices 8 | 9 | 1. At this point, you probably have two *Visual Studio Codes* opened; one with the WebApp project, and a second one with the CustomerList project. At the Visual Studio Code, go to *Window* and select the one that contains **WebApp** on its name. 10 | 11 | 2. From the Visual Studio, create two new files **Customers.cshtml** and **Customers.cshtml.cs** by right-clicking on the **Pages** directory and selecting **New file**. 12 | 13 | 14 | 15 | 3. Download the [Customers.cshtml](Customers.cshtml) file and copy its content to the newly created **Customers.cshtml**. Repeat the step for the [Customers.cshtml.cs](Customers.cshtml.cs) file too. Both files are also accessible at the *lab-4-alltogether* directory in the repo. 16 | 17 | 4. Execute ```aws apigateway get-rest-apis --query 'items[*].{name:name,restApiId:id}'``` and search for the result that contains **-CustomerList** in its the name. 18 | 5. Open the **Customers.cshtml**. Go to line 40 and replace the **restApiId** value (obtained on the previous step). The excerpt below shows what to look for. Don't forget to replace the **awsRegion** with the aws region you are running the workshop. 19 | ``` 20 | "ajax": { 21 | "url": "https://.execute-api..amazonaws.com/v1", 22 | "type": "GET", 23 | "beforeSend": setHeader, 24 | "error": function () { 25 | }, 26 | } 27 | ``` 28 | 29 | 6. Download the [Index.cshtml.cs](Index.cshtml.cs) and replace the existent **Index.cshtml.cs** with the content of the downloaded file. 30 | 31 | 7. Replace the existent **Index.cshtml** code with: 32 | ```html 33 | @page 34 | @model IndexModel 35 | @{ 36 | ViewData["Title"] = "Customers page"; 37 | } 38 | 39 |

Congratulations!!! Your ASP.NET Core Web Application is now Serverless

40 | 41 |

42 | @ViewData["Token"] 43 |

44 | 45 |

46 | @ViewData["Message"] 47 |

48 | ``` 49 | 50 | 8. Compiles the new code to create the binaries that will be deployed. 51 | ``` 52 | dotnet publish -c Release 53 | ``` 54 | 9. Execute the following command: 55 | 56 | :notebook: **Note**: The [Support Commands Page](/SupportCommands.md) provides a list of useful commands that helps you identify the resources' names created during the labs executions;like the Amazon S3 bucked required for deployment. 57 | 58 | ```bash 59 | dotnet lambda deploy-serverless --template serverless.template --s3-bucket --s3-prefix "aspnetcorewebapp/" --stack-name AspNetCoreWebApp 60 | ``` 61 | 9. Wait until the deployment finished: 62 | ```bash 63 | Stack finished updating with status: UPDATE_COMPLETE 64 | 65 | Output Name Value 66 | ------------------------------ -------------------------------------------------- 67 | ApiURL https://.execute-api..amazonaws.com/Prod/ 68 | ``` 69 | 70 | ## Step 2: Accessing the Web App 71 | 72 | 1. Let's try first access to the page shown on the last step. 73 | 2. Provide the email and password you have already created to log in 74 | 3. If you have followed the Step 1 correctly, the page will show you the JWT token and when it expires 75 | 76 | 77 | 78 | 4. Access the **Customers** page that calls the Customer List microservices by adding Customers at the URL: 79 | ``` 80 | https://.execute-api..amazonaws.com/Prod/Customers 81 | ``` 82 | 83 | 84 | ## Step 3: Testing the Microservices API Authentication using an API testing tool 85 | In this step, we will access the DotNet CustomerList API directly to show how the API Authentication works. In the example below the tool utilized is [Postman](https://www.getpostman.com/) 86 | 87 | 1. Obtain the restApiId for the CustomerList microservices by executing ```aws apigateway get-rest-apis --query 'items[*].{name:name,restApiId:id}'``` and copy the **restApiId** that contains **-CustomerList** in the name. 88 | 89 | 2. From the AspNetCoreWebApp page (https://*restApiId*.execute-api.*awsRegion*.amazonaws.com/Prod/) copy the token from the initial page. 90 | 91 | 92 | 93 | 3. Open the API tool of your choice and paste the https://*restApiId*.execute-api.*awsRegion*.amazonaws.com/v1 with the method **GET** and send the request. As the call hasn't provided any identity token, the result is an **Unauthorized** message. 94 | 95 | 96 | 97 | 4. At this time, add a header key/pair value for authentication. Add a key called **Authorization** and its value being the **identity token** copied at step 3.6. When you submit the call, the result will be the json file with all the customer's information. 98 | 99 | 100 | 101 | ***You have now completed this Lab and can move onto [Lab 5](../lab-5-xray/).*** 102 | -------------------------------------------------------------------------------- /lab-5-xray/README.md: -------------------------------------------------------------------------------- 1 | # Lab 5: Improving monitoring with x-Ray 2 | 3 | [AWS X-Ray](https://aws.amazon.com/xray/) traces user requests as they travel through your entire application. In this Lab, we will add X-Ray to the AWS Lambda functions. We will also enable it at the Amazon API Gateway to enrich the traceability. You can find more information about the AWS X-Ray SDK for .Net and .Net Core [here](https://github.com/aws/aws-xray-sdk-dotnet). 4 | 5 | ## Step 1: Enabling AWS X-Ray for Amazon API Gateway 6 | 7 | 1. Let's once again get the restApiId for the CustomerList microservices and AspNetCoreApp. Use Amazon Api Gateway [Support Commands Page](/SupportCommands.md) to obtain the **restApiId** that contains **AspNetCoreWebApp** and **CustomerList** in the their name. 8 | 9 | 2. Execute the following commands to enable logging and tracing with the *AspNetCoreWebApp* **restApiId** pointing to the *Prod* Stage. 10 | ``` 11 | aws apigateway update-stage --rest-api-id --stage-name 'Prod' --patch-operations op=replace,path=/*/*/logging/dataTrace,value=true 12 | ``` 13 | 14 | 3. similarly, run the following command with the *CustomerList* **restApiId** pointing to the *v1* Stage to enable logging and tracing. 15 | ``` 16 | aws apigateway update-stage --rest-api-id --stage-name 'v1' --patch-operations op=replace,path=/*/*/logging/dataTrace,value=true 17 | ``` 18 | 19 | ## Step 2: Adding AWS X-Ray to AspNetCore WebApp 20 | 21 | 1. On the **Visual Studio Code** Go to *Window* and select the **WebApp** windows. 22 | 23 | 2. In the terminal panel, execute the command to add the AWS X-Ray library: 24 | ```bash 25 | dotnet add package AWSXRayRecorder.Handlers.AspNetCore --version 2.7.1 26 | ``` 27 | 3. Instrument the X-Ray in the **Configure()** method of the **Startup.cs** file by adding **app.UseXRay("AspNetCoreWebApp");**. The exerpt below show where they should go in the Startup.cs file. 28 | 29 | ```c# 30 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 31 | { 32 | ... 33 | 34 | app.UseAuthorization(); 35 | app.UseXRay("AspNetCoreWebApp"); 36 | ... 37 | } 38 | ``` 39 | 40 | 1. Add the folling lines to **Index.cshtml.cs** and **Customers.cshtml.cs**. 41 | ```c# 42 | using Amazon.XRay.Recorder.Core; 43 | ``` 44 | 45 | 5. At the **Index.cshtml.cs** and **Customers.cshtml.cs** insert the following line into the **public async Task OnGetAsync(int id)** method, right after *string expDateStr = expDate.ToString();*. 46 | ```c# 47 | AWSXRayRecorder.Instance.AddAnnotation("username", username); 48 | ``` 49 | 6. Make sure you project still compiles correctly. 50 | ```bash 51 | dotnet publish -c Release 52 | ``` 53 | 7. Re-publish the project again by executing the following command: 54 | 55 | :notebook: **Note**: The [Support Commands Page](/SupportCommands.md) provides a list of useful commands that helps you identify the resources' names created during the labs executions; like the Amazon S3 bucked required for deployment. 56 | 57 | ```bash 58 | dotnet lambda deploy-serverless --template serverless.template --s3-bucket --s3-prefix "aspnetcorewebapp/" --stack-name AspNetCoreWebApp 59 | ``` 60 | 61 | ## Step 3: Visualizing the X-Ray information 62 | 63 | 1. Access the AspNetCoreWebApp app a few times. 64 | 2. Open the [AWS X-Ray](https://console.aws.amazon.com/xray/) console. 65 | 3. Select **Traces** at the left panel. 66 | 4. Take a look at the traces and their latencies. Both AspNetCoreWebApp and CustomerList have traces, because we enabled the AWS X-Ray integration at the lambda level. 67 | 5. Select **Analytics** on the left panel. 68 | 6. Choose the cog icon to explore table configuration options. Select **Annotation.username** to filter traces. 69 | 70 | 71 | 72 | 7. The table now shows the analytics per user. 73 | 74 | ***You have now completed this Lab and can move onto [Lab 99](../lab-99-cleanup/).*** 75 | -------------------------------------------------------------------------------- /lab-99-cleanup/README.md: -------------------------------------------------------------------------------- 1 | # Lab 99: Clean-up 2 | 3 | ## Step 1: Removing files from Amazon S3 4 | 5 | :notebook: **Note**: The [Support Commands Page](/SupportCommands.md) provides a list of useful commands that help you identify the names of the resources created in the labs, as the Amazon S3 bucked required for deployment. 6 | 7 | 1. recursively deletes all objects under a bucket, for when deleting the Cloudformation Stack the bucket can be successfully deleted. If a bucket has objects, it won't get deleted. 8 | ``` 9 | aws s3 rm s3:/// --recursive 10 | ``` 11 | 12 | ## Step 2: Removing Amazon Cognito 13 | :notebook: **Note**: The [Support Commands Page](/SupportCommands.md) provides a list of useful commands that help you identify the names of the resources created in the labs; like Listing the Amazon Cognito user pools. 14 | 15 | 1. Obtain the Amazon Cognito User pool Id for the *first initial* + *last initial* + *-CognitoUserPool*. 16 | 2. Delete the user pool domain: 17 | ``` 18 | aws cognito-idp delete-user-pool-domain --domain + + -dotnetcore --user-pool-id 19 | ``` 20 | 3. Delete the user pool by executing the command: 21 | ``` 22 | aws cognito-idp delete-user-pool --user-pool-id 23 | ``` 24 | 25 | ## Step 3: Removing Cloudformation Stacks 26 | 27 | 1. Open the [CloudFormation](https://console.aws.amazon.com/cloudformation/) console. 28 | 2. Select the *CustomerList* Select **Delete** and confirm by clicking on **Delete stack** 29 | 3. Select the *AspNetCoreWebApp* Select **Delete** and confirm by clicking on **Delete stack** 30 | 4. Select the *DotNetLab* Select **Delete** and confirm by clicking on **Delete stack** 31 | 5. Confirm that all stacks were successfuly deleted. 32 | 33 | **Thank you** --------------------------------------------------------------------------------