Skip to content

Commit 0e7436f

Browse files
author
Mihir Dilip
committed
Add project files.
1 parent 4294279 commit 0e7436f

File tree

8 files changed

+178
-0
lines changed

8 files changed

+178
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.27428.2011
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mihir.AspNetCore.Authentication.Basic", "Mihir.AspNetCore.Authentication.Basic\Mihir.AspNetCore.Authentication.Basic.csproj", "{F8FEFB06-9F93-4F50-8530-80F5C0A677FC}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{F8FEFB06-9F93-4F50-8530-80F5C0A677FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{F8FEFB06-9F93-4F50-8530-80F5C0A677FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{F8FEFB06-9F93-4F50-8530-80F5C0A677FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{F8FEFB06-9F93-4F50-8530-80F5C0A677FC}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {70815049-1680-480A-BF5A-00536D6C9C20}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Mihir.AspNetCore.Authentication.Basic
2+
{
3+
public static class BasicDefaults
4+
{
5+
public const string AuthenticationScheme = "Basic";
6+
public const string DisplayName = "Basic";
7+
public const string DefaultRealm = "Stirling";
8+
}
9+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.AspNetCore.Authentication;
2+
using System;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Options;
5+
6+
namespace Mihir.AspNetCore.Authentication.Basic
7+
{
8+
public static class BasicExtensions
9+
{
10+
public static AuthenticationBuilder AddBasic<TBasicUserValidationService>(this AuthenticationBuilder builder, Action<BasicOptions> configureOptions)
11+
where TBasicUserValidationService : class, IBasicUserValidationService
12+
{
13+
builder.Services.AddSingleton<IPostConfigureOptions<BasicOptions>, BasicPostConfigureOptions>();
14+
builder.Services.AddTransient<IBasicUserValidationService, TBasicUserValidationService>();
15+
16+
return builder.AddScheme<BasicOptions, BasicHandler>(BasicDefaults.AuthenticationScheme, configureOptions);
17+
}
18+
}
19+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Net.Http.Headers;
3+
using System.Security.Claims;
4+
using System.Text;
5+
using System.Text.Encodings.Web;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Authentication;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Options;
10+
using Microsoft.Net.Http.Headers;
11+
12+
namespace Mihir.AspNetCore.Authentication.Basic
13+
{
14+
public class BasicHandler : AuthenticationHandler<BasicOptions>
15+
{
16+
private readonly IBasicUserValidationService _basicUserValidationService;
17+
18+
public BasicHandler(IOptionsMonitor<BasicOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IBasicUserValidationService basicUserValidationService)
19+
: base(options, logger, encoder, clock)
20+
{
21+
_basicUserValidationService = basicUserValidationService;
22+
}
23+
24+
//protected new BasicEvents Events { get => (BasicEvents)base.Events; set => base.Events = value; }
25+
//protected override Task<object> CreateEventsAsync() => Task.FromResult<object>(new BasicEvents());
26+
27+
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
28+
{
29+
if (!Request.Headers.ContainsKey(HeaderNames.Authorization))
30+
{
31+
return AuthenticateResult.NoResult();
32+
}
33+
34+
if (!AuthenticationHeaderValue.TryParse(Request.Headers[HeaderNames.Authorization], out var headerValue))
35+
{
36+
return AuthenticateResult.NoResult();
37+
}
38+
39+
if (!headerValue.Scheme.Equals(BasicDefaults.AuthenticationScheme, StringComparison.OrdinalIgnoreCase))
40+
{
41+
return AuthenticateResult.NoResult();
42+
}
43+
44+
var usernameAndPassword = Encoding.UTF8.GetString(Convert.FromBase64String(headerValue.Parameter));
45+
var usernameAndPasswordSplit = usernameAndPassword.Split(':');
46+
if (usernameAndPasswordSplit.Length != 2)
47+
{
48+
return AuthenticateResult.Fail("Invalid Basic authentication header");
49+
}
50+
var username = usernameAndPasswordSplit[0];
51+
var password = usernameAndPasswordSplit[1];
52+
53+
var isValidUser = await _basicUserValidationService.IsValidAsync(username, password);
54+
if (!isValidUser)
55+
{
56+
return AuthenticateResult.Fail("Invalid username or password");
57+
}
58+
59+
var claims = new[] { new Claim(ClaimTypes.Name, username) };
60+
var identity = new ClaimsIdentity(claims, Scheme.Name);
61+
var principal = new ClaimsPrincipal(identity);
62+
var ticket = new AuthenticationTicket(principal, Scheme.Name);
63+
return AuthenticateResult.Success(ticket);
64+
}
65+
66+
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
67+
{
68+
Response.Headers[HeaderNames.WWWAuthenticate] = Options.Challenge;
69+
await base.HandleChallengeAsync(properties);
70+
}
71+
}
72+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.AspNetCore.Authentication;
2+
3+
namespace Mihir.AspNetCore.Authentication.Basic
4+
{
5+
public class BasicOptions : AuthenticationSchemeOptions
6+
{
7+
public string Realm { get; set; }
8+
9+
public string Challenge => $"{BasicDefaults.AuthenticationScheme} realm=\"{Realm}\", charset=\"UTF-8\"";
10+
11+
//public new BasicEvents Events
12+
//{
13+
// get => (BasicEvents)base.Events;
14+
// set => base.Events = value;
15+
//}
16+
}
17+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using Microsoft.Extensions.Options;
3+
4+
namespace Mihir.AspNetCore.Authentication.Basic
5+
{
6+
class BasicPostConfigureOptions : IPostConfigureOptions<BasicOptions>
7+
{
8+
public void PostConfigure(string name, BasicOptions options)
9+
{
10+
if (string.IsNullOrWhiteSpace(options.Realm))
11+
{
12+
throw new InvalidOperationException("Realm must be set in basic options");
13+
}
14+
}
15+
}
16+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System.Threading.Tasks;
2+
3+
namespace Mihir.AspNetCore.Authentication.Basic
4+
{
5+
public interface IBasicUserValidationService
6+
{
7+
Task<bool> IsValidAsync(string username, string password);
8+
}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.0.3" />
9+
</ItemGroup>
10+
11+
</Project>

0 commit comments

Comments
 (0)