Every now and then I want to do a simple authentication scheme in an ASP.NET web application, without calling on various providers or other components that bring too much weight & functionality to the table. Implementing a custom IIdentity and IPrincipal is a straightforward way of achieving this.
IIdentity
IPrincipal
The steps involved:
HttpContext.Current
Implementing the interfaces
A simple skeleton for the IIdentity implementation:
public class MyIdentity : IIdentity { private string passedInName; // IIdentity public string AuthenticationType { get { return "MyIdentityAuthenticationType"; } } public string Name { get { return passedInName; } } public bool IsAuthenticated { get { // TODO: add whatever logic we need here (for instance, AD group membership) throw new NotImplementedException(); } } // /IIdentity // Hide constructor private MyIdentity() { } // Static factory method internal static IIdentity CreateIdentity(IUserNameProvider provider) { IIdentity identity = new MyIdentity { passedInName = provider.AccountName } ; return identity; } }
And for the IPrincipal-implementing class:
public class MyPrincipal : IPrincipal { private IIdentity identity; // IPrincipal public IIdentity Identity { get { return identity; } } public bool IsInRole(string role) { // TODO add logic here throw new NotImplementedException(); } // hide Ctor private MyPrincipal() { } // static factory method public static IPrincipal CreatePrincipal(IUserNameProvider provider) { IIdentity identity = MyIdentity.CreateIdentity(provider); IPrincipal principal = new MyPrincipal { identity = identity }; return principal; } }
A few things to note:
IUserNameProvider
IsAuthenticated
IsInRole
Hooking them up
The easiest way I've found to link the custom principal is by implementing the Application_AcquireRequestState event in the global.asax:
Application_AcquireRequestState
protected void Application_AcquireRequestState(object sender, EventArgs e) { IPrincipal principal = null; var current = HttpContext.Current; // check if session is available; needed because this event can/does fire multiple times per request if (current.Session != null) { // check if we have the principal in session if (current.Session["magicstring"] != null) { principal = current.Session["magicstring"] as IPrincipal; } // catches both (1) key not in session and (2) cast not succesfull if (principal == null) { // create a new one and store it in the session principal = MyPrincipal.CreatePrincipal(new MyWebAccountNameProvider()); current.Session["magicstring"] = principal; } // regardless of source (session or freshly created, store in appropriate context) current.User = principal; } }
Actually, this event is a bit weird, because it can fire multiple times for a single request. The Session reference is only valid once however, which is why that check is there. So, what happens:
Wrapup and comments
This is by no means a do-all, end-all solution, but it works well. Depending on the tiering/layering of the solution, you can either check the HttpContext.Current.User property, or you can abstract that into a simple wrapper. I usually either use a static method on my principal that returns the actual active principal, or set up a context registry to hold my principal reference transparently across layers.
HttpContext.Current.User