DotNetNuke Multi-Factor Authentication Provider

DotNetNuke Multifactor Authentication Requiring Password, One-Time SMS PIN, and X.509 Certificate

DotNetNuke Multifactor Authentication Requiring Password, One-Time SMS PIN, and X.509 Certificate

I am pleased to announce an initial release of a multi-factor provider for the DotNetNuke content management system.

This authentication provider allows a host to configure enhanced authentication (including SMS, SMTP, YubiKey, and X.509 certificates) for any number and combination of portal roles. Each factor must be fulfilled prior to authentication being granted, increasing overall security.

This package may be downloaded from it’s project site, located at http://dnnmultifactor.codeplex.com.

Goals

  • A robust, extensible multi-factor authentication framework for the DotNetNuke content management system.
  • Complete integration into the framework using existing extension points, with no core modifications or recompilation required.
  • Support for host-, administrator-, and user-level configuration, with the ability to vary required factors across an arbitrary set of roles.
  • A robust set of included factor providers, including SMS, secure SMTP, YubiKey (www.yubico.com), and X.509 certificate.
  • Extension points in the authentication system allowing for development of custom factors by third parties.
  • Reliance on the existing ASP.NET membership subsystem for existing (first-factor) authentication.
  • Minimization of “custom security” risk by relying, insofar as is possible, upon existing security infrastructure  ASP.NET membership, DotNetNuke portal security, password generation, et cetera.
  • As small an overall surface area as is possible, and an absolute reliance upon the existing ASP.NET membership system as a first-factor fallback, should any unforeseen vulnerability exist.

Background

The DotNetNuke content management system is a mature, robust, and widely-adopted web application framework. It offers role-based authentication and authorization using the ASP.NET membership subsystem, and allows for multiplexing across any number of websites per installation. Read more about DotNetNuke security-related functionality on their website.

DotNetNuke Multifactor Authentication Requiring Password and One-Time SMS PIN

DotNetNuke Multifactor Authentication Requiring Password and One-Time SMS PIN

However, DotNetNuke does not offer an out-of-the-box multi-factor authentication story. This is especially unfortunate, especially as the platform is more widely relied upon as a line-of-business portal for enterprise-scale applications such as high-volume e-commerce and banking. These applications, along with the fact that the platform offers cross-website “host” accounts, require a higher-than-normal level of authentication than a simple username/password combination is able to offer.

This project attempts to bridge that gap by extending existing DotNetNuke functionality to offer multi-factor authentication to those roles, users, and hosts configured to require it. Available factors include, in addition to the first-factor ASP.NET membership subsystem (which is always required), SMS, secure SMTP, and X.509 certificate-based authentication. The package is designed to be flexible and easily extended, allowing any arbitrary additional factors to be introduced by third-party developers.

Usage

This authentication extension has been designed for, tested against, and requires DotNetNuke version 5.0.1 or later. Details about core installation and the downloads therefor, are located on the DotNetNuke website.

Installation instructions

  1. Download the latest release.
  2. Backup all data associated with your installation, including both files and database.
  3. Sign in as a host account.
  4. The default configuration is designed, for demonstration purposes, to enable a SMTP factor for host accounts. Ensure that your installation has specified valid SMTP settings and that your host account has been configured with an active e-mail account.
  5. Install the authentication extension via the Host->Extensions menu option.
  6. After installation, visit your site’s login page (after logging out of your host account) to ensure the provider is functioning properly.
  7. Configure your web.config to select those factors and roles you wish to augment with additional security (see below).
  8. When satisfied with your ultimate configuration, disable the default DotNetNuke authentication system through the Host->Extensions->Default Authentication menu option.

Configuration

The DotNetNuke multi-factor authentication provider currently requires modification to the web.config file when specifying those roles that are to be authenticated with additional factors. This may be changed in the future and effectuated through the settings page.

The default addition to the installation’s web.config file includes a node located at configuration/brandonHaynes. It is here that the factors — and the roles that map thereto — are defined:

<brandonHaynes>
  <authenticationFactors>
    <factor name="Membership" type="BrandonHaynes.Membership.Factors.MembershipFactor..." />
    <factor name="SMTP" text="Enter your one-time password:"
        type="BrandonHaynes.Membership.Factors.SmtpFactor..." />
    <factor name="SMS" appId="3165534" userName="..." password="..."
        text="Enter your one-time password:"
        type="BrandonHaynes.Membership.Factors.SMSFactor..." />
    <factor name="X.509" text="A valid certificate is required to access this account."
        type="BrandonHaynes.Membership.Factors.ClientCertificateFactor..." />
      <factor name="YubiKey" text="Activate your YubiKey now:" id="..." key="..."
        type="BrandonHaynes.Membership.MultifactorMembershipProvider..." />
  </authenticationFactors>
  <authenticationMap>
    <role name="host">
      <factor name="YubiKey" />
    </role>
    <role name="Administrators">
      <factor name="SMS" />
    </role>
  </authenticationMap>
</brandonHaynes>

Here, we have five factors defined  ASP.NET membership, SMTP, SMS, YubiKey, and X.509 certificate) as children of the brandonHaynes/authenticationFactors node. Additional factors may be defined therein.

Under the brandonHaynes/authenticationMap node, we map roles to factors. In the example above, the host role requires a valid YubiKey one-time password and those in an administrator role requires a SMS-dispatched one-time-password. Additional roles and factors may be added (or removed) to enable virtually any configuration. Note that, regardless of configuration, the ASP.NET membership factor is always required.

Factors Included in this Distribution

The following factors are included in this distribution:

  • SMS. This factor utilizes the Clickatell SMS gateway for secure one-time password delivery. Factor configuration requires account id and password; this may be encrypted for additional protection (see this for details).
  • X.509. This factor utilizes the certificate presented by the client when making an authentication decision. The factor utilizes the built-in ASP.NET subsystem (which, itself, relies upon IIS) in making decisions related to the presence and validity of a client certificate. Assuming both, the certificate subject is compared to a (configurable) user property (by default the username). Alternate profile properties may be used as a basis for comparison by including a profileProperty attribute in the factor definition. For example, to compare a user’s e-mail address, configure the factor as:
    <factor name="X.509" profileProperty="email" text="..."
         type="BrandonHaynes.Membership.Factors.ClientCertificateFactor..." />
  • SMTP. This factor utilizes the SMTP server configured for the current DotNetNuke website for transmission of one-time password. Though these settings may be configured to transmit this information securely, in most situations this configuration should not be relied upon for true out-of-band one-time-password transmission (an exception might exist for delivery to a SMS or handheld device external to the authenticating computer system).
  • YubiKey. This factor requires activation of a valid YubiKey (www.yubico.com) to authenticate. By default, the factor authenticates againsthttps://api.yubico.com/wsapi/verify, but this may be changed by including a verifyUri attribute on the factor node. Note that by default if no YubiKey is associated with an authenticating account, it will be associated upon first use. Subsequent authentication requires the same YubiKey to be presented. This feature may be deactivated with an associateOnFirstUse=”false” attribute. Note that if automatic association is disabled, accounts requiring YubiKey authentication must be manually associated (by a host). If possible, it is a good idea to disable automatic association after all accounts have associated for the first time.

Custom Factors

This authentication provider is designed to be highly extensible and to allow for additional factors to be added with relative ease. Developers may deploy their own factors by:

  • Including a constructor having the prototype of the form:
MyFactor(IDictionary<string, string> attributes);
  • Implementing the BrandonHaynes.Membership.IAuthenticationFactor interface, defined as:
public interface IAuthenticationFactor
	{
	string Name { get; }
	void Authenticate(UserInfo user, Credential credential);
	Control PromptControl { get; }
	}

Note that the PromptControl property above is a control that handles the input of whatever additional credential (one-time-password or otherwise) is desired. Three are provided for use in custom development:

  1. MessagePrompt (to display a simple message),
  2. StringPrompt (to input a one-time-password), and
  3. NullPrompt (does not display or require any additional input).

Though any control may be used here, note that because of the way they are instantiated, much of the control life cycle is omitted (in particular ViewState and automatic IPostBackDataHandler handling is not activated). These controls must remain as simple as possible.

Custom SMS Gateways

The SMS factor is designed to be extendable and allow for integration with alternate SMS gateway providers. This may be effectuated by deriving from BrandonHaynes.Membership.SmsFactor and overriding one or more of the following methods:

// Override this method to send a specific one-time password to a telephone 
// number for a particular user using whatever custom SMS gateway is desired.
protected void SendSms(UserInfo user, string telephone, string onetimePassword);
// Override this method to format a given telephone number for transmission to a 
// custom SMS gateway service.  Note that the default implementation is 
// (unfortunately) US-centric and does not perform a robust E.164 formatting (code 
// submissions for this facet would be greatly appreciated!).  
string FormatTelephoneByCountry(UserInfo user, string telephone);

In Case of Emergency…

As with any authentication system, any failure that might occur has a high risk of locking a host out of his or her account. As such, I strongly recommend deploying this package to a staging server prior to production deployment, along with other reasonable precautions.

Should an unrecoverable lockout occur, please note that the default DotNetNuke authentication system may be re-enabled through modification to the dbo.Authentication table of your database. The following SQL will effectuate this (and at the same time disable all other authentication systems that might be available). Use it as an emergency escape.

UPDATE dbo.Authentication
SET IsEnabled =
	CASE WHEN AuthenticationType = 'DNN' THEN 1 ELSE 0 END

Note that you will need to recycle your application pool to realize this change; this may be most easily effectuated by recycling your IIS application pool, restarting IIS, or by making any change to your web.config file (thanks to Mitchel Sellers for pointing out this oversight!).

Feedback Appreciated!

Feedback about your experiences is needed, and greatly appreciated!

Be Sociable, Share!

16 Comments

  1. Mitchel Sellers

    May 1, 2009 @ 11:17 am

    1

    Brandon,

    This looks like a great provider!

    Just one note about your lockout situation. After running the SQL update you must re-cycle the application before the change to the provider will be there.

  2. Mike Horton

    May 4, 2009 @ 12:08 am

    2

    Nice work Brandon.

  3. Scott Allender

    May 4, 2009 @ 7:11 pm

    3

    Wicked smart authentication system. Looking forward to testing it out on some of my own systems.

  4. Deep

    May 5, 2009 @ 7:26 am

    4

    Great Stuff, like God sent, I was just looking for this 🙂

    Just one question, we are using 4.9.3, can we somehow tweak this to use with this version??

    Thanx in advance

  5. Brandon Haynes

    May 5, 2009 @ 7:48 am

    5

    @Mitchel: Thanks for that catch! I’ve adjusted the details herein to rectify this oversight.

    @Deep: The 5.x line is due to become the recommended version in the near future; as such, it is unlikely that I will provide a 4.x build. However, there is no reason that you could not build a 4.x-compatible version yourself. There are only a few places where I am using 5.x-specific calls; these would have to be adjusted to their 4.x deprecated variants. There should be no functionality that would not be easily transferable to 4.x.

    @Everyone: Appreciate the feedback. I hope the community finds it useful.

  6. Will Baker

    May 11, 2009 @ 5:03 am

    6

    This looks really interesting.

    I’m currently looking to authenticate an extranet against a clients RADIUS server. Is it possible that your provider could be extended to handle this?

    Thanks,

    Will

  7. Brandon Haynes

    May 11, 2009 @ 8:39 am

    7

    Hi Will,

    The applicability of this provider will depend on your exact needs. If your RADIUS server will be serving as an authoritative store for your authentication information then your best bet will be to consume a simple membership provider (a la http://wcfradiusservice.codeplex.com/) or create one that extends System.Web.Security.MembershipProvider.

    If you require additional UI, consider an authentication extension of a flavor similar to that in the DotNetNuke core OpenID or LiveID providers. This additional provider would exist as a login option alongside whatever other authentication options you wanted to enable on your system (or it could be the sole option, if that is your requirement).

    If you need to enforce authentication factors on a role-by-role basis, then this provider may meet that need (note that it still requires first-factor authentication against an ASP.NET membership store). This does appear, prima facia, to be the least likely of the cases presented above.

    Hope this helps!

    Brandon

  8. Will Baker

    May 14, 2009 @ 11:09 am

    8

    Hi Brandon,

    Yes, thanks for your reply – it does help shed some light on what options are open…

    I think some more research is needed!

    Will

  9. Jonathan

    September 30, 2009 @ 11:13 pm

    9

    Hi Brandon — This could not be used to control access to specified tabs, correct? For example, many banks and e-commerce sites require re-authenticating when attempting to open certain pages (e.g., account profile, credit card info, etc.) even if the user as logged into the site.

    Thanks.

  10. Eugene

    October 6, 2009 @ 4:05 pm

    10

    Hi Brandon,

    I have some issues with Multifactor Membership Provider – I downloaded and install last week latest DNN (5.1.1) and you module.

    My task is to build our company intranet web site, but we are planning to use Digital Certificates to authenticate all our users (some sales people located in different countries outside of our office), so requirement is to hide all UserID/Password of usual form authentication and use only Digital Certificate to make login transparent and seamless – we are already have other .NET system (not DNN based) in production with exactly the same authentication.

    So is it possible to hide (disable/bypass) existing form authentication and use you module for digital certificate authentication ?

    Back to error I have with you module:

    InnerException: The given key was not present in the dictionary.
    FileName:
    FileLineNumber: 0
    FileColumnNumber: 0
    Method: System.ThrowHelper.ThrowKeyNotFoundException
    StackTrace:
    Message: DotNetNuke.Services.Exceptions.PageLoadException: The given key was not present in the dictionary. —> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. at System.ThrowHelper.ThrowKeyNotFoundException() at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at BrandonHaynes.Membership.MultifactorMembershipProvider.b__4(String name) at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate) at BrandonHaynes.Membership.MultifactorMembershipProvider.MultifactorLogin(MultifactorUser user, Credential credential, UserLoginStatus& loginStatus) at BrandonHaynes.Membership.MultifactorMemb…erLogin(Int32 portalId, String username, String password, String authType, String verificationCode, UserLoginStatus& loginStatus) at DotNetNuke.Entities.Users.UserController.ValidateUser(Int32 portalId, String Username, String Password, String authType, String VerificationCode, String PortalName, String IP, UserLoginStatus& loginStatus) at DotNetNuke.Modules.Admin.Authentication.Login.cmdLogin_Click(Object sender, EventArgs e) at System.Web.UI.WebControls.Button.OnClick(EventArgs e) at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) — End of inner exception stack trace —
    Source:
    Server Name: QVCONL01

    My Config file:

    Thank you Brandon for your effort!

    Eugene

  11. Eugene

    October 6, 2009 @ 4:08 pm

    11

    Sorry Brandon, in my last comment missing my Config file:

    Thank Brandon for your effort!

    Eugene

  12. -mika-

    December 3, 2009 @ 12:11 pm

    12

    Hi Brandon,

    Than you for the provider! I have been using it with the ClientCertificate option and fixed some of the bugs on the go, like the previous ” The given key was not present in the dictionary.” error.

    Below are short descriptions of the issues. I have been using the provider only with DNN 4.9.5.

    Issue: Does not install in 4.9.X
    Solution: Convert the .dnn manifest, take example from another auth provider.

    Issue: Does not compile in 4.9.X
    Solution: Comment out the unsupported fields in MultifactorUser.cs and SmtpFactor.cs

    Issue: If DNN username not found, you’ll get an error “The given key was not present in the dictionary” or “Object reference not set to an instance of an object”
    Solution: Check if GetUserByUserName(portalId, username, false) returns null. If it does, return null in UserLogin. (MultifactorMembershipProvider.cs:40)

    Issue: “Value cannot be null. Parameter name: source” if DNN user is not authorized
    Solution: Check if user.MissingCredentials is null in IsIncompleteMultifactorUser(MultifactorUser user). Return false if it is. (MultifactorControl.cs:44)

    Other:
    In ClientCertificateFactor.cs, you might have to parse certificate.Subject. In my case, the certificate.Subject was a X.400 formatted address.

    Also, if your site has both public sections and client certificate authenticated sections, it is annoying if the client cert dialogue pops up for anonymous users.
    I used a virtual directory /login redirected at /login.aspx, and required the client cert for only this url. Now, if the user can’t login because of missing client cert, the MessagePrompt text (in web.config or in code) will instruct to visit this /login url first.

    Hope I remembered everything right and didn’t introduce new bugs. I can send you the code if needed.

    -mika-
    http://www.flowmarks.com

  13. Brandon Haynes

    December 7, 2009 @ 11:36 am

    13

    Hi Mika,

    Greatly appreciate the feedback, and (notwithstanding the issues you described), am glad to hear that you’ve found it useful.

    As you’re already aware, the provider was designed for and built against 5.0.1 (or later), so it is expected that some issues exist with the 4.x line. I would be very interested in investigating and/or incorporating your work to remedy this (and apparently other issues as well).

    If you’re willing, please consider uploading one or more of your change-sets to the CodePlex website. I’ve been meaning to give this provider some attention, but due to other responsibilities this will slip until at least after December 21.

    You’ve got a great point about the certificate parsing; if I recall correctly, I actually intentionally “underdeveloped” this factor to see how it would be used in practice. It certainly would not be too difficult to include a regular expression pattern in the factor definition to handle, inter alia, the X.400 case.

    Your help here is greatly appreciate, and I’d be very excited to incorporate some of your suggestions and code revisions.

    B

  14. Germaine

    June 30, 2010 @ 3:17 am

    14

    Hi Brandon! Thanks for the wonderful mulitfactor extensions!! wondering how do I add in the sms authentication? I have my sms gateway

    The SMS factor is designed to be extendable and allow for integration with alternate SMS gateway providers. This may be effectuated by deriving from BrandonHaynes.Membership.SmsFactor and overriding one or more of the following methods:

    // Override this method to send a specific one-time password to a telephone
    // number for a particular user using whatever custom SMS gateway is desired.
    protected void SendSms(UserInfo user, string telephone, string onetimePassword);

    // Override this method to format a given telephone number for transmission to a
    // custom SMS gateway service. Note that the default implementation is
    // (unfortunately) US-centric and does not perform a robust E.164 formatting (code
    // submissions for this facet would be greatly appreciated!).
    string FormatTelephoneByCountry(UserInfo user, string telephone);

    thanks!

  15. Brandon Haynes

    July 7, 2010 @ 11:06 am

    15

    Hi Germaine,

    You will need to create a new class that inherits the base BrandonHaynes.Membership.SmsFactor class and overrides one (or both) of the virtual methods. Overriding the first will allow you to contact your gateway and issue a SMS message to the given telephone number with the given OTP.

    Hope this helps!

    Brandon

  16. -mika-

    July 30, 2010 @ 4:02 am

    16

    Sorry it took so long (until my holiday!), but I posted the change set to CodePlex now. I stripped from the patch all the custom stuff that prevented me to send it earlier.

    One custom thing we did for our client was to bypass subsequent DNN logins once the client certificate was validated. But for the first time, the certificate has to be attached to a DNN account by giving valid DNN credentials. This and the certificate parsing code is quite special-purpose, though.

Log in