Optionally provide private feedback to help us improve this article...

Thank you for your feedback!


Understanding The User Identity Provider Model

With InstantForum 2016 we've introduced a new extensible approach to user storage and user authentication. This new approach uses the provider model design pattern to allow developers to easily extend or customize how core user information is stored within InstantForum and how user authentication is persisted on the client.

We've introduced this approach in order to support the newer authentication options available within .NET such as the ASP.NET Identity / OWIN options whilst continuing to ensure we support existing customers using standard ASP.NET forms authentication.

Currently we offer two providers with our InstantForum 2015-1 release to demonstrate both forms authentication and ASP.NET identity authentication. By default InstantForum still uses the regular forms authentication provider. If you have applications that uses ASP.NET Identity you can choose our OWIN provider via the web.config. See below for further information.

Before we dig into the code you can find all of the classes shown below within the InstantASP.InstantForum project provided with our InstantForum Developer License. These classes can be found within the Providers/UserIdentity folder.

UserIdentityProvider Abstract Base Class

Your custom user identity provider will always need to inherit from this base class and will need to override the various abstract methods provided within the UserIdentityProvider class. The UserIdentityProvider class and abstract methods are shown below....

using System;
using InstantASP.Common.Security;
using InstantASP.Common.ComponentModel;

namespace InstantASP.InstantForum.Providers.UserIdentity
{
public abstract class UserIdentityProvider : Common.Providers.ProviderBase
{

#region "Private Static Variables"

private static Type _providerType;

#endregion

#region "Interface Implementation"

public abstract int InsertUpdateUser(Components.User user, string connString = "");
public abstract Components.User SelectUser(string identity, Common.Enumerations.EnumLoginUsing loginUsing);
public abstract Components.User SelectUser(int UserID);
public abstract bool DeleteUser(int UserID);

public abstract Components.User GetAuthenticatedUser(Common.Enumerations.EnumLoginUsing loginUsing = Common.Enumerations.EnumLoginUsing.EmailAddress, bool deleteAuthCookieIfUserNotFound = true);
public abstract int ValidateUser(string username, string password, Common.Enumerations.EnumLoginUsing loginUsing);
public abstract void SignIn(Components.User user, bool isPersistent = false, Common.Enumerations.EnumLoginUsing loginUsing = Common.Enumerations.EnumLoginUsing.EmailAddress);
public abstract void SignOut(bool deleteAllCookies = false);

#endregion

#region "Instance"

public static UserIdentityProvider Instance()
{

if (_providerType == null)
_providerType = Common.Providers.ProviderConfiguration.GetProviderTypeForService("userIdentity");

if (_providerType != null)
return (UserIdentityProvider)ComponentFactory.GetComponent(_providerType);

return new InstantASPUserIdentityProvider();

}

#endregion

}

}

Concrete Implementation For Forms Authentication (InstantASPUserIdentityProvider )

The code below shows the default provider used by InstantForum. This uses standard ASP.NET forms authentication to persist user authentication and the standard InstantForum database tables to store core user data. The important methods to review here are the SignIn, SignOut and GetAuthenticatedUser merhotds.

using System.Collections.Specialized;

namespace InstantASP.InstantForum.Providers.UserIdentity
{
public class InstantASPUserIdentityProvider : UserIdentityProvider
{

#region "Private Variables"

private string _name;
private string _description;

#endregion

#region "IUserIdentity Implementation"

public override int InsertUpdateUser(Components.User user, string connString = "")
{
return Business.User.InsertUpdateUser(user, connString);
}

public override Components.User SelectUser(string identity, Common.Enumerations.EnumLoginUsing loginUsing)
{
Components.User user = (Components.User)Common.Cache.DataCache.Item(identity, Enumerations.EnumCachedIdentifiers.InstantForumUser.ToString());
if (user == null)
{
user = new Components.User(identity);
Common.Cache.DataCache.Add(identity, user, InstantASP.Common.Cache.Durations.Instance().InstantASP_User(), Enumerations.EnumCachedIdentifiers.InstantForumUser.ToString());
}
return user;
}

public override Components.User SelectUser(int UserID)
{
return new Components.User(UserID);
}

public override bool DeleteUser(int UserID)
{
Business.User.DeleteUser(UserID);
return true;
}

public override int ValidateUser(
string username,
string password,
Common.Enumerations.EnumLoginUsing loginUsing)
{
return Common.Business.UserRepository.ValidateUser(username, password, loginUsing);
}

public override Components.User GetAuthenticatedUser(Common.Enumerations.EnumLoginUsing loginUsing = Common.Enumerations.EnumLoginUsing.EmailAddress, bool deleteAuthCookieIfUserNotFound = true)
{

System.Security.Principal.IIdentity identity =
System.Web.HttpContext.Current.User.Identity;

if ((identity != null) && (identity.IsAuthenticated))
{

Components.User User = SelectUser(identity.Name, loginUsing);

// check to ensure we retrieved user from successfully
if (((User != null) & User.UserID > 0))
{
return User;
}
else
{
// if we are authenticated but don't find the user the user may have been
// deleted from the database, if so delete forms authentication cookie
if (identity.Name != null && deleteAuthCookieIfUserNotFound)
{
// ensure this is not a oAuth ticket - we allow users to authenticate
// view oAuth even without an account during the sign-up process
if (identity.Name.IndexOf("http://") == -1 && identity.Name.IndexOf("https://") == -1)
Common.Security.FormsAuth.DeleteTicket(true);
}
return null;
}

}

return null;

}

public override void SignIn(Components.User user, bool isPersistent = false, Common.Enumerations.EnumLoginUsing loginUsing = Common.Enumerations.EnumLoginUsing.EmailAddress)
{
user.Authenticate(isPersistent, loginUsing);
}

public override void SignOut(bool deleteAllCookies = false)
{
// log out the current user
InstantASP.Common.Security.FormsAuth.DeleteTicket();

// clear current session
if (System.Web.HttpContext.Current != null)
{
if ((System.Web.HttpContext.Current.Session != null))
{
System.Web.HttpContext.Current.Session.Abandon();
}
}

}

#endregion

#region "Provider Implementation"

public override void Initialize(string name, string desc, NameValueCollection configValue)
{
this._name = name;
}

public override string Name
{
get { return _name; }
}

public override string Description
{
get { return _description; }
}

#endregion

}
}

Concrete Implementation For ASP.NET Identity / OWIN (OwinUserIdentityProvider)

The implementation below shows how you could developer a provider to support ASP.NET Identity / OWIN authentication within InstantForum. This could be helpful if your existing web site uses OWIN and you wish to provide a single sign on experience with InstantForum. The important methods to review here are the SignIn, SignOut and GetAuthenticatedUser merhotds.

using System.Collections.Specialized;

namespace InstantASP.InstantForum.Providers.UserIdentity
{
public class OwinUserIdentityProvider : UserIdentityProvider
{

#region "Private Variables"

private string _name;
private string _description;

#endregion

#region "Concrete Methods"

public override int InsertUpdateUser(Components.User user, string connString = "")
{
return Business.User.InsertUpdateUser(user, connString);
}

public override Components.User SelectUser(string identity, Common.Enumerations.EnumLoginUsing loginUsing)
{
Components.User user = (Components.User)Common.Cache.DataCache.Item(identity, Enumerations.EnumCachedIdentifiers.InstantForumUser.ToString());
if (user == null)
{
user = new Components.User(identity);
Common.Cache.DataCache.Add(identity, user, InstantASP.Common.Cache.Durations.Instance().InstantASP_UsersReputation(), Enumerations.EnumCachedIdentifiers.InstantForumUser.ToString());
}
return user;
}

public override Components.User SelectUser(int UserID)
{
return new Components.User(UserID);
}

public override bool DeleteUser(int UserID)
{
Business.User.DeleteUser(UserID);
return true;
}

public override int ValidateUser(
string username,
string password,
Common.Enumerations.EnumLoginUsing loginUsing)
{
return Common.Business.UserRepository.ValidateUser(username, password, loginUsing);
}

public override Components.User GetAuthenticatedUser(Common.Enumerations.EnumLoginUsing loginUsing = Common.Enumerations.EnumLoginUsing.EmailAddress, bool deleteAuthCookieIfUserNotFound = true)
{


string identity = Common.Security.OwinAuth.GetIdentity(loginUsing);

if (!string.IsNullOrEmpty(identity))
{

Components.User User = new Components.User(identity);
if (((User != null) & User.UserID > 0))
{
return User;
}
else
{
// if we are authenticated but don't find the user the user may have been
// deleted from the database, if so delete authentication cookie
if (!string.IsNullOrEmpty(identity) && deleteAuthCookieIfUserNotFound)
SignOut();
}

}

return null;

}

public override void SignIn(Components.User user, bool isPersistent = false, Common.Enumerations.EnumLoginUsing loginUsing = Common.Enumerations.EnumLoginUsing.EmailAddress)
{
Common.Security.OwinAuth.SignIn(user, isPersistent);
}

public override void SignOut(bool deleteAllCookies = false)
{

Common.Security.OwinAuth.SignOut();
}

#endregion

#region "Provider Implementation (No need to change)"

public override void Initialize(string name, string desc, NameValueCollection configValue)
{
this._name = name;
}

public override string Name
{
get { return _name; }
}

public override string Description
{
get { return _description; }
}

#endregion

}
}

Choosing Which Identity Provider To Use (Forms Authentication or OWIN)

You can then swap out which implementation to use via our web.config like so...

<InstantASP.Common>
<!-- providers allow developers to swap or extend functionality -->
<providers>
<userIdentity defaultProvider="InstantASPIdentityProvider">
<providers>
<add name="InstantASPIdentityProvider" type="InstantASP.InstantForum.Providers.UserIdentity.InstantASPUserIdentityProvider, InstantASP.Common" />
<add name="OwinIdentityProvider" type="InstantASP.InstantForum.Providers.UserIdentity.OwinUserIdentityProvider, InstantASP.Common" />
</providers>
</userIdentity>

..........

This lets you add your own class to handle user store and authentication within InstantForum without having to modify our code.For example you could create a new concrete class called MySiteWeb.UserIdentity that inherits from our abstract UserIdentityProvider base class and register this in the web.config like so...

<userIdentity defaultProvider="MyWebSiteIdentityProvider">
<providers>
<add name="MyWebSiteIdentityProvider" type="MyWebSite.UserIdentityProvider, MyWebSiteDLL" /> ............

Then in your MyWebSite.UserIdentityProvider class you could override our various methods for core user storage and authentication persistence with your own unique implementation.