I am trying to implement a very simple example of FormsAuthentication. It is not real life but it has thrown up a problem. The AuthenticationService, which is intended to be an Application level singleton, appears to be instantiated twice.
Here is the code:
public class User : IUserIdentity
{
public string UserName { get; set; }
public IEnumerable<string> Claims { get; set; }
}
public interface IAuthenticationService
{
Guid GetIdentifier(string username, string password);
}
public class AuthenticationService : IUserMapper, IAuthenticationService
{
public readonly Guid Identifier = Guid.NewGuid();
private readonly string Username = "admin";
private readonly string Password = "x";
public Guid GetIdentifier(string username, string password)
{
return (username == Username && password == Password) ? Identifier : Guid.Empty;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
return (identifier == Identifier) ? new User { UserName = "admin" } : null;
}
}
public class MyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<IAuthenticationService, AuthenticationService>().AsSingleton();
}
}
The code above is being used by my LoginModule as follows. Please note that I am injecting the application-level singleton instance of the AuthenticationService via the module’s constructor.
public LoginModule(IAuthenticationService authenticationService)
{
Post["/login"] = _ =>
{
var identifier = authenticationService.GetIdentifier(
(string) Form.Username,
(string) Form.Password);
if (identifier.IsEmpty())
{
return Context.GetRedirect("~/login?error=true");
}
return this.LoginAndRedirect(identifier);
};
}
What should happen is that when the user POSTs their username and password, these are checked by the AuthenticationService via the GetIdentifier(..) method. If the credentials match then the single GUID identifier is returned. This GUID will always be the same because it is created as a readonly field and thus set once when the singleton AuthenticationService is first instantiated at application startup.
However this is not the case. Instead two distinct instances of the AuthenticationService are created, one that is injected into the LoginModule constructor and used to call the GetIdentifier(..) method and another instance which Nancy uses to call the IUserIdentity.GetUserFromIdentifier(..) method.
These two instances have different GUID identifiers and so the GetUserFromIdentifier(..) method always return null.
I have tested a standard singleton service that does not implement IUserMapper and it works as expected, only one instance is created.
So it seems that Nancy is instantiating the IUserMapper singleton twice, once for its own internal use during FormsAuthentication, and once to inject into my LoginModule constructor!
Can you spot my mistake?
Thanks
It’s probably because you’re using a different interface so you have one singleton for things requesting IUsernameMapper and another for things requesting IAuthenticationService.
You can either:
My I ask why you’re doing any of this though rather than just using the built in forms auth?