Skip to content

Instantly share code, notes, and snippets.

@derekforeman
Created March 12, 2020 18:32
Show Gist options
  • Save derekforeman/9469b9a1a100ac4162bf63c9c2df6da8 to your computer and use it in GitHub Desktop.
Save derekforeman/9469b9a1a100ac4162bf63c9c2df6da8 to your computer and use it in GitHub Desktop.
using System;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using BloomAmcatStats.DAL;
namespace Stats.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class BasicAuthenticationFilter : AuthorizationFilterAttribute
{
readonly bool _active = true;
public BasicAuthenticationFilter()
{ }
/// <summary>
/// Overriden constructor to allow explicit disabling of this
/// filter's behavior. Pass false to disable (same as no filter
/// but declarative)
/// </summary>
/// <param name="active"></param>
public BasicAuthenticationFilter(bool active)
{
_active = active;
}
/// <summary>
/// Override to Web API filter method to handle Basic Auth check
/// </summary>
/// <param name="actionContext"></param>
public override void OnAuthorization(HttpActionContext actionContext)
{
if (!_active) return;
var identity = ParseAuthorizationHeader(actionContext);
if (identity == null)
{
Challenge(actionContext);
return;
}
if (!OnAuthorizeUser(identity.Name, identity.Password, actionContext))
{
Challenge(actionContext);
return;
}
var principal = new GenericPrincipal(identity, null);
Thread.CurrentPrincipal = principal;
// inside of ASP.NET this is required
//if (HttpContext.Current != null)
// HttpContext.Current.User = principal;
base.OnAuthorization(actionContext);
}
/// <summary>
/// Base implementation for user authentication - you probably will
/// want to override this method for application specific logic.
///
/// The base implementation merely checks for username and password
/// present and set the Thread principal.
///
/// Override this method if you want to customize Authentication
/// and store user data as needed in a Thread Principle or other
/// Request specific storage.
/// </summary>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="actionContext"></param>
/// <returns></returns>
protected virtual bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
{
var userRepo = new UserRepository();
var userValidated = userRepo.Authenticate(username, password);
return userValidated.LoginResult == 1;
}
/// <summary>
/// Parses the Authorization header and creates user credentials
/// </summary>
/// <param name="actionContext"></param>
protected virtual BasicAuthenticationIdentity ParseAuthorizationHeader(HttpActionContext actionContext)
{
string authHeader = null;
var auth = actionContext.Request.Headers.Authorization;
if (auth != null && auth.Scheme == "Basic")
authHeader = auth.Parameter;
if (string.IsNullOrEmpty(authHeader))
return null;
authHeader = Encoding.Default.GetString(Convert.FromBase64String(authHeader));
char[] splitChar = ":".ToCharArray();
var tokens = authHeader.Split(splitChar, 2);
return tokens.Length < 2 ? null : new BasicAuthenticationIdentity(tokens[0], tokens[1]);
}
/// <summary>
/// Send the Authentication Challenge request
/// </summary>
/// <param name="actionContext"></param>
void Challenge(HttpActionContext actionContext)
{
var host = actionContext.Request.RequestUri.DnsSafeHost;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment