Forms authentication with NancyFx

In theses past weeks, I’ve been playing around with NancyFx – a simple web framework a la Ruby Sinatra. So far it has been a joy to use. While I still love ASP.NET MVC, I find myself a bit more appreciative each day of the simplicity of Nancy.

One of the thing that I had set out to do was to build a simple registration module using Forms Authentication for a sample app, and to verify that the authentication worked by trying to access a secured module.

The code is quite simple. First off, I had to implement a Bootstrapper to inject my service clients and to enable Forms Authentication.

public class BlogBootstrapper : AutofacNancyBootstrapper
{
protected override void ApplicationStartup(Autofac.ILifetimeScope container, Nancy.Bootstrapper.IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
#if DEBUG
StaticConfiguration.DisableErrorTraces = false;
#endif
CookieBasedSessions.Enable(pipelines);
var builder = new ContainerBuilder();
builder.RegisterType<AuthenticatedUserProvider>().As<IUserMapper>();
builder.RegisterType<BlogServiceClient>().As<IBlogServiceClient>();
builder.RegisterType<AuthenticationServiceClientClient>().As<IAuthenticationServiceClient>();
builder.Update(container.ComponentRegistry);
var config = new FormsAuthenticationConfiguration
{
RedirectUrl = "~/",
UserMapper =container.Resolve<IUserMapper>()
};
FormsAuthentication.Enable(pipelines,config);
}
//Loads my css, Images & scripts
protected override void ConfigureConventions(NancyConventions conventions)
{
base.ConfigureConventions(conventions);
conventions.StaticContentsConventions.Add(
StaticContentConventionBuilder.AddDirectory("Content", @"Content")
);
conventions.StaticContentsConventions.Add(
StaticContentConventionBuilder.AddDirectory("Scripts", @"Scripts")
);
}
}

view raw
BlogBootstrapper.cs
hosted with ❤ by GitHub

With the dependencies injected, I could build my Authentication module to handle login in and out of the system.

public class AccountModule : NancyModule
{
private readonly IAuthenticationServiceClient m_authenticationService;
public AccountModule(IAuthenticationServiceClient authenticationService)
: base("/Account")
{
m_authenticationService = authenticationService;
Post["/Login"] = args =>
{
var model = this.Bind<LogOnModel>();
var apiToken = m_authenticationService.Authenticate(model);
return this.LoginAndRedirect(Guid.NewGuid(), fallbackRedirectUrl: "~/blog");
};
Get["/Register"] = _ => View["/Account/Register"];
Get["/Login"] = _ => View["/Account/Login"];
Get["/LogOff"] = _ =>
{
this.ShouldBeAuthenticated();
Context.CurrentUser = null;
return this.LogoutAndRedirect("/Login");
};
}
}

view raw
AccountModule.cs
hosted with ❤ by GitHub

Now, to verify that it did indeed work, I wrote an extension class as well as a SecureModule class that validates that the user was set to the NancyContext prior to each requests .

public static class ModuleSecurity
{
public static void ShouldBeAuthenticated(this NancyModule module)
{
module.Before.AddItemToEndOfPipeline(ShouldBeAuthenticated);
}
private static Response ShouldBeAuthenticated(NancyContext context)
{
Response response = null;
if ((context.CurrentUser == null) ||
String.IsNullOrWhiteSpace(context.CurrentUser.UserName))
{
response = new RedirectResponse("/");
}
return response;
}
}

view raw
ModuleSecurity.cs
hosted with ❤ by GitHub

public abstract class SecureModule : NancyModule
{
protected SecureModule():this("")
{
}
protected SecureModule(string path):base(path)
{
this.ShouldBeAuthenticated();
}
}

view raw
SecureModule.cs
hosted with ❤ by GitHub

All that is left is to implement a module that would inherit from my SecureModule, et voila!

public class BloggerModule : SecureModule
{
private readonly IBlogServiceClient m_blogService;
public BloggerModule(IBlogServiceClient blogService)
: base("/blog")
{
m_blogService = blogService;
Get["/{Id}"] = parameters =>
{
var blogPost = m_blogService.GetBlogPost((long)parameters.Id,
(Context.CurrentUser as SessionUser).ApiToken);
return Negotiate.WithView("BlogPostDetail") //Use the Negotiatior to prepare my response
.WithModel(blogPost)
.WithStatusCode(HttpStatusCode.OK);
};
Get["/create"] = parameters => Negotiate.WithView("CreateBlogPost").WithStatusCode(HttpStatusCode.OK);
}
}

view raw
BloggerModule.cs
hosted with ❤ by GitHub

Forms authentication with NancyFx

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s