Skip to content

Commit 3d2d776

Browse files
committed
Allow claims coming in from the RequestContext to of an API Gateway request to be serialized into APIGatewayCustomAuthorizerContext.
If there are claims create ClaimsPrincipal for the HttpContext.User with the claims for the AspNetCoreServer This address GitHub issue #98
1 parent 7693c9c commit 3d2d776

File tree

10 files changed

+315
-10
lines changed

10 files changed

+315
-10
lines changed
Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,139 @@
11
namespace Amazon.Lambda.APIGatewayEvents
22
{
3+
using System;
4+
using System.Collections.Generic;
35
using System.Runtime.Serialization;
6+
using Newtonsoft.Json.Linq;
47

58
/// <summary>
69
/// An object representing the expected format of an API Gateway custom authorizer response.
710
/// </summary>
811
[DataContract]
9-
public class APIGatewayCustomAuthorizerContext
12+
public class APIGatewayCustomAuthorizerContext : Dictionary<string, object>
1013
{
1114
/// <summary>
1215
/// Gets or sets the 'principalId' property.
1316
/// </summary>
14-
[DataMember(Name = "principalId", IsRequired = false)]
15-
public string PrincipalId { get; set; }
17+
[Obsolete("This property is obsolete. Code should be updated to use the string index property like authorizer[\"principalId\"]")]
18+
public string PrincipalId
19+
{
20+
get
21+
{
22+
object value;
23+
if (this.TryGetValue("principalId", out value))
24+
return value.ToString();
25+
return null;
26+
}
27+
set
28+
{
29+
this["principalId"] = value;
30+
}
31+
}
1632

1733
/// <summary>
1834
/// Gets or sets the 'stringKey' property.
1935
/// </summary>
20-
[DataMember(Name = "stringKey", IsRequired = false)]
21-
public string StringKey { get; set; }
36+
[Obsolete("This property is obsolete. Code should be updated to use the string index property like authorizer[\"stringKey\"]")]
37+
public string StringKey
38+
{
39+
get
40+
{
41+
object value;
42+
if (this.TryGetValue("stringKey", out value))
43+
return value.ToString();
44+
return null;
45+
}
46+
set
47+
{
48+
this["stringKey"] = value;
49+
}
50+
}
2251

2352
/// <summary>
2453
/// Gets or sets the 'numKey' property.
2554
/// </summary>
26-
[DataMember(Name = "numKey", IsRequired = false)]
27-
public int? NumKey { get; set; }
55+
[Obsolete("This property is obsolete. Code should be updated to use the string index property like authorizer[\"numKey\"]")]
56+
public int? NumKey
57+
{
58+
get
59+
{
60+
object value;
61+
if (this.TryGetValue("numKey", out value))
62+
{
63+
int i;
64+
if (int.TryParse(value?.ToString(), out i))
65+
{
66+
return i;
67+
}
68+
}
69+
70+
return null;
71+
}
72+
set
73+
{
74+
this["numKey"] = value;
75+
}
76+
}
2877

2978
/// <summary>
3079
/// Gets or sets the 'boolKey' property.
3180
/// </summary>
32-
[DataMember(Name = "boolKey", IsRequired = false)]
33-
public bool? BoolKey { get; set; }
81+
[Obsolete("This property is obsolete. Code should be updated to use the string index property like authorizer[\"boolKey\"]")]
82+
public bool? BoolKey
83+
{
84+
get
85+
{
86+
object value;
87+
if (this.TryGetValue("boolKey", out value))
88+
{
89+
bool b;
90+
if(bool.TryParse(value?.ToString(), out b))
91+
{
92+
return b;
93+
}
94+
}
95+
96+
return null;
97+
}
98+
set
99+
{
100+
this["boolKey"] = value;
101+
}
102+
}
103+
104+
Dictionary<string, string> _claims;
105+
/// <summary>
106+
/// Gets or sets the claims coming from Cognito
107+
/// </summary>
108+
public Dictionary<string, string> Claims
109+
{
110+
get
111+
{
112+
if(_claims == null)
113+
{
114+
_claims = new Dictionary<string, string>();
115+
116+
object value;
117+
if(this.TryGetValue("claims", out value))
118+
{
119+
JObject jsonClaims = value as JObject;
120+
if (jsonClaims != null)
121+
{
122+
foreach (JProperty property in jsonClaims.Properties())
123+
{
124+
_claims[property.Name] = property.Value?.ToString();
125+
126+
}
127+
}
128+
}
129+
}
130+
131+
return _claims;
132+
}
133+
set
134+
{
135+
this._claims = value;
136+
}
137+
}
34138
}
35139
}

Libraries/src/Amazon.Lambda.APIGatewayEvents/Amazon.Lambda.APIGatewayEvents.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18+
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
1819
<PackageReference Include="System.Collections" Version="4.0.11" />
1920
<PackageReference Include="System.Runtime" Version="4.1.0" />
2021
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.1.1" />

Libraries/src/Amazon.Lambda.AspNetCoreServer/APIGatewayProxyFunction.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
using System;
1111
using System.Collections.Generic;
1212
using System.IO;
13+
using System.Linq;
1314
using System.Net;
1415
using System.Reflection;
16+
using System.Security.Claims;
1517
using System.Text;
1618
using System.Text.Encodings.Web;
1719
using System.Threading.Tasks;
@@ -177,6 +179,15 @@ public virtual async Task<APIGatewayProxyResponse> FunctionHandlerAsync(APIGatew
177179

178180
var context = this.CreateContext(features);
179181

182+
if (request?.RequestContext?.Authorizer?.Claims != null)
183+
{
184+
var identity = new ClaimsIdentity(request.RequestContext.Authorizer.Claims.Select(
185+
entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity");
186+
187+
lambdaContext.Logger.LogLine($"Configuring HttpContext.User with {request.RequestContext.Authorizer.Claims.Count} claims coming from API Gateway's Request Context");
188+
context.HttpContext.User = new ClaimsPrincipal(identity);
189+
}
190+
180191
// Add along the Lambda objects to the HttpContext to give access to Lambda to them in the ASP.NET Core application
181192
context.HttpContext.Items[LAMBDA_CONTEXT] = lambdaContext;
182193
context.HttpContext.Items[APIGATEWAY_REQUEST] = request;

Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/Amazon.Lambda.AspNetCoreServer.Test.csproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,24 @@
1111
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
1212
</PropertyGroup>
1313

14+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
15+
<NoWarn>1701;1702;1705;CS0618</NoWarn>
16+
</PropertyGroup>
17+
18+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
19+
<NoWarn>1701;1702;1705;CS0618</NoWarn>
20+
</PropertyGroup>
21+
1422
<ItemGroup>
1523
<Content Include="*.json">
1624
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
1725
</Content>
1826
</ItemGroup>
1927

28+
<ItemGroup>
29+
<None Remove="authtest-access-request.json" />
30+
</ItemGroup>
31+
2032
<ItemGroup>
2133
<ProjectReference Include="..\TestWebApp\TestWebApp.csproj" />
2234
<ProjectReference Include="..\..\src\Amazon.Lambda.TestUtilities\Amazon.Lambda.TestUtilities.csproj" />

Libraries/test/Amazon.Lambda.AspNetCoreServer.Test/TestCallingWebAPI.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,24 @@ public async Task TestEscapeCharacterInResourcePath()
189189
Assert.Equal("value=query string", response.Body);
190190
}
191191

192+
[Fact]
193+
public async Task TestAuthTestAccess()
194+
{
195+
var response = await this.InvokeAPIGatewayRequest("authtest-access-request.json");
196+
197+
Assert.Equal(200, response.StatusCode);
198+
Assert.Equal("You Have Access", response.Body);
199+
}
200+
201+
202+
[Fact]
203+
public async Task TestAuthTestNoAccess()
204+
{
205+
var response = await this.InvokeAPIGatewayRequest("authtest-noaccess-request.json");
206+
207+
Assert.NotEqual(200, response.StatusCode);
208+
}
209+
192210
private async Task<APIGatewayProxyResponse> InvokeAPIGatewayRequest(string fileName)
193211
{
194212
var context = new TestLambdaContext();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"resource": "/{proxy+}",
3+
"path": "/api/authtest",
4+
"httpMethod": "GET",
5+
"headers": {
6+
"Authorization": "eyJraWQiOiJLdXprWCtcL0E4MWlVZGU5QSt2SFBMdzZCTUFmQzVqUGJ1NTZiT0VGNXdkaz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI4ZWYzZTQ1NS0zODY5LTQwMmUtYWM5ZS1mODhlODZjMzIwYjMiLCJjb2duaXRvOmdyb3VwcyI6WyJBZG1pbiJdLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6b25hd3MuY29tXC91cy13ZXN0LTJfUExpM2NmMHUwIiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjp0cnVlLCJjb2duaXRvOnVzZXJuYW1lIjoibm9ybWoiLCJhdWQiOiIzbXVzaGZjOHNnbTh1b2FjdmlmNXZoa3Q0OSIsImV2ZW50X2lkIjoiNzU3NjBmNTgtZjk4NC0xMWU3LThkNGEtMjM4OWVmYzUwZDY4IiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE1MTU5NzMyOTYsInBob25lX251bWJlciI6IisxNDI1NzM2NzM0OSIsImV4cCI6MTUxNTk3Njg5NiwiaWF0IjoxNTE1OTczMjk2LCJlbWFpbCI6ImpvaGFuc28yQGdtYWlsLmNvbSJ9.v1zIeTutUMzgXGoQy5dpOXUW6km9Ye-X-iLehgn1fO4HeJPZ9rOY9jDRMyRjyF5I57sAsN1v9uB3f98gEdStzO4qpASlYK7F7CtcenJ2lZYNU4gJPDQqEDdoMvE5Nir89RL09PYFceKaZw2Qr53VTLBfwaNGJYeSYeiZN09RL6fXwciH5h15rGwgNpl3kvzZOTLCqHNv3J7Y5POr8BjT2DvqUVJv7X1M5pOzHxWIqtBfAfyhEzZftIDqNKsI_aKZolkRd_UI77dSMNuZokJSAKlEuXc6fNJ556R3xSAhEli5DeZUIMdQLQVB7pIQzRlXvt2M0MMK-uC2d7_kUWAkVg",
7+
"CloudFront-Forwarded-Proto": "https",
8+
"CloudFront-Is-Desktop-Viewer": "true",
9+
"CloudFront-Is-Mobile-Viewer": "false",
10+
"CloudFront-Is-SmartTV-Viewer": "false",
11+
"CloudFront-Is-Tablet-Viewer": "false",
12+
"CloudFront-Viewer-Country": "US",
13+
"Host": "zzqupfvhrk.execute-api.us-west-2.amazonaws.com",
14+
"Via": "1.1 ca79756ec49e2babf1b916300304b2fb.cloudfront.net (CloudFront)",
15+
"X-Amz-Cf-Id": "3OhHlF5fim8xxjG1-RZEFK4nos0t-JtPu6vvpNkUg9NOcl-Os53gBg==",
16+
"X-Amzn-Trace-Id": "Root=1-5a5beab2-7cd6a52c614f43910ab9be30",
17+
"X-Forwarded-For": "50.35.77.7, 52.46.17.45",
18+
"X-Forwarded-Port": "443",
19+
"X-Forwarded-Proto": "https"
20+
},
21+
"queryStringParameters": null,
22+
"pathParameters": {
23+
"proxy": "api/authtest"
24+
},
25+
"stageVariables": null,
26+
"requestContext": {
27+
"resourceId": "8gffya",
28+
"authorizer": {
29+
"claims": {
30+
"cognito:groups": "Admin",
31+
"phone_number_verified": "true",
32+
"cognito:username": "normj",
33+
"aud": "3mushfc8sgm8uoacvif5vhkt49",
34+
"event_id": "75760f58-f984-11e7-8d4a-2389efc50d68",
35+
"token_use": "id",
36+
"auth_time": "1515973296",
37+
"you_are_special" : "true"
38+
}
39+
},
40+
"resourcePath": "/{proxy+}",
41+
"httpMethod": "GET",
42+
"requestTime": "14/Jan/2018:23:41:38 +0000",
43+
"path": "/Prod/api/authtest",
44+
"accountId": "626492997873",
45+
"protocol": "HTTP/1.1",
46+
"stage": "Prod",
47+
"requestTimeEpoch": 1515973298687,
48+
"requestId": "7713b963-f984-11e7-b02a-f346964d4540",
49+
"identity": {
50+
"cognitoIdentityPoolId": null,
51+
"accountId": null,
52+
"cognitoIdentityId": null,
53+
"caller": null,
54+
"sourceIp": "50.35.77.7",
55+
"accessKey": null,
56+
"cognitoAuthenticationType": null,
57+
"cognitoAuthenticationProvider": null,
58+
"userArn": null,
59+
"userAgent": null,
60+
"user": null
61+
},
62+
"apiId": "zzqupfvhrk"
63+
},
64+
"body": null,
65+
"isBase64Encoded": false
66+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"resource": "/{proxy+}",
3+
"path": "/api/authtest",
4+
"httpMethod": "GET",
5+
"headers": {
6+
"Authorization": "eyJraWQiOiJLdXprWCtcL0E4MWlVZGU5QSt2SFBMdzZCTUFmQzVqUGJ1NTZiT0VGNXdkaz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI4ZWYzZTQ1NS0zODY5LTQwMmUtYWM5ZS1mODhlODZjMzIwYjMiLCJjb2duaXRvOmdyb3VwcyI6WyJBZG1pbiJdLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6b25hd3MuY29tXC91cy13ZXN0LTJfUExpM2NmMHUwIiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjp0cnVlLCJjb2duaXRvOnVzZXJuYW1lIjoibm9ybWoiLCJhdWQiOiIzbXVzaGZjOHNnbTh1b2FjdmlmNXZoa3Q0OSIsImV2ZW50X2lkIjoiNzU3NjBmNTgtZjk4NC0xMWU3LThkNGEtMjM4OWVmYzUwZDY4IiwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE1MTU5NzMyOTYsInBob25lX251bWJlciI6IisxNDI1NzM2NzM0OSIsImV4cCI6MTUxNTk3Njg5NiwiaWF0IjoxNTE1OTczMjk2LCJlbWFpbCI6ImpvaGFuc28yQGdtYWlsLmNvbSJ9.v1zIeTutUMzgXGoQy5dpOXUW6km9Ye-X-iLehgn1fO4HeJPZ9rOY9jDRMyRjyF5I57sAsN1v9uB3f98gEdStzO4qpASlYK7F7CtcenJ2lZYNU4gJPDQqEDdoMvE5Nir89RL09PYFceKaZw2Qr53VTLBfwaNGJYeSYeiZN09RL6fXwciH5h15rGwgNpl3kvzZOTLCqHNv3J7Y5POr8BjT2DvqUVJv7X1M5pOzHxWIqtBfAfyhEzZftIDqNKsI_aKZolkRd_UI77dSMNuZokJSAKlEuXc6fNJ556R3xSAhEli5DeZUIMdQLQVB7pIQzRlXvt2M0MMK-uC2d7_kUWAkVg",
7+
"CloudFront-Forwarded-Proto": "https",
8+
"CloudFront-Is-Desktop-Viewer": "true",
9+
"CloudFront-Is-Mobile-Viewer": "false",
10+
"CloudFront-Is-SmartTV-Viewer": "false",
11+
"CloudFront-Is-Tablet-Viewer": "false",
12+
"CloudFront-Viewer-Country": "US",
13+
"Host": "zzqupfvhrk.execute-api.us-west-2.amazonaws.com",
14+
"Via": "1.1 ca79756ec49e2babf1b916300304b2fb.cloudfront.net (CloudFront)",
15+
"X-Amz-Cf-Id": "3OhHlF5fim8xxjG1-RZEFK4nos0t-JtPu6vvpNkUg9NOcl-Os53gBg==",
16+
"X-Amzn-Trace-Id": "Root=1-5a5beab2-7cd6a52c614f43910ab9be30",
17+
"X-Forwarded-For": "50.35.77.7, 52.46.17.45",
18+
"X-Forwarded-Port": "443",
19+
"X-Forwarded-Proto": "https"
20+
},
21+
"queryStringParameters": null,
22+
"pathParameters": {
23+
"proxy": "api/authtest"
24+
},
25+
"stageVariables": null,
26+
"requestContext": {
27+
"resourceId": "8gffya",
28+
"authorizer": {
29+
"claims": {
30+
"cognito:groups": "Admin",
31+
"phone_number_verified": "true",
32+
"cognito:username": "normj",
33+
"aud": "3mushfc8sgm8uoacvif5vhkt49",
34+
"event_id": "75760f58-f984-11e7-8d4a-2389efc50d68",
35+
"token_use": "id",
36+
"auth_time": "1515973296"
37+
}
38+
},
39+
"resourcePath": "/{proxy+}",
40+
"httpMethod": "GET",
41+
"requestTime": "14/Jan/2018:23:41:38 +0000",
42+
"path": "/Prod/api/authtest",
43+
"accountId": "626492997873",
44+
"protocol": "HTTP/1.1",
45+
"stage": "Prod",
46+
"requestTimeEpoch": 1515973298687,
47+
"requestId": "7713b963-f984-11e7-b02a-f346964d4540",
48+
"identity": {
49+
"cognitoIdentityPoolId": null,
50+
"accountId": null,
51+
"cognitoIdentityId": null,
52+
"caller": null,
53+
"sourceIp": "50.35.77.7",
54+
"accessKey": null,
55+
"cognitoAuthenticationType": null,
56+
"cognitoAuthenticationProvider": null,
57+
"userArn": null,
58+
"userAgent": null,
59+
"user": null
60+
},
61+
"apiId": "zzqupfvhrk"
62+
},
63+
"body": null,
64+
"isBase64Encoded": false
65+
}

Libraries/test/Amazon.Lambda.Tools.Test/ValidateAspNetCoreAllReferenceTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void NewerAspNetCoreReference()
1818
var manifest = File.ReadAllText(@"ManifestTestFiles/SampleManifest.xml");
1919
var projectFile = File.ReadAllText(@"ManifestTestFiles/NewerAspNetCoreReference.xml");
2020

21-
Assert.Throws<AmazonLambdaException>(() => Utilities.ValidateMicrosoftAspNetCoreAllReferenceWithManifest(logger, manifest, projectFile));
21+
Assert.Throws<LambdaToolsException>(() => Utilities.ValidateMicrosoftAspNetCoreAllReferenceWithManifest(logger, manifest, projectFile));
2222
}
2323

2424
[Fact]

0 commit comments

Comments
 (0)