Cogslave
Cogslave

Reputation: 2643

Forms Authentication with NancyFx

I am using NancyFx to make a simple website were users can login and out using an ajax control.

I have read the documentation on Forms Authentication with Nancy and I think I have completed all the required steps.

  1. Install the Nancy.Authentication.Forms package
  2. Implement an IUserMapper
  3. Implement routes to handle login and logout
  4. Configure and enable Forms Authentication

I am having an issue where after calling login, requests do not have a current user set. I can see a cookie set after the login is executed. However the user mapper is not getting called. I have tried requesting routes with and with out the this.RequiresAuthentication(); and still no user.

Here is my Bootstrapper Implementation for step 4

public class Bootstrapper : DefaultNancyBootstrapper
{
    protected override void ConfigureApplicationContainer(TinyIoCContainer container)
    {
        container.Register<ISessionFactory>((c, p) => SessionFactory.Factory);
    }

    protected override void ConfigureConventions(NancyConventions conventions)
    {
        base.ConfigureConventions(conventions);

        conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("assets", @"content/assets"));
        conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("application", @"content/application"));
    }

    protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
    {
        base.ConfigureRequestContainer(container, context);
        container.Register<IUserMapper, UserMapper>();
    }

    protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
    {
        base.RequestStartup(container, pipelines, context);

        var formsAuthConfiguration =
            new FormsAuthenticationConfiguration()
            {
                RedirectUrl = "~/",
                UserMapper = container.Resolve<IUserMapper>()
            };

        FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
    }
}

Here is my login & logout logic for step 3.

Post["/login"] = x =>
        {
            //Verify here, hardcode for testing
            string email = "[email protected]";

            User user = ExecuteCommand(new FetchUser(email));

            this.LoginWithoutRedirect(user.Session);
            return new { email = user.Email, authorized = true, status = "okay" };
        };

        Post["/logout"] = x =>
        {
            return this.Logout("~/");
        };

My IUsermapper simply looks up a user from a database with the given id. I can see it gets constructed when the RequestStartup resolves the IUserMapper but then there are never any calls to the get GetUserFromIdentifier function.

Any help would be great.

Upvotes: 2

Views: 2188

Answers (1)

biofractal
biofractal

Reputation: 19123

The GetUserFromIdentifier method is not being called because you are using the LoginWithoutRedirect extension. It is not the login that calls GetUserFromIdentifier but rather any subsequent redirect.

A more usual way of doing things would be:

string email = "[email protected]";
User user = ExecuteCommand(new FetchUser(email));
this.LoginAndRedirect(user.Session);

It is not expected that the login route would be accessed directly. Instead the user would normally request a protected resource, be authenticated and then redirected to the requested resource.

A couple of other points:

When I tried your code I got an error returning an anonymous type. Instead I needed to return the type as json, like this:

this.LoginWithoutRedirect(user.Session);
return Response.AsJson(new
{
    email = user.Email,
    authorized = true,
    status = "okay"
});

This works fine, it logs in and returns your anonymous type as a json object and since there is no redirect then, as expected, it does not call GetUserFromIdentifier.

Finally, your /logout route should be protected using this.RequiresAuthentication(). It makes sense because only authenticated users need to logout. It will also protect you when GetUserFromIdentifier returns null - perhaps because a cached user has been timed out. RequiresAuthentication detects this null user and redirects back to Get["/login"].

Upvotes: 1

Related Questions