Using OWIN Security with MultiValue Data - Part 3
In the first article, I explained a little bit about what OWIN was, and the basic setup for creating a connection between an OWIN application and a MultiValue framework. In the second article, we covered the basic interface you need to implement in order to get the minimum requirements for using OWIN's identity/security framework in place. There are a few connection points left to create before it will be complete. Ready?
Quick Review
ApplicationDBContext - This class does all the work of connecting to the database and making the subroutine calls to return or update data.
ApplicationUser - This class is used to hold information about the user that the rest of the OWIN Identity system will use. This class will call the SPECTRUM.OWIN.USER subroutine and return a dynamic array of information about the user.
ApplicationUserStore - This class does most of the work of interface between OWIN and the ApplicationUser class. As you saw in the Part2 this class is very modular in design, so you only have implemented the interface requirement that you need.
ApplicationUserManager
The UserManager is designed to configure all the account management settings required for OWIN to handle your logins for you. Over all it is a pretty simple class [Figure 1]
Imports Microsoft.AspNet.Identity Imports Microsoft.AspNet.Identity.Owin Public Class ApplicationUserManager Inherits UserManager(Of ApplicationUser) Public Sub New(ByVal Store As ApplicationUserStore) MyBase.New(Store) Me.PasswordHasher = ApplicationUser.PasswordHasher ' New ApplicationPasswordHasher End Sub Public Shared Function Create() As SpectrumUserManager Dim manager As New SpectrumUserManager(New SpectrumUserStore) ' Configure validation logic for usernames manager.UserValidator = New UserValidator(Of SpectrumUser)(manager) With { .AllowOnlyAlphanumericUserNames = False, .RequireUniqueEmail = True } ' Configure validation logic for passwords manager.PasswordValidator = New PasswordValidator With { .RequiredLength = 4, .RequireNonLetterOrDigit = False, .RequireDigit = False, .RequireLowercase = False, .RequireUppercase = False } ' Configure user lockout defaults manager.UserLockoutEnabledByDefault = True manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5) manager.MaxFailedAccessAttemptsBeforeLockout = 5 ' Register two factor authentication providers. This application uses ' Phone and Emails as a step of receiving a code for verifying the user ' You can write your own provider and plug it in here. manager.RegisterTwoFactorProvider("Phone Code", New PhoneNumberTokenProvider(Of SpectrumUser) With { .MessageFormat = "Your security code is {0}" }) manager.RegisterTwoFactorProvider("Email Code", New EmailTokenProvider(Of SpectrumUser) With { .Subject = "Security Code", .BodyFormat = "Your security code is {0}" }) manager.EmailService = New EmailService() manager.SmsService = New SmsService() Return manager End Function End ClassFigure 1
Looking at the code you will notice that this class require the ApplicationUser class and the ApplicationUserStore class. It only has method that you need to be concerned with: Create() .
The create method will be used in the Startup.ConfigureAuth method. Similar to how the ApplicationDBContext was created in the first article in the series.
User and Password Policies
The first thing you will need to do is define how you want to set your username and password policy. The properties UserValidator and PasswordValidator are used by OWIN to help create client side validation for your UserName and Password [Figure 1, lines 15-25]
There are many different options you can set here, but the minimum I would set are AllowOnlyAlphanumericUserNames and RequireUniqueEmail . If you set the first one to true will only allow A-Z and 0-9. Setting AllowOnlyAlphanumericUserNames to false allows any character to be included in the user name. You want it to be false so that email addresses can be accepted as valid user name.
The RequireUniqueEmail address is also important if you are going to be using OWIN's create new users system. If you plan on allowing OWIN to create and manage the user information, and let's face it, why wouldn't you , then make sure you set this property to true.
Of course, if if is set to true, then you should also implement the IUserEmailStore interface in the ApplicationUserStore . This interface implements the FindByEmail method so that you can look in your database to make sure the email address doesn't already exist.
This will force OWIN to look into the database to make sure the email address hasn't been duplicated. If it has been duplicated then, it will generate an error message and allow the user to send an "Forgot my User" email. It will allow your database to manage the email conformation when a password is reset and allow OWIN to implement two-factor logins.
The PasswordValidator is just as important, and you will need to take a really close look at what your password policies are. Consider these settings:
RequiredLength - Minimum length of the password.
RequireNonLetterOrDigit - Password must contain at least one symbol. The @ and the . in the email address with satisfy this condition.
RequireDigit - Password must contain at least one number.
RequireLowercase - Password must contain at least one lowercase character.
RequireUppercase - Password must contain at least one uppercase character.
User Lockouts
OWIN can also handle your user lockout for you. You can define how many times the user can attempt to login with the incorrect password before locking the user out, as well as the length of time that the lockout will be in maintained.
To really be effective with this option, you should also implement the IUserLockoutStore interface in the ApplicationUserStore . This way your database will be notified of the lockout status of each user, how many times a bad password has been attempted and as well as allow you to unlock it from the database.
If you don't want to have user lockouts, just setUserLockoutEnabledByDefault to false, and you don't have to implement anything, or worry about users calling asking why they can't get in.
ApplicationSignInManager
This class does all the grunt work of the sign in/out, password validation, and external tokens and two-factor authentication. There really isn't anything you are required to do here, other than make a class that connects all your custom classes together [Figure 2].
Public Class ApplicationSigninManager Inherits SignInManager(Of ApplicationUser, String) Public Sub New(ByVal UserManager As SpectrumUserManager, ByVal AuthenticationManager As IAuthenticationManager) MyBase.New(UserManager, AuthenticationManager) End Sub Public Shared Function Create(options As IdentityFactoryOptions(Of SpectrumSigninManager), context As IOwinContext) As SpectrumSigninManager Return New ApplicationSigninManager(context.GetUserManager(Of SpectrumUserManager)(), context.Authentication) End Function End Class
Figure 2
StartUp.Auth
Once you have these classes implemented, you need to update your Startup class [Figure 3].
Public Sub ConfigureAuth(app As IAppBuilder) ... app.CreatePerOwinContext(AddressOf ApplicationDbContext.Create) app.CreatePerOwinContext(Of ApplicationUserManager)(AddressOf ApplicationUserManager.Create) app.CreatePerOwinContext(Of ApplicationSignInManager)(AddressOf ApplicationSignInManager.Create) ... End Sub
Figure 3
While this seems like a lot to do to make OWIN work, it really is more of a cut-and-paste kind of coding that anything complex. That's about it for implementing the OWIN identity framework.