In a previous blog entry I looked at adding persistence to a WF workflow inside an MVC application, using a simple wizard example. This was easy enough but not really a good real world example and discussions about it suggested an expiring password reset token scenario could be a good use of workflow inside a web site. Using a persisted WF workflow would separate the process logic from the MVC controllers and model classes and avoid needing persistence logic for the process instances (expiring tokens). This style of solution could also be used for other website functionality which require persisted workflow actions, such as changing email addresses and retrieving usernames.
This example takes the default MVC website and updated the login screen to include a forgot password link, this link leads to a new screen which allows the user to enter their username and submit a password reset request. This starts the workflow, sending an email to the users email address containing a link with an expiring token. If the user clicks the link they are taken to a screen where the token is validated and if it hasn’t expired they can submit answers to their security questions. If they answer correctly their password will be reset and an email with the new password will be sent to them.
The sample project uses the base MVC2 website template, updated with a new PasswordResetController with related model/service objects and views. The controller logic is very simple, merely calling onto the service object to interact with the workflow and depending on the returned result it directs to the required view. The model/service logic is also simple, using a host class for the associated workflow to make calls to the process. The process logic is all contained in the PasswordReset folder, which holds the WF Process Activity, custom Activities, host class and service class. This was placed in the web site for simplicity, but should probably be held in a separate class library with it’s own tests. The service class is there so all the workflow integration logic (sending emails and reseting the password) is handled by an object passed when starting the process, implementing an interface. This allows the workflow to be tested independently of the web site by mocking the PasswordResetService object. The main logic is in the host class which starts/resumes the workflow.
The process itself uses a flowchart activity with custom activities (SendEmailToken etc.) to call onto the integration logic in the service class with the workflow variables. There is a bookmark in the PasswordResetClientActivity which persists the workflow at that point, waiting for client commands. The only oddity is the decision control looping back to the PasswordResetClientActivity when it is completed with the result “Valid”. This is needed when client calls to check if the token is still valid, so the workflow does not complete.
It was very quick and easy to implement this, letting WF handle the process and persistence logic saves a lot of work. The main task was figuring out the tricks in the hosts flow, how to pass in the workflow arguments when starting and action the client activity using the bookmark. It’s a nice simple solution and the separation between the workflow logic and controller logic means changes can easily be made to the process without needing changes to the controller logic. One small disadvantage is the expiring tokens only expire (end the process) at the point they are checked, so it the user does not click the link the process won’t end. Since the workflow is only active when not idle this only means old instances will leave a couple of rows in the instance store DB, which shouldn’t be an issue and can be cleaned up if necessary. If you wanted a more complicated workflow or needed better auditing/error handling you could host the workflow in a WCF service instead and make use of Windows Server AppFabric.
Download word document with attached source.