Last active
June 5, 2019 19:28
-
-
Save GeorgDangl/70dca6188e351496145e83f2f76b2385 to your computer and use it in GitHub Desktop.
Asp.Net Core middleware for Asp.Net Identity projects to support Http Basic authentication, see https://blog.dangl.me/archive/http-basic-authentication-in-aspnet-core-projects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Project.Middleware | |
{ | |
public class BasicAuthenticationHeaderValue | |
{ | |
public BasicAuthenticationHeaderValue(string authenticationHeaderValue) | |
{ | |
if (!string.IsNullOrWhiteSpace(authenticationHeaderValue)) | |
{ | |
_authenticationHeaderValue = authenticationHeaderValue; | |
if (TryDecodeHeaderValue()) | |
{ | |
ReadAuthenticationHeaderValue(); | |
} | |
} | |
} | |
private readonly string _authenticationHeaderValue; | |
private string[] _splitDecodedCredentials; | |
public bool IsValidBasicAuthenticationHeaderValue { get; private set; } | |
public string UserIdentifier { get; private set; } | |
public string UserPassword { get; private set; } | |
private bool TryDecodeHeaderValue() | |
{ | |
const int headerSchemeLength = 6; // "Basic ".Length; | |
if (_authenticationHeaderValue.Length <= headerSchemeLength) | |
{ | |
return false; | |
} | |
var encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength); | |
try | |
{ | |
var decodedCredentials = Convert.FromBase64String(encodedCredentials); | |
var plainCredentials = System.Text.Encoding.ASCII.GetString(decodedCredentials); | |
// The username may not include colon per the RFC: | |
// https://tools.ietf.org/html/rfc2617#section-2 | |
var colonIndex = plainCredentials.IndexOf(':'); | |
if (colonIndex < 0) | |
{ | |
return false; | |
} | |
_splitDecodedCredentials = new string[] | |
{ | |
plainCredentials.Substring(0, colonIndex), | |
plainCredentials.Substring(colonIndex + 1) | |
}; | |
return true; | |
} | |
catch (FormatException) | |
{ | |
return false; | |
} | |
} | |
private void ReadAuthenticationHeaderValue() | |
{ | |
IsValidBasicAuthenticationHeaderValue = _splitDecodedCredentials.Length == 2 | |
&& !string.IsNullOrWhiteSpace(_splitDecodedCredentials[0]) | |
&& !string.IsNullOrWhiteSpace(_splitDecodedCredentials[1]); | |
if (IsValidBasicAuthenticationHeaderValue) | |
{ | |
UserIdentifier = _splitDecodedCredentials[0]; | |
UserPassword = _splitDecodedCredentials[1]; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Http; | |
namespace Project.Middleware | |
{ | |
/// <summary> | |
/// Accepts either username or email as user identifier for sign in with Http Basic authentication | |
/// </summary> | |
public class BasicAuthenticationMiddleware | |
{ | |
public BasicAuthenticationMiddleware(RequestDelegate next) | |
{ | |
_next = next; | |
} | |
private readonly RequestDelegate _next; | |
public async Task Invoke(HttpContext context) | |
{ | |
if (!context.User.Identity.IsAuthenticated) | |
{ | |
var basicAuthenticationHeader = GetBasicAuthenticationHeaderValue(context); | |
if (basicAuthenticationHeader.IsValidBasicAuthenticationHeaderValue) | |
{ | |
var authenticationManager = new BasicAuthenticationSignInManager(context, basicAuthenticationHeader); | |
await authenticationManager.TrySignInUser(); | |
} | |
} | |
await _next.Invoke(context); | |
} | |
private BasicAuthenticationHeaderValue GetBasicAuthenticationHeaderValue(HttpContext context) | |
{ | |
var basicAuthenticationHeader = context.Request.Headers["Authorization"] | |
.FirstOrDefault(header => header.StartsWith("Basic", StringComparison.OrdinalIgnoreCase)); | |
var decodedHeader = new BasicAuthenticationHeaderValue(basicAuthenticationHeader); | |
return decodedHeader; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.Extensions.DependencyInjection; | |
namespace Project.Middleware | |
{ | |
public class BasicAuthenticationSignInManager | |
{ | |
public BasicAuthenticationSignInManager(HttpContext context, BasicAuthenticationHeaderValue authenticationHeaderValue) | |
{ | |
_context = context; | |
_authenticationHeaderValue = authenticationHeaderValue; | |
_userManager = _context.RequestServices.GetRequiredService<UserManager<IdentityUser>>(); | |
_signInManager = _context.RequestServices.GetRequiredService<SignInManager<IdentityUser>>(); | |
} | |
private readonly HttpContext _context; | |
private readonly BasicAuthenticationHeaderValue _authenticationHeaderValue; | |
private readonly UserManager<IdentityUser> _userManager; | |
private readonly SignInManager<IdentityUser> _signInManager; | |
private IdentityUser _user; | |
public async Task TrySignInUser() | |
{ | |
if (_authenticationHeaderValue.IsValidBasicAuthenticationHeaderValue) | |
{ | |
await GetUserByUsernameOrEmail(); | |
if (_user != null) | |
{ | |
await SignInUserIfPasswordIsCorrect(); | |
} | |
} | |
} | |
private async Task GetUserByUsernameOrEmail() | |
{ | |
_user = await _userManager.FindByEmailAsync(_authenticationHeaderValue.UserIdentifier) | |
?? await _userManager.FindByNameAsync(_authenticationHeaderValue.UserIdentifier); | |
} | |
private async Task SignInUserIfPasswordIsCorrect() | |
{ | |
if (await _userManager.CheckPasswordAsync(_user, _authenticationHeaderValue.UserPassword)) | |
{ | |
_context.User = await _signInManager.CreateUserPrincipalAsync(_user); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Builder; | |
namespace Project.Middleware | |
{ | |
public static class MiddlewareExtensions | |
{ | |
public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilder builder) | |
{ | |
return builder.UseMiddleware<BasicAuthenticationMiddleware>(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |
{ | |
app.UseIdentity(); | |
// Add middleware after Identity but before Mvc | |
app.UseBasicAuthentication(); | |
app.UseMvc(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment