Skip to content

Commit 9c7f589

Browse files
committed
Multi scheme test added
1 parent e29771f commit 9c7f589

File tree

3 files changed

+162
-11
lines changed

3 files changed

+162
-11
lines changed

test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyHandlerBaseTests.cs

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
// Licensed under the MIT License. See LICENSE file in the project root for license information.
33

44
using AspNetCore.Authentication.ApiKey.Tests.Infrastructure;
5+
using Microsoft.AspNetCore.Authorization;
6+
using Microsoft.Extensions.DependencyInjection;
57
using System;
8+
using System.Collections.Generic;
9+
using System.Linq;
610
using System.Net;
711
using System.Net.Http;
12+
using System.Net.Http.Headers;
13+
using System.Security.Claims;
814
using System.Text.Json;
915
using System.Threading.Tasks;
1016
using Xunit;
@@ -389,11 +395,127 @@ public async Task HandleAuthenticate_OnAuthenticationSucceeded_result_not_null()
389395
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
390396
}
391397

392-
#endregion // HandleAuthenticate
398+
#endregion // HandleAuthenticate
393399

394-
private async Task<ClaimsPrincipalDto> DeserializeClaimsPrincipalAsync(HttpResponseMessage response)
400+
#region Multi-Scheme
401+
402+
[Fact]
403+
public async Task MultiScheme()
404+
{
405+
var keyName1 = "Key1";
406+
var keyName2 = "Key2";
407+
var keyName3 = "Key3";
408+
var keyName4 = "Key4";
409+
var claimProvider1 = new ClaimDto { Type = "Provider", Value = "1" };
410+
var claimProvider2 = new ClaimDto { Type = "Provider", Value = "2" };
411+
var claimRole = new ClaimDto(FakeApiKeys.FakeRoleClaim);
412+
var schemes = new List<string> { "InHeader", "InHeaderWithProvider", "InAuthorizationHeader", "InQueryParams" };
413+
414+
using var server = TestServerBuilder.BuildTestServer(services =>
415+
{
416+
services.AddAuthentication("InHeader")
417+
.AddApiKeyInHeader("InHeader", options =>
418+
{
419+
options.Realm = TestServerBuilder.Realm;
420+
options.KeyName = keyName1;
421+
options.Events.OnValidateKey = context =>
422+
{
423+
context.Response.Headers.Add("X-Custom", "InHeader Scheme");
424+
context.ValidationSucceeded();
425+
return Task.CompletedTask;
426+
};
427+
})
428+
.AddApiKeyInHeader<FakeApiKeyProviderLocal_1>("InHeaderWithProvider", options =>
429+
{
430+
options.Realm = TestServerBuilder.Realm;
431+
options.KeyName = keyName2;
432+
})
433+
.AddApiKeyInAuthorizationHeader<FakeApiKeyProviderLocal_2>("InAuthorizationHeader", options =>
434+
{
435+
options.Realm = TestServerBuilder.Realm;
436+
options.KeyName = keyName3;
437+
})
438+
.AddApiKeyInQueryParams<FakeApiKeyProvider>("InQueryParams", options =>
439+
{
440+
options.Realm = TestServerBuilder.Realm;
441+
options.KeyName = keyName4;
442+
});
443+
444+
#if !(NET461 || NETSTANDARD2_0 || NETCOREAPP2_1)
445+
services.Configure<AuthorizationOptions>(options => options.FallbackPolicy = new AuthorizationPolicyBuilder(schemes.ToArray()).RequireAuthenticatedUser().Build());
446+
#endif
447+
});
448+
449+
using var client = server.CreateClient();
450+
451+
using var request1 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[0]);
452+
request1.Headers.Add(keyName1, FakeApiKeys.FakeKey);
453+
using var response1 = await client.SendAsync(request1);
454+
Assert.True(response1.IsSuccessStatusCode);
455+
Assert.Equal(HttpStatusCode.OK, response1.StatusCode);
456+
var response1Principal = await DeserializeClaimsPrincipalAsync(response1);
457+
Assert.Contains(response1.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme"));
458+
Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value);
459+
Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value);
460+
Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value);
461+
462+
463+
using var request2 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[1]);
464+
request2.Headers.Add(keyName2, FakeApiKeys.FakeKey);
465+
using var response2 = await client.SendAsync(request2);
466+
Assert.True(response2.IsSuccessStatusCode);
467+
Assert.Equal(HttpStatusCode.OK, response2.StatusCode);
468+
var response2Principal = await DeserializeClaimsPrincipalAsync(response2);
469+
Assert.DoesNotContain(response2.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme"));
470+
Assert.Contains(response2Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value);
471+
Assert.DoesNotContain(response2Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value);
472+
Assert.DoesNotContain(response2Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value);
473+
474+
475+
using var request3 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[2]);
476+
request3.Headers.Authorization = new AuthenticationHeaderValue(keyName3, FakeApiKeys.FakeKey);
477+
using var response3 = await client.SendAsync(request3);
478+
Assert.True(response3.IsSuccessStatusCode);
479+
Assert.Equal(HttpStatusCode.OK, response3.StatusCode);
480+
var response3Principal = await DeserializeClaimsPrincipalAsync(response3);
481+
Assert.DoesNotContain(response3.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme"));
482+
Assert.DoesNotContain(response3Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value);
483+
Assert.Contains(response3Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value);
484+
Assert.DoesNotContain(response3Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value);
485+
486+
487+
using var request4 = new HttpRequestMessage(HttpMethod.Get, $"{TestServerBuilder.ClaimsPrincipalUrl}?scheme={schemes[3]}&{keyName4}={FakeApiKeys.FakeKey}");
488+
using var response4 = await client.SendAsync(request4);
489+
Assert.True(response4.IsSuccessStatusCode);
490+
Assert.Equal(HttpStatusCode.OK, response3.StatusCode);
491+
var response4Principal = await DeserializeClaimsPrincipalAsync(response4);
492+
Assert.DoesNotContain(response4.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme"));
493+
Assert.DoesNotContain(response4Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value);
494+
Assert.DoesNotContain(response4Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value);
495+
Assert.Contains(response4Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value);
496+
}
497+
498+
#endregion // Multi-Scheme
499+
500+
private async Task<ClaimsPrincipalDto> DeserializeClaimsPrincipalAsync(HttpResponseMessage response)
395501
{
396502
return JsonSerializer.Deserialize<ClaimsPrincipalDto>(await response.Content.ReadAsStringAsync());
397503
}
504+
505+
private class FakeApiKeyProviderLocal_1 : IApiKeyProvider
506+
{
507+
public Task<IApiKey> ProvideAsync(string key)
508+
{
509+
return Task.FromResult((IApiKey)new FakeApiKey(key, "Test", new List<Claim> { new Claim("Provider", "1") }));
510+
}
511+
}
512+
513+
private class FakeApiKeyProviderLocal_2 : IApiKeyProvider
514+
{
515+
public Task<IApiKey> ProvideAsync(string key)
516+
{
517+
return Task.FromResult((IApiKey)new FakeApiKey(key, "Test", new List<Claim> { new Claim("Provider", "2") }));
518+
}
519+
}
398520
}
399521
}

test/AspNetCore.Authentication.ApiKey.Tests/AspNetCore.Authentication.ApiKey.Tests.csproj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
17+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
1818
<PackageReference Include="xunit" Version="2.4.1" />
1919
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
2020
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2121
<PrivateAssets>all</PrivateAssets>
2222
</PackageReference>
23-
<PackageReference Include="coverlet.collector" Version="3.0.2">
23+
<PackageReference Include="coverlet.collector" Version="3.0.3">
2424
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2525
<PrivateAssets>all</PrivateAssets>
2626
</PackageReference>
2727
</ItemGroup>
2828

2929
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
30-
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.2" />
30+
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.3" />
3131
</ItemGroup>
3232

3333
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
@@ -39,11 +39,15 @@
3939
</ItemGroup>
4040

4141
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
42+
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.3" />
43+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
4244
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
4345
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
4446
</ItemGroup>
4547

4648
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
49+
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.3" />
50+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
4751
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
4852
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
4953
</ItemGroup>

test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/TestServerBuilder.cs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
// Licensed under the MIT License. See LICENSE file in the project root for license information.
33

44
using Microsoft.AspNetCore.Authentication;
5+
using Microsoft.AspNetCore.Authorization;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Hosting;
78
using Microsoft.AspNetCore.Http;
9+
using Microsoft.AspNetCore.Mvc;
10+
using Microsoft.AspNetCore.Mvc.Authorization;
811
using Microsoft.AspNetCore.TestHost;
912
using Microsoft.Extensions.DependencyInjection;
13+
using Microsoft.Extensions.Primitives;
1014
using System;
1115
using System.Linq;
1216
using System.Text.Json;
@@ -122,7 +126,7 @@ internal static TestServer BuildTestServer(Action<IServiceCollection> configureS
122126

123127
#if !(NET461 || NETSTANDARD2_0 || NETCOREAPP2_1)
124128
services.AddRouting();
125-
services.AddAuthorization();
129+
services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
126130
#endif
127131

128132
configureServices(services);
@@ -150,18 +154,18 @@ internal static TestServer BuildTestServer(Action<IServiceCollection> configureS
150154
endpoints.MapGet("/", async context =>
151155
{
152156
await context.Response.WriteAsync("Hello World!");
153-
}).RequireAuthorization();
157+
});
154158

155159
endpoints.MapGet("/claims-principal", async context =>
156160
{
157161
context.Response.ContentType = "application/json";
158162
await context.Response.WriteAsync(JsonSerializer.Serialize(new ClaimsPrincipalDto(context.User)));
159-
}).RequireAuthorization();
163+
});
160164

161165
endpoints.MapGet("/forbidden", async context =>
162166
{
163167
await context.ForbidAsync();
164-
}).RequireAuthorization();
168+
});
165169

166170
endpoints.MapGet("/anonymous", async context =>
167171
{
@@ -184,10 +188,31 @@ internal static TestServer BuildTestServer(Action<IServiceCollection> configureS
184188
{
185189
if (!context.User.Identity.IsAuthenticated)
186190
{
187-
await context.ChallengeAsync();
188-
return;
191+
var scheme = StringValues.Empty;
192+
context.Request.Query.TryGetValue("scheme", out scheme);
193+
194+
if (scheme.Any())
195+
{
196+
var result = await context.AuthenticateAsync(scheme);
197+
if (result?.Principal != null)
198+
{
199+
context.User = result.Principal;
200+
}
201+
else
202+
{
203+
await context.ChallengeAsync(scheme);
204+
return;
205+
}
206+
}
207+
else
208+
{
209+
await context.ChallengeAsync();
210+
return;
211+
}
189212
}
190213

214+
215+
191216
if (context.Request.Path == "/claims-principal")
192217
{
193218
context.Response.ContentType = "application/json";

0 commit comments

Comments
 (0)