I am making this little project in Nancy because I want to use it at work next year. I tried out most of the things I will be needing and trying to get a feel of the product. This has of course resulted in some blogposts. And it will save me a lot of time once I get back.

Yesterday it was time to add some authentication to my pages. I choose to use Form Authentication.

Setting it up

You will just have to add the Nancy.Authentication.Forms to your project.

And then the first thing to do is to alter your bootstrapper.

And add this to it.

```vbnet Protected Overrides Sub ConfigureRequestContainer(container As TinyIoCContainer, context As NancyContext) MyBase.ConfigureRequestContainer(container, context) container.Register(Of IUserMapper, FakeUserMapper)() End Sub

Protected Overrides Sub RequestStartup(container As TinyIoCContainer, pipelines As IPipelines, context As NancyContext)
    Dim formsAuthConfiguration = New FormsAuthenticationConfiguration With
        .RedirectUrl = "~/login",
        .UserMapper = container.Resolve(Of IUserMapper)()
    FormsAuthentication.Enable(pipelines, formsAuthConfiguration)
End Sub```

The above tells us that the route to our login page will be /login and that we need a class that implement IUserMapper and that is called FakeUserMapper. So here we go.

```vbnet Imports Nancy.Authentication.Forms Imports WebApplication2.Services

Namespace Security Public Class FakeUserMapper Implements IUserMapper

    Private ReadOnly _userService As UserService

    Public Sub New(userService As UserService)
        _userService = userService
    End Sub

    Public Function GetUserFromIdentifier(ByVal identifier As Guid, ByVal context As Nancy.NancyContext) As Nancy.Security.IUserIdentity Implements IUserMapper.GetUserFromIdentifier
        Dim user = _userService.GetById(identifier)
        Return New AuthenticatedUser() With
            .UserName = user.Name,
            .Claims = user.Claims
    End Function

End Class

End Namespace``` So I will also need a UserService and an AuthenticatedUser class.

```vbnet Imports Nancy.Security

Namespace Security

Public Class AuthenticatedUser
    Implements IUserIdentity

    Public Property UserName() As String Implements IUserIdentity.UserName
    Public Property Claims() As IEnumerable(Of String) Implements IUserIdentity.Claims
End Class

End Namespace vbnet Imports Microsoft.VisualBasic.ApplicationServices Imports WebApplication2.Model

Namespace Services Public Class UserService

    Private ReadOnly _users As IList(Of UserModel)

    Public Sub New()
        _users = New List(Of UserModel)
        _users.Add(New UserModel With {.Id = New Guid("00000000000000000000000000000004"), .Name = "Chris1", .Password = "123"})
        _users.Add(New UserModel With {.Id = New Guid("00000000000000000000000000000001"), .Name = "Chris2", .Password = "123"})
        _users.Add(New UserModel With {.Id = New Guid("00000000000000000000000000000002"), .Name = "Chris3", .Password = "123"})
        _users.Add(New UserModel With {.Id = New Guid("00000000000000000000000000000003"), .Name = "Chris4", .Password = "123"})
    End Sub

    Public Function GetUsers() As IList(Of UserModel)
        Return _users
    End Function

    Public Function AuthenticateUser(ByVal username As String, ByVal password As String)
        Dim user = _users.SingleOrDefault(Function(userModel) userModel.Name = username)
        If user IsNot Nothing AndAlso Not user.Password.Equals(password) Then
            Return Nothing
        End If
        Return user
    End Function

    Public Function GetById(ByVal identifier As Guid) As UserModel
        Return _users.SingleOrDefault(Function(userModel) userModel.Id = identifier)
    End Function
End Class

End Namespace``` Warning! Warning! Warning!

Don’t use New Guid(“00000000000000000000000000000000”) that is not considered to be a Guid. And it will have you scratching your hair for a few hours. If only I had hair.

Now we just need a module to intercept our login route.

```vbnet Option Strict Off

Imports System.Dynamic Imports Nancy.Authentication.Forms Imports Nancy.ModelBinding Imports Nancy Imports WebApplication2.Services

Namespace Modules

Public Class LoginModule
    Inherits NancyModule

    Public Sub New(ByVal userService As UserService)
        MyBase.Get("/login") = Function(parameters)
                                   Return View("login.vbhtml")
                               End Function
        MyBase.Post("/login") = Function(parameters)
                                    Dim loginParams = Me.Bind(Of LoginParams)()
                                    Dim user = userService.AuthenticateUser(loginParams.Username, loginParams.Password)
                                    If user Is Nothing Then
                                        Return "Your username and password were incorrect please enter a correct one."
                                    End If
                                    Return Me.LoginAndRedirect(user.Id)
                                End Function
        MyBase.Get("/logout") = Function(parameters)
                                    Return Me.LogoutAndRedirect("~/")
                                End Function
    End Sub
End Class

Public Class LoginParams
    Public Property Username As String
    Public Property Password As String
End Class

End Namespace``` So we got a get for login to show our login page, we got a post for login so that people can be authenticated and we got a get for logout. The important methods are LoginAndRedirect and LogoutAndRedirect.

Here is the View that goes with that.

```vbnet @Code ViewBag.Title = “Index page” Layout = “Master.vbhtml” End Code

<form method="POST">
    Username <input type="text" name="Username" />
    <br />
    Password <input name="Password" type="password" />
    <br />
    <input type="submit" value="Login" />

So now we have our login process all set up. Now it is time to protect something.

The protected

I will protect the user information in this one.

Our module is super simple.

```vbnet Imports WebApplication2.Model Imports Nancy Imports Nancy.Security Imports WebApplication2.Services

Namespace Modules

Public Class UsersModule
    Inherits NancyModule

    Public Sub New(userService As UserService)
        MyBase.Get("/users") = Function(parameters)
                                   Return View(New UsersModel() With {.Users = userService.GetUsers()})
                               End Function
        MyBase.Get("/users/{Id}") = Function(parameters)
                                        Dim result As Guid
                                        Dim isInteger = Guid.TryParse(, result)
                                        Dim user = userService.GetById(result)
                                        If isInteger AndAlso user IsNot Nothing Then
                                            Return View(user)
                                            Return HttpStatusCode.NotFound
                                        End If
                                    End Function
    End Sub
End Class

End Namespace``` The one important line is the RequiresAuthetication. And that will make it work. Simple.

Now we just need our views.

But you can find all those on the github thing, together with the models.


So now that you got all that working it is time to write our tests… and from this point forward you can start writing tests first.

If we take the way we have been testing our modules before we will get a 401. But we needed to test that anyway.

Here is one such test in complete isolation, just for you.

vbnet <Test()> Public Sub IfPlantWithId2ReturnsWebpagePlantWithId2() Dim loggedInBrowserResponse As BrowserResponse Dim formsAuthenticationConfiguration = New FormsAuthenticationConfiguration() With { .RedirectUrl = "~/login", .UserMapper = New FakeUserMapper(New UserService()) } Dim configuration = A.Fake(Of IRazorConfiguration)() Dim bootstrapper = New ConfigurableBootstrapper(Sub(config) config.Module(Of UsersModule)() config.Module(Of LoginModule)() config.ViewEngine(New RazorViewEngine(configuration)) config.RequestStartup(Sub(x, pipelines, z) FormsAuthentication.Enable(pipelines, formsAuthenticationConfiguration) End Sub) End Sub) loggedInBrowserResponse = New Browser(bootstrapper2).Post("/login", Sub(x) x.HttpRequest() x.FormValue("Username", "Chris1") x.FormValue("Password", "123") End Sub) Dim result = loggedInBrowserResponse.Then.Get("/users/00000000-0000-0000-0000-000000000004", Sub(x) x.HttpRequest() End Sub) Assert.AreEqual("00000000-0000-0000-0000-000000000004", result.BodyAsXml.Descendants("td")(1).Value) End Sub As you can see I had to add FormsAuth to my custom bootstrapper.

I then did a post with some correct credentials.

And then we do a get of a user. And checked if that data was in the response.

Also make sure you add the LoginModule to your bootstrapper

Simples, once you know how.


This seems like a lot of code, but once you have it set up it’s pretty much ok.

Next post I will explain how to get the login/logout link to appear on each page. Next post after the next post that is. Because the next post is post 500 and that is special like me.