I\'m trying to get social authentication working in an asp.net-hosted Nancy web app using the WorldDomination SimpleAuthentication plugin for Nancy. TL;DRs skip to the question
With Simple Authentication, we simply handle the authentication with a provider in a simple way. Every provider has slightly different implementations, different naming, different promises, so we can to consolidate all that into Simple Authentication and make it easier for a developer to implement into their website.
Thats why the Nancy.SimpleAuthentication package exists. Because we know how Nancy works we have simplified the integration into Nancy by creating the modules for you to handle redirection, authentication callback, etc.
The problem is, we simply do not know how you authenticate a user against your website.
We can handle the whole forms auth scenario ourselves, and I actually plan to in the future. (have to implement claims first which I'm 60% way through), but it will still at bare minimum require you to implement the IAuthenticationCallbackProvider
public class Test : IAuthenticationCallbackProvider
{
public dynamic Process(
NancyModule nancyModule,
AuthenticateCallbackData model)
{
//Query for the database user based on the Provider / Id
//Authenticate the user
//Call LoginWithoutRedirect, and redirect how you want...
// or LoginWithRedirect
return nancyModule.Negotiate.WithView("AuthenticateCallback")
.WithModel(model);
}
}
This class is required in order for you to authenticate the user against your database.
The thing we thought about tho is 95% of the time the user putting in the authentication, most likely already has some form of authentication already. Usually Forms Auth.
So assuming you've pulled in SimpleAuthentication, and wired up your IAuthenticationCallbackProvider
class. All you really need to do is implement the Forms Auth stuff, which is pretty much 1 class, and a method call.
In the provider you need to call the LoginWithoutRedirect
method so that Nancy can create an auth cookie.
Then you need to setup the IUserMapper
class to tell Nancy how to get the user from the Database. If you're using RavenDB this would look something like:
public class DatabaseUser : IUserMapper
{
public IDocumentStore DocumentStore { get; set; }
public DatabaseUser(IDocumentStore documentStore)
{
DocumentStore = documentStore;
}
public IUserIdentity GetUserFromIdentifier(
Guid identifier,
NancyContext context)
{
using (var session = DocumentStore.OpenSession())
{
var member = session.Query<Member>()
.SingleOrDefault(x => x.Identifier == identifier);
if (member == null)
return null;
return new UserIdentity
{
UserName = member.DisplayName,
Claims = new []
{
"NewUser",
"CanComment"
}
};
}
}
}
Configured in the bootstrapper like:
protected override void ConfigureRequestContainer(
TinyIoCContainer container,
NancyContext context)
{
base.ConfigureRequestContainer(container, context);
container.Register<IUserMapper, DatabaseUser>();
}
protected override void RequestStartup(
TinyIoCContainer container,
IPipelines pipelines,
NancyContext context)
{
base.RequestStartup(container, pipelines, context);
var formsAuthConfiguration = new FormsAuthenticationConfiguration
{
RedirectUrl = "~/login",
UserMapper = container.Resolve<IUserMapper>(),
};
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
}
And that's really it...
I personally don't think it's a lot of code that you have to write. Both Nancy and Simple Authentication have done most of the leg work for you :)
I hope we can make SimpleAuthentication even easier in the future by removing the need for the Forms Auth, but for now I think we have a pretty good solution.
Helpful links:
http://www.philliphaydon.com/2012/12/18/forms-authentication-with-nancyfx/
http://www.philliphaydon.com/2013/01/31/oauth-with-nancyfx-and-world-domination-authentication/
The 2nd link for World Domination, although there's a bit of renaming, it's mostly the same. I do plan to do an updated blog post and revamp the wiki when we have polished off Claims.
I hope that helps you.
Edit: