Skip to content

Commit 5481f27

Browse files
committed
Microsoft style events implemented.
1 parent 58f13c0 commit 5481f27

21 files changed

+780
-257
lines changed

README.md

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ PM> Install-Package AspNetCore.Authentication.Basic
1818

1919
## Example Usage
2020

21+
Samples are available under [samples directory](samples).
22+
2123
Setting it up is quite simple. You will need basic working knowledge of ASP.NET Core 2.2 or newer to get started using this code.
2224

2325
On [**Startup.cs**](#startupcs), as shown below, add 2 lines in *ConfigureServices* method `services.AddAuthentication(BasicDefaults.AuthenticationScheme).AddBasic<BasicUserValidationService>(options => { options.Realm = "My App"; });`. And a line `app.UseAuthentication();` in *Configure* method.
@@ -35,10 +37,17 @@ public class Startup
3537
public void ConfigureServices(IServiceCollection services)
3638
{
3739
// Add the Basic scheme authentication here..
38-
// AddBasic extension takes an implementation of IBasicUserValidationService for validating the username and password.
39-
// It also requires Realm to be set in the options.
40-
services.AddAuthentication(BasicDefaults.AuthenticationScheme)
41-
.AddBasic<BasicUserValidationService>(options => { options.Realm = "My App"; });
40+
// It requires Realm to be set in the options if SuppressWWWAuthenticateHeader is not set.
41+
// If an implementation of IBasicUserValidationService interface is registered in the dependency register as well as OnValidateCredentials delegete on options.Events is also set then this delegate will be used instead of an implementation of IBasicUserValidationService.
42+
services.AddAuthentication(BasicDefaults.AuthenticationScheme)
43+
44+
// The below AddBasic without type parameter will require OnValidateCredentials delegete on options.Events to be set unless an implementation of IBasicUserValidationService interface is registered in the dependency register.
45+
// Please note if both the delgate and validation server are set then the delegate will be used instead of BasicUserValidationService.
46+
//.AddBasic(options => { options.Realm = "My App"; });
47+
48+
// The below AddBasic with type parameter will add the BasicUserValidationService to the dependency register.
49+
// Please note if OnValidateCredentials delegete on options.Events is also set then this delegate will be used instead of BasicUserValidationService.
50+
.AddBasic<BasicUserValidationService>(options => { options.Realm = "My App"; });
4251

4352
services.AddControllers();
4453

@@ -77,10 +86,17 @@ public class Startup
7786
public void ConfigureServices(IServiceCollection services)
7887
{
7988
// Add the Basic scheme authentication here..
80-
// AddBasic extension takes an implementation of IBasicUserValidationService for validating the username and password.
81-
// It also requires Realm to be set in the options.
82-
services.AddAuthentication(BasicDefaults.AuthenticationScheme)
83-
.AddBasic<BasicUserValidationService>(options => { options.Realm = "My App"; });
89+
// It requires Realm to be set in the options if SuppressWWWAuthenticateHeader is not set.
90+
// If an implementation of IBasicUserValidationService interface is registered in the dependency register as well as OnValidateCredentials delegete on options.Events is also set then this delegate will be used instead of an implementation of IBasicUserValidationService.
91+
services.AddAuthentication(BasicDefaults.AuthenticationScheme)
92+
93+
// The below AddBasic without type parameter will require OnValidateCredentials delegete on options.Events to be set unless an implementation of IBasicUserValidationService interface is registered in the dependency register.
94+
// Please note if both the delgate and validation server are set then the delegate will be used instead of BasicUserValidationService.
95+
//.AddBasic(options => { options.Realm = "My App"; });
96+
97+
// The below AddBasic with type parameter will add the BasicUserValidationService to the dependency register.
98+
// Please note if OnValidateCredentials delegete on options.Events is also set then this delegate will be used instead of BasicUserValidationService.
99+
.AddBasic<BasicUserValidationService>(options => { options.Realm = "My App"; });
84100

85101
services.AddMvc();
86102

@@ -163,7 +179,6 @@ app.UseEndpoints(endpoints =>
163179
- [RFC 7617: Technical spec for HTTP Basic](https://tools.ietf.org/html/rfc7617)
164180
- [ASP.NET Core Security documentation](https://docs.microsoft.com/en-us/aspnet/core/security)
165181
- [aspnet/Security](https://github.com/dotnet/aspnetcore/tree/master/src/Security)
166-
- [Creating an authentication scheme in ASP.NET Core 2.0](https://joonasw.net/view/creating-auth-scheme-in-aspnet-core-2)
167182

168183
## License
169-
[MIT License](https://github.com/mihirdilip/aspnetcore-authentication-basic/blob/master/LICENSE.txt)
184+
[MIT License](https://github.com/mihirdilip/aspnetcore-authentication-basic/blob/master/LICENSE.txt)
Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.AspNetCore.Mvc;
22
using System.Collections.Generic;
3+
using System.Text;
34

45
namespace SampleWebApi_2_2.Controllers
56
{
@@ -13,29 +14,21 @@ public IEnumerable<string> Get()
1314
return new string[] { "value1", "value2" };
1415
}
1516

16-
// GET api/values/5
17-
[HttpGet("{id}")]
18-
public string Get(int id)
19-
{
20-
return "value";
21-
}
22-
23-
// POST api/values
24-
[HttpPost]
25-
public void Post([FromBody] string value)
26-
{
27-
}
28-
29-
// PUT api/values/5
30-
[HttpPut("{id}")]
31-
public void Put(int id, [FromBody] string value)
32-
{
33-
}
17+
[HttpGet("claims")]
18+
public string Claims()
19+
{
20+
var sb = new StringBuilder();
21+
foreach (var claim in User.Claims)
22+
{
23+
sb.AppendLine($"{claim.Type}: {claim.Value}");
24+
}
25+
return sb.ToString();
26+
}
3427

35-
// DELETE api/values/5
36-
[HttpDelete("{id}")]
37-
public void Delete(int id)
28+
[HttpGet("forbid")]
29+
public new IActionResult Forbid()
3830
{
31+
return base.Forbid();
3932
}
4033
}
4134
}

samples/SampleWebApi_2_0/SampleWebApi_2_0.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</ItemGroup>
1313

1414
<ItemGroup>
15-
<PackageReference Include="AspNetCore.Authentication.Basic" Version="3.1.0-preview.1" />
15+
<PackageReference Include="AspNetCore.Authentication.Basic" Version="3.1.0" />
1616
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.9" />
1717
</ItemGroup>
1818

@@ -21,5 +21,9 @@
2121
</ItemGroup>
2222

2323
<Import Project="..\SampleWebApi.Shared\SampleWebApi.Shared.projitems" Label="Shared" />
24+
25+
<!--<ItemGroup>
26+
<ProjectReference Include="..\..\src\AspNetCore.Authentication.Basic\AspNetCore.Authentication.Basic.csproj" />
27+
</ItemGroup>-->
2428

2529
</Project>

samples/SampleWebApi_2_0/Startup.cs

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
using Microsoft.AspNetCore.Authorization;
1+
using AspNetCore.Authentication.Basic;
2+
using Microsoft.AspNetCore.Authorization;
23
using Microsoft.AspNetCore.Builder;
34
using Microsoft.AspNetCore.Hosting;
5+
using Microsoft.AspNetCore.Http;
46
using Microsoft.AspNetCore.Mvc.Authorization;
57
using Microsoft.AspNetCore.Rewrite;
68
using Microsoft.Extensions.Configuration;
79
using Microsoft.Extensions.DependencyInjection;
8-
using AspNetCore.Authentication.Basic;
910
using SampleWebApi.Repositories;
1011
using SampleWebApi.Services;
12+
using System.Collections.Generic;
13+
using System.Security.Claims;
14+
using System.Threading.Tasks;
1115

1216
namespace SampleWebApi
1317
{
14-
public class Startup
18+
public class Startup
1519
{
1620
public Startup(IConfiguration configuration)
1721
{
@@ -26,13 +30,124 @@ public void ConfigureServices(IServiceCollection services)
2630
// Add User repository to the dependency container.
2731
services.AddTransient<IUserRepository, InMemoryUserRepository>();
2832

29-
// Add the Basic scheme authentication here..
30-
// AddBasic extension takes an implementation of IBasicUserValidationService for validating the username and password.
31-
// It also requires Realm to be set in the options.
32-
services.AddAuthentication(BasicDefaults.AuthenticationScheme)
33-
.AddBasic<BasicUserValidationService>(options => { options.Realm = "Sample Web API"; });
33+
// Add the Basic scheme authentication here..
34+
// It requires Realm to be set in the options if SuppressWWWAuthenticateHeader is not set.
35+
// If an implementation of IBasicUserValidationService interface is registered in the dependency register as well as OnValidateCredentials delegete on options.Events is also set then this delegate will be used instead of an implementation of IBasicUserValidationService.
36+
services.AddAuthentication(BasicDefaults.AuthenticationScheme)
3437

35-
services.AddMvc(options =>
38+
// The below AddBasic without type parameter will require OnValidateCredentials delegete on options.Events to be set unless an implementation of IBasicUserValidationService interface is registered in the dependency register.
39+
// Please note if both the delgate and validation server are set then the delegate will be used instead of BasicUserValidationService.
40+
//.AddBasic(options =>
41+
42+
// The below AddBasic with type parameter will add the BasicUserValidationService to the dependency register.
43+
// Please note if OnValidateCredentials delegete on options.Events is also set then this delegate will be used instead of BasicUserValidationService.
44+
.AddBasic<BasicUserValidationService>(options =>
45+
{
46+
options.Realm = "Sample Web API";
47+
48+
//// Optional option to suppress the browser login dialog for ajax calls.
49+
//options.SuppressWWWAuthenticateHeader = true;
50+
51+
//// Optional events to override the basic original logic with custom logic.
52+
//// Only use this if you know what you are doing at your own risk. Any of the events can be assigned.
53+
options.Events = new BasicEvents
54+
{
55+
56+
//// A delegate assigned to this property will be invoked just before validating credentials.
57+
//OnValidateCredentials = async (context) =>
58+
//{
59+
// // custom code to handle credentials, create principal and call Success method on context.
60+
// var userRepository = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
61+
// var user = await userRepository.GetUserByUsername(context.Username);
62+
// var isValid = user != null && user.Password == context.Password;
63+
// if (isValid)
64+
// {
65+
// context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateCredentials");
66+
// var claims = new[]
67+
// {
68+
// new Claim(ClaimTypes.NameIdentifier, context.Username, ClaimValueTypes.String, context.Options.ClaimsIssuer),
69+
// new Claim(ClaimTypes.Name, context.Username, ClaimValueTypes.String, context.Options.ClaimsIssuer),
70+
// new Claim("CustomClaimType", "Custom Claim Value - from OnValidateCredentials")
71+
// };
72+
// context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
73+
// context.Success();
74+
// }
75+
// else
76+
// {
77+
// context.NoResult();
78+
// }
79+
//},
80+
81+
//// A delegate assigned to this property will be invoked just before validating credentials.
82+
//// NOTE: Same as above delegate but slightly different implementation which will give same result.
83+
//OnValidateCredentials = async (context) =>
84+
//{
85+
// // custom code to handle credentials, create principal and call Success method on context.
86+
// var userRepository = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
87+
// var user = await userRepository.GetUserByUsername(context.Username);
88+
// var isValid = user != null && user.Password == context.Password;
89+
// if (isValid)
90+
// {
91+
// context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateCredentials");
92+
// var claims = new[]
93+
// {
94+
// new Claim("CustomClaimType", "Custom Claim Value - from OnValidateCredentials")
95+
// };
96+
// context.ValidationSucceeded(claims); // claims are optional
97+
// }
98+
// else
99+
// {
100+
// context.ValidationFailed();
101+
// }
102+
//},
103+
104+
//// A delegate assigned to this property will be invoked before a challenge is sent back to the caller when handling unauthorized response.
105+
//OnHandleChallenge = async (context) =>
106+
//{
107+
// // custom code to handle authentication challenge unauthorized response.
108+
// context.Response.StatusCode = StatusCodes.Status401Unauthorized;
109+
// context.Response.Headers.Add("ChallengeCustomHeader", "From OnHandleChallenge");
110+
// await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleChallenge\"}");
111+
// context.Handled(); // important! do not forget to call this method at the end.
112+
//},
113+
114+
//// A delegate assigned to this property will be invoked if Authorization fails and results in a Forbidden response.
115+
//OnHandleForbidden = async (context) =>
116+
//{
117+
// // custom code to handle forbidden response.
118+
// context.Response.StatusCode = StatusCodes.Status403Forbidden;
119+
// context.Response.Headers.Add("ForbidCustomHeader", "From OnHandleForbidden");
120+
// await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleForbidden\"}");
121+
// context.Handled(); // important! do not forget to call this method at the end.
122+
//},
123+
124+
//// A delegate assigned to this property will be invoked when the authentication succeeds. It will not be called if OnValidateCredentials delegate is assigned.
125+
//// It can be used for adding claims, headers, etc to the response.
126+
//OnAuthenticationSucceeded = (context) =>
127+
//{
128+
// //custom code to add extra bits to the success response.
129+
// context.Response.Headers.Add("SuccessCustomHeader", "From OnAuthenticationSucceeded");
130+
// var customClaims = new List<Claim>
131+
// {
132+
// new Claim("CustomClaimType", "Custom Claim Value - from OnAuthenticationSucceeded")
133+
// };
134+
// context.AddClaims(customClaims);
135+
// //or can add like this - context.Principal.AddIdentity(new ClaimsIdentity(customClaims));
136+
// return Task.CompletedTask;
137+
//},
138+
139+
//// A delegate assigned to this property will be invoked when the authentication fails.
140+
//OnAuthenticationFailed = (context) =>
141+
//{
142+
// // custom code to handle failed authentication.
143+
// context.Fail("Failed to authenticate");
144+
// return Task.CompletedTask;
145+
//}
146+
147+
};
148+
});
149+
150+
services.AddMvc(options =>
36151
{
37152
// ALWAYS USE HTTPS (SSL) protocol in production when using Basic authentication.
38153
//options.Filters.Add<RequireHttpsAttribute>();
Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
using System;
1+
using Microsoft.AspNetCore.Mvc;
22
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Threading.Tasks;
5-
using Microsoft.AspNetCore.Mvc;
3+
using System.Text;
64

75
namespace SampleWebApi_2_2.Controllers
86
{
9-
[Route("api/[controller]")]
7+
[Route("api/[controller]")]
108
[ApiController]
119
public class ValuesController : ControllerBase
1210
{
@@ -17,29 +15,21 @@ public ActionResult<IEnumerable<string>> Get()
1715
return new string[] { "value1", "value2" };
1816
}
1917

20-
// GET api/values/5
21-
[HttpGet("{id}")]
22-
public ActionResult<string> Get(int id)
18+
[HttpGet("claims")]
19+
public ActionResult<string> Claims()
2320
{
24-
return "value";
21+
var sb = new StringBuilder();
22+
foreach (var claim in User.Claims)
23+
{
24+
sb.AppendLine($"{claim.Type}: {claim.Value}");
25+
}
26+
return sb.ToString();
2527
}
2628

27-
// POST api/values
28-
[HttpPost]
29-
public void Post([FromBody] string value)
30-
{
31-
}
32-
33-
// PUT api/values/5
34-
[HttpPut("{id}")]
35-
public void Put(int id, [FromBody] string value)
36-
{
37-
}
38-
39-
// DELETE api/values/5
40-
[HttpDelete("{id}")]
41-
public void Delete(int id)
29+
[HttpGet("forbid")]
30+
public new IActionResult Forbid()
4231
{
32+
return base.Forbid();
4333
}
4434
}
4535
}

samples/SampleWebApi_2_2/SampleWebApi_2_2.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="AspNetCore.Authentication.Basic" Version="3.1.0-preview.1" />
9+
<PackageReference Include="AspNetCore.Authentication.Basic" Version="3.1.0" />
1010
<PackageReference Include="Microsoft.AspNetCore.App" />
1111
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
1212
</ItemGroup>
13-
13+
1414
<Import Project="..\SampleWebApi.Shared\SampleWebApi.Shared.projitems" Label="Shared" />
1515

16+
<!--<ItemGroup>
17+
<ProjectReference Include="..\..\src\AspNetCore.Authentication.Basic\AspNetCore.Authentication.Basic.csproj" />
18+
</ItemGroup>-->
19+
1620
</Project>

0 commit comments

Comments
 (0)