5
votes

I want to use ASP.NET SimpleMembership to authenticate users that consume my WebAPI. Thinktecture has a wonderful authentication library called Thinktecture.IdentityModel (http://thinktecture.github.com/Thinktecture.IdentityModel.45/) with an example that ties Forms Auth with Basic Auth (source). However, the example uses Membership.ValidateUser() which doesn't work without a ASP.NET Membership provider, which isn't supported by SimpleMembership (source) (edit: this isn't entirely true, see Mark's answer below).


Edit:

Here's what I did:

1) Create a new MVC Internet Application

2) Install Thinktecture.IdentityModel via NuGet

3) Create a model and an api controller via scaffolding:

public class Goober
{
    public int GooberId { get; set; }
    public string GooberWords { get; set; }
}

4) Ran the project, created a new user and created a new Goober using Fiddler

5) Added [Authorize] to GetGoober(int id)

6) In WebApiConfig.cs added:

var authConfig = new AuthenticationConfiguration();

authConfig.AddBasicAuthentication((userName, password) =>
    Membership.ValidateUser(userName, password));

config.MessageHandlers.Add(new AuthenticationHandler(authConfig));

When I run the project and hit api/goober/1 with Fiddler I get a 401 www-Authenticate: unspecified. But if I log in first using the AccountController then use Fiddler I get a 200 and everything is peachy.

Edit

Okay, I think the problem isn't related to my initial question. I suspect it's related to the initialization of SimpleMembership in the template. When I open the project and run debug then hit the api with Fiddler I can't get past Auth. But when I simply click the "register" link on the web frontend I get past Auth. I'm guessing it's because the InitializeSimpleMembershipAttribute is called at the AccountController so doesn't initialize until the controller is called?


I've tried using WebSecurity.Login() in the place of Membership.ValidateUser() but that doesn't work.

I'm at a loss on how to actually implement this. Does anyone have any advice? Or maybe I'm attempting to tackle this problem from the wrong angle?

1

1 Answers

3
votes

You are correct that ASP.NET Membership provider is not compatible with the SimpleMembershipProvider however SimpleMembershipProvider does support ValidateUser, see here. Assuming SimpleMembership is correctly configured and initalised you should still be able to call Membership.ValidateUser().

If you have already tried Membership.ValidateUser() and got an error please let me know and we can try resolve it.

Update

So having followed your reproduction steps I have managed to pin-point an error. Having brought the Thinktecture AuthenticationHandler handler inline and run in debug. 30 seconds after a request to the api controller a database connection error is being raised. This is failing asynchronously and silently.

After some fiddling around I believe it is the DefaultConnection connection string which is at fault.

Like me your default connection probably contains a file name like this: AttachDBFilename=|DataDirectory|\aspnet-MvcApplication3-20121215104323.mdf"

When ValidateUser is called inside the delegate registered at app start up (for validating the credentials) it appears to be failing to resolve |DataDirectory| I found that by updating this path to the full name my connection problems went away.

<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MvcApplication3-20121215104323;Integrated Security=SSPI;AttachDBFilename=C:\mydatabase\file\path\example\aspnet-MvcApplication-20121215104323.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>

I then found this post here, it indicates that the AppDomain has not had it's data directory set correctly at this point.

So once the config set up and the connection string altered with a proper file path and a user name "test" and a password "testing" this request through fiddler got a 200:

GET http://localhost/api/goober/1
User-Agent: Fiddler
Host: localhost
Authorization: Basic dGVzdDp0ZXN0aW5n

As an aside

I found that to get the forms authorisation token to also allow access to the api controllers I had to add this. Otherwise the Thinkteckture code sets the principle back to anonymous:

Add this

authConfig.InheritHostClientIdentity = true;

To counteract this (line 52):

if (_authN.Configuration.InheritHostClientIdentity == false)
{
   //Tracing.Information(Area.HttpAuthentication, "Setting anonymous principal");
   SetPrincipal(Principal.Anonymous);
}