diff --git a/Libraries/src/Amazon.Lambda.AspNetCoreServer/APIGatewayProxyFunction.cs b/Libraries/src/Amazon.Lambda.AspNetCoreServer/APIGatewayProxyFunction.cs index 9be308984..cd9daf422 100644 --- a/Libraries/src/Amazon.Lambda.AspNetCoreServer/APIGatewayProxyFunction.cs +++ b/Libraries/src/Amazon.Lambda.AspNetCoreServer/APIGatewayProxyFunction.cs @@ -76,6 +76,7 @@ protected APIGatewayProxyFunction(AspNetCoreStartupMode startupMode) } + /// /// /// @@ -86,16 +87,35 @@ protected APIGatewayProxyFunction(StartupMode startupMode) } - - private protected override void InternalPostCreateContext(HostingApplication.Context context, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext) + private protected override void InternalPostCreateContext( + HostingApplication.Context context, + APIGatewayProxyRequest apiGatewayRequest, + ILambdaContext lambdaContext) { - if (apiGatewayRequest?.RequestContext?.Authorizer?.Claims != null) + var authorizer = apiGatewayRequest?.RequestContext?.Authorizer; + + if (authorizer != null) { - var identity = new ClaimsIdentity(apiGatewayRequest.RequestContext.Authorizer.Claims.Select( - entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity"); + // handling claims output from cognito user pool authorizer + if (authorizer.Claims != null && authorizer.Claims.Count != 0) + { + var identity = new ClaimsIdentity(authorizer.Claims.Select( + entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity"); + + lambdaContext.Logger.LogLine( + $"Configuring HttpContext.User with {authorizer.Claims.Count} claims coming from API Gateway's Request Context"); + context.HttpContext.User = new ClaimsPrincipal(identity); + } + else + { + // handling claims output from custom lambda authorizer + var identity = new ClaimsIdentity(authorizer.Select( + entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity"); - _logger.LogDebug($"Configuring HttpContext.User with {apiGatewayRequest.RequestContext.Authorizer.Claims.Count} claims coming from API Gateway's Request Context"); - context.HttpContext.User = new ClaimsPrincipal(identity); + lambdaContext.Logger.LogLine( + $"Configuring HttpContext.User with {authorizer.Count} claims coming from API Gateway's Request Context"); + context.HttpContext.User = new ClaimsPrincipal(identity); + } } } @@ -115,12 +135,13 @@ private protected override void InternalCustomResponseExceptionHandling(HostingA protected override void MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext) { { - var requestFeatures = (IHttpRequestFeature)features; + var requestFeatures = (IHttpRequestFeature) features; requestFeatures.Scheme = "https"; requestFeatures.Method = apiGatewayRequest.HttpMethod; string path = null; - if (apiGatewayRequest.PathParameters != null && apiGatewayRequest.PathParameters.ContainsKey("proxy") && !string.IsNullOrEmpty(apiGatewayRequest.Resource)) + if (apiGatewayRequest.PathParameters != null && apiGatewayRequest.PathParameters.ContainsKey("proxy") && + !string.IsNullOrEmpty(apiGatewayRequest.Resource)) { var proxyPath = apiGatewayRequest.PathParameters["proxy"]; path = apiGatewayRequest.Resource.Replace("{proxy+}", proxyPath); @@ -157,7 +178,8 @@ protected override void MarshallRequest(InvokeFeatures features, APIGatewayProxy if (requestContextPath.EndsWith(path)) { - requestFeatures.PathBase = requestContextPath.Substring(0, requestContextPath.Length - requestFeatures.Path.Length); + requestFeatures.PathBase = requestContextPath.Substring(0, + requestContextPath.Length - requestFeatures.Path.Length); } } @@ -190,7 +212,7 @@ protected override void MarshallRequest(InvokeFeatures features, APIGatewayProxy { // set up connection features - var connectionFeatures = (IHttpConnectionFeature)features; + var connectionFeatures = (IHttpConnectionFeature) features; if (!string.IsNullOrEmpty(apiGatewayRequest?.RequestContext?.Identity?.SourceIp) && IPAddress.TryParse(apiGatewayRequest.RequestContext.Identity.SourceIp, out var remoteIpAddress)) @@ -207,7 +229,6 @@ protected override void MarshallRequest(InvokeFeatures features, APIGatewayProxy // was marshalled into ASP.NET Core request. PostMarshallConnectionFeature(connectionFeatures, apiGatewayRequest, lambdaContext); } - } /// @@ -261,4 +282,4 @@ protected override APIGatewayProxyResponse MarshallResponse(IHttpResponseFeature return response; } } -} +} \ No newline at end of file diff --git a/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs b/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs index 3d8668521..4b97566f2 100644 --- a/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs +++ b/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Net; using System.Reflection; - using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -33,7 +32,7 @@ public async Task TestGetAllValues() Assert.True(response.MultiValueHeaders.ContainsKey("Content-Type")); Assert.Equal("application/json; charset=utf-8", response.MultiValueHeaders["Content-Type"][0]); - Assert.Contains("OnStarting Called", ((TestLambdaLogger)context.Logger).Buffer.ToString()); + Assert.Contains("OnStarting Called", ((TestLambdaLogger) context.Logger).Buffer.ToString()); } [Fact] @@ -160,8 +159,9 @@ public void TestCustomAuthorizerSerialization() new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement { Effect = "Allow", - Action = new HashSet { "execute-api:Invoke" }, - Resource = new HashSet { "arn:aws:execute-api:us-west-2:1234567890:apit123d45/Prod/GET/*" } + Action = new HashSet {"execute-api:Invoke"}, + Resource = new HashSet + {"arn:aws:execute-api:us-west-2:1234567890:apit123d45/Prod/GET/*"} } } } @@ -177,8 +177,8 @@ public void TestCustomAuthorizerSerialization() public async Task TestGetBinaryContent() { var response = await this.InvokeAPIGatewayRequest("values-get-binary-apigatway-request.json"); - - Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + + Assert.Equal((int) HttpStatusCode.OK, response.StatusCode); IList contentType; Assert.True(response.MultiValueHeaders.TryGetValue("Content-Type", out contentType), @@ -186,12 +186,12 @@ public async Task TestGetBinaryContent() Assert.Equal("application/octet-stream", contentType[0]); Assert.NotNull(response.Body); Assert.True(response.Body.Length > 0, - "Body content is not empty"); + "Body content is not empty"); Assert.True(response.IsBase64Encoded, "Response IsBase64Encoded"); // Compute a 256-byte array, with values 0-255 - var binExpected = new byte[byte.MaxValue].Select((val, index) => (byte)index).ToArray(); + var binExpected = new byte[byte.MaxValue].Select((val, index) => (byte) index).ToArray(); var binActual = Convert.FromBase64String(response.Body); Assert.Equal(binExpected, binActual); } @@ -251,6 +251,15 @@ public async Task TestAuthTestAccess() Assert.Equal("You Have Access", response.Body); } + [Fact] + public async Task TestAuthTestAccess_CustomLambdaAuthorizerClaims() + { + var response = + await this.InvokeAPIGatewayRequest("authtest-access-request-custom-lambda-authorizer-output.json"); + + Assert.Equal(200, response.StatusCode); + Assert.Equal("You Have Access", response.Body); + } [Fact] public async Task TestAuthTestNoAccess() @@ -310,4 +319,4 @@ private async Task InvokeAPIGatewayRequest(TestLambdaCo return await lambdaFunction.FunctionHandlerAsync(request, context); } } -} +} \ No newline at end of file diff --git a/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/authtest-access-request-custom-lambda-authorizer-output.json b/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/authtest-access-request-custom-lambda-authorizer-output.json new file mode 100644 index 000000000..f8b920cb8 --- /dev/null +++ b/Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/authtest-access-request-custom-lambda-authorizer-output.json @@ -0,0 +1,64 @@ +{ + "resource": "/{proxy+}", + "path": "/api/authtest", + "httpMethod": "GET", + "headers": { + "Authorization": "eyJraWQiOiJLdXprWCtcL0E4MWlVZGU5QSt2SFBMdzZCTUFmQzVqUGJ1NTZiT0VGNXdkaz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI4ZWYzZTQ1NS0zODY5LTQwMmUtYWM5ZS1mODhlODZjMzIwYjMiLCJjb2duaXRvOmdyb3VwcyI6WyJBZG1pbiJdLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6b25hd3MuY29tXC91cy13ZXN0LTJfUExpM2NmMHUwIiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjp0cnVlLCJjb2duaXRvOnVzZXJuYW1lIjoibm9ybWoiLCJhdWQiOiIzbXVzaGZjOHNnbTh1b2FjdmlmNXZoa3Q0OSIsImV2ZW50X2lkIjoiNzU3NjBmNTgtZjk4NC0xMWU3LThkNGEtMjM4OWVmYzUwZDY4IiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE1MTU5NzMyOTYsInBob25lX251bWJlciI6IisxNDI1NzM2NzM0OSIsImV4cCI6MTUxNTk3Njg5NiwiaWF0IjoxNTE1OTczMjk2LCJlbWFpbCI6ImpvaGFuc28yQGdtYWlsLmNvbSJ9.v1zIeTutUMzgXGoQy5dpOXUW6km9Ye-X-iLehgn1fO4HeJPZ9rOY9jDRMyRjyF5I57sAsN1v9uB3f98gEdStzO4qpASlYK7F7CtcenJ2lZYNU4gJPDQqEDdoMvE5Nir89RL09PYFceKaZw2Qr53VTLBfwaNGJYeSYeiZN09RL6fXwciH5h15rGwgNpl3kvzZOTLCqHNv3J7Y5POr8BjT2DvqUVJv7X1M5pOzHxWIqtBfAfyhEzZftIDqNKsI_aKZolkRd_UI77dSMNuZokJSAKlEuXc6fNJ556R3xSAhEli5DeZUIMdQLQVB7pIQzRlXvt2M0MMK-uC2d7_kUWAkVg", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "zzqupfvhrk.execute-api.us-west-2.amazonaws.com", + "Via": "1.1 ca79756ec49e2babf1b916300304b2fb.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "3OhHlF5fim8xxjG1-RZEFK4nos0t-JtPu6vvpNkUg9NOcl-Os53gBg==", + "X-Amzn-Trace-Id": "Root=1-5a5beab2-7cd6a52c614f43910ab9be30", + "X-Forwarded-For": "50.35.77.7, 52.46.17.45", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "queryStringParameters": null, + "pathParameters": { + "proxy": "api/authtest" + }, + "stageVariables": null, + "requestContext": { + "resourceId": "8gffya", + "authorizer": { + "cognito:groups": "Admin", + "phone_number_verified": "true", + "cognito:username": "normj", + "aud": "3mushfc8sgm8uoacvif5vhkt49", + "event_id": "75760f58-f984-11e7-8d4a-2389efc50d68", + "token_use": "id", + "auth_time": "1515973296", + "you_are_special": "true" + }, + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "requestTime": "14/Jan/2018:23:41:38 +0000", + "path": "/Prod/api/authtest", + "accountId": "626492997873", + "protocol": "HTTP/1.1", + "stage": "Prod", + "requestTimeEpoch": 1515973298687, + "requestId": "7713b963-f984-11e7-b02a-f346964d4540", + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "sourceIp": "50.35.77.7", + "accessKey": null, + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": null, + "user": null + }, + "apiId": "zzqupfvhrk" + }, + "body": null, + "isBase64Encoded": false +} \ No newline at end of file