-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Forms authentication
This document provides an overview on how to enable forms authentication in your Nancy application. For a full working sample, please refer to the Nancy.Demo.Authentication.Forms
project in the Nancy solution.
To enable forms authentication in your application, you need to complete the following steps
- Install the
Nancy.Authentication.Forms
package - Implement an
IUserMapper
- Implement routes to handle login and logout
- Configure and enable Forms Authentication
The user mapper is responsible for mapping a user from an identifier. The identifier is the token that will be put in the authentication cookie which will be used to re-establish the identity of the user that is performing the request, so that you do not need to enter your credentials for each request.
We have chosen to use a GUID as the identifier. The reason for this is that using something like the username of id is a potential vulnerability, because, if the cookie encryption was somehow compromised, it would be easy to spoof the identity of another user by guessing their username or id.
The identifier should never be read by a human, so making it a GUID also discourages that it is ever displayed anywhere, and each GUID is both unique and non-sequential, making it a lot harder to spoof than the identity of someone else.
It is also important to know that the identifier should be treated as permanent for the user that it was generated for and will be reused across requests and application sessions.
The IUserMapper
interface is defined in the following way
public interface IUserMapper
{
/// <summary>
/// Get the real username from an identifier
/// </summary>
/// <param name="identifier">User identifier</param>
/// <param name="context">The current NancyFx context</param>
/// <returns>Matching populated IUserIdentity object, or empty</returns>
IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context);
}
A single method whose sole responsibility is to take the GUID identifier and return the corresponding user. If the identifier could not be mapped to a user then null
should be returned. For more information about the IUserIdentity
interface, refer to the [Authentication overview](Authentication overview).
Once you have implemented an IUserMapper
, it is time to add the resources to your application that are responsible for handling the login and logout procedures. These are implemented using normal routes, and should be placed in a module that does not require the user to be authenticated to access.
Below is an example of the basic structure of such a module. Please note that the paths of the resources, and the name of the module, can be anything; it’s not necessary to follow the naming in this example:
public class LoginModule : NancyModule
{
public LoginModule()
{
Get["/login"] = parameters => {
// Called when the user visits the login page or is redirected here because
// an attempt was made to access a restricted resource. It should return
// the view that contains the login form
};
Get["/logout"] = parameters => {
// Called when the user clicks the sign out button in the application. Should
// perform one of the Logout actions (see below)
};
Post["/login"] = parameters => {
// Called when the user submits the contents of the login form. Should
// validate the user based on the posted form data, and perform one of the
// Login actions (see below)
};
}
}
When the user logs in or out, certain actions need to be taken, such as creating or removing the authentication cookie and redirecting the user to the appropriate location. To aid with these common tasks, there are several extension methods (located in the Nancy.Authentication.Forms
namespace) that you can use
-
LoginAndRedirect
- Logs the user in and redirects the user back to the url that they came from. You can specify a fallback url that will be used if no redirect url was available -
LoginWithoutRedirect
- Logs the user in and returns a response with status code 200 (OK) -
Login
will call theIsAjaxRequest
extension method on the current request and perform aLoginAndRedirect
if it was not an Ajax call, otherwise it will performLoginWithoutRedirect
-
LogoutAndRedirect
- Logs the user out and redirects to the provided url -
LogoutWithoutRedirect
- Logs the user our and returns a response with status code 200 (OK) -
Logout
will call theIsAjaxRequest
extension method on the current request and perform aLogoutAndRedirect
if it was not an Ajax call, otherwise it will performLogoutWithoutRedirect
Note1: The IsAjaxRequest
extension method (found in Nancy.Extensions.RequestExtensions) will look for the X-Requested-With
header and return true if it contains the value XMLHttpRequest
Note 2: Please make sure the routes you define for displaying the login page and taking login credentials are not secured behind a RequiresAuthentication call - you may laugh, but this is quite a common mistake :-)
The only thing left to do now is actually enable Forms Authentication, this is done by simply adding a line like this to your bootstrapper:
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
It can either be added to the ApplicationStartup
or RequestStartup
. Which you should choose often depends on your implementation of the IUserMapper
, that is, should the user mapper have an application or request lifetime.
The formsAuthConfiguration
variable that is passed into the FormsAuthentication.Enable
method, is an instance of the FormsAuthenticationConfiguration
type, which enables you to customize the behavior of the forms authentication provider.
For instance, here is a basic configuration
var formsAuthConfiguration =
new FormsAuthenticationConfiguration()
{
RedirectUrl = "~/login",
UserMapper = container.Resolve<IUserMapper>(),
};
It configures the Forms Authentication to redirect the user to ~/login
when an unauthorised user attempts to gain access to a protected resource and it tells it which user mapper it should use.
Depending on if this is placed in ApplicationStartup
or RequestStartup
the mapper will have different lifetimes. Also, please note that the method for resolving the mapper, from the container, will vary depending on which bootstrapper you are using.
The following configurations can be made
-
RedirectingQuerystringKey
- The name of the querystring parameter that will contain the URL of the secure resource that was being accessed while being unauthorized. This is used to redirect the user back once the login was successful. The default name isreturnUrl
-
RedirectingUrl
- The (relative) url that the user should be redirected to when trying to access a secure resource when not being authenticated. This is usually your login page and should be in the format~/login
-
UserMapper
- TheIUserMapper
that should be used during authentication -
RequiresSSL
- Determines if the forms authentication cookie can only be accessed over a secure request or not. The default is false -
DisableRedirect
- Determines whether to redirect to the login page during an unauthorized access -
CryptographyConfiguration
- TheCryptographyConfiguration.Default
that should be used when working with the forms authentication cookie. If this is not specified thenCryptographyConfiguration.Default
will be used as the default.
If you do not specify a value for the CryptographyConfiguration
property on the FormsAuthenticationConfiguration
object, then it will default to using CryptographyConfiguration.Default
.
The default behavior uses the RandomKeyGenerator
which means that a new crypto key is generated each time the application starts, so if your application is restarted, or the appdomain recycled, then user’s authentication cookies will become invalid; this will also be the case between machines in a load balanced environment. Don’t panic though! Changing this configuration is simple to do, for more in depth information read about the [Cryptographic configurations](The cryptography helpers).
For this reason you should always use your own CryptographyConfiguration
, below is an example of what that could look like when used together with forms authentication.
var cryptographyConfiguration = new CryptographyConfiguration(
new RijndaelEncryptionProvider(new PassphraseKeyGenerator("SuperSecretPass", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })),
new DefaultHmacProvider(new PassphraseKeyGenerator("UberSuperSecure", new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 })));
var config =
new FormsAuthenticationConfiguration()
{
CryptographyConfiguration = cryptographyConfiguration,
RedirectUrl = "/login",
UserMapper = container.Resolve<IUserMapper>(),
};
Forms authentication with nancyfx
Multiple forms authentication sections
[<< Part 21. Stateless](Stateless Authentication) - Documentation overview - [Part 21. Token >>](Token Authentication)
- Introduction
- Exploring the Nancy module
- Routing
- Taking a look at the DynamicDictionary
- Async
- View Engines
- Using Models
- Managing static content
- Authentication
- Lifecycle of a Nancy Application
- Bootstrapper
- Adding a custom FavIcon
- Diagnostics
- Generating a custom error page
- Localization
- SSL Behind Proxy
- Testing your application
- The cryptography helpers
- Validation
- Hosting Nancy with ASP.NET
- Hosting Nancy with WCF
- Hosting Nancy with Azure
- Hosting Nancy with Suave.IO
- Hosting Nancy with OWIN
- Hosting Nancy with Umbraco
- Hosting Nancy with Nginx on Ubuntu
- Hosting Nancy with FastCgi
- Self Hosting Nancy
- Implementing a Host
- Accessing the client certificate when using SSL
- Running Nancy on your Raspberry Pi
- Running Nancy with ASP.NET Core 3.1