Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ServiceStack Web Service with Basic Authentication and SetCredentials

Right now I'm playing with ServiceStack and its Authentication and authorization support.

Working on the CustomAuthenticationMvc (the Web Service Host app) from the Use Cases project and while trying to understand how the authentication works, I did this:

public class AppHost : AppHostBase
{
    public AppHost() : base("Basic Authentication Example",
                                                        typeof(AppHost).Assembly) { }

    public override void Configure(Container container)
    {
        // register storage for user sessions 
        container.Register<ICacheClient>(new MemoryCacheClient());
        container.Register<ISessionFactory>(c => new SessionFactory(
                                            c.Resolve<ICacheClient>()));

        //Register AuthFeature with custom user session and Basic auth provider
        Plugins.Add(new AuthFeature(
            () => new CustomUserSession(),
            new AuthProvider[] { new BasicAuthProvider() }
        ) );

        var userRep = new InMemoryAuthRepository();
        container.Register<IUserAuthRepository>(userRep);

        //Add a user for testing purposes
        string hash;
        string salt;
        new SaltedHash().GetHashAndSaltString("test", out hash, out salt);
        userRep.CreateUserAuth(new UserAuth
        {
            Id = 1,
            DisplayName = "DisplayName",
            Email = "[email protected]",
            UserName = "john",
            FirstName = "FirstName",
            LastName = "LastName",
            PasswordHash = hash,
            Salt = salt,
        }, "test");
    }
}

The service implementation is the famous Hello World one with the [Authenticate] attribute applied right above the Request DTO...

[Authenticate]
[Route("/hello")]
[Route("/hello/{Name}")]
public class HelloRequest : IReturn<HelloResponse>
{
    public string Name { get; set; }
}

public class HelloResponse
{
    public string Result { get; set; }
}

public class HelloService : IService<HelloRequest>
{
    public object Execute(HelloRequest request)
    {
        return new HelloResponse
        {
            Result = "Hello, " + request.Name
        };
    }
}

Using a Console Application as a Client, I have this code:

static void Main(string[] args)
{   
    // The host ASP.NET MVC is working OK! When I visit this URL I see the
    // metadata page.
    const string BaseUrl = "http://localhost:8088/api";

    var restClient = new JsonServiceClient(BaseUrl);

    restClient.SetCredentials("john", "test");

    HelloResponse response = restClient.Get<HelloResponse>("/hello/Leniel");

    Console.WriteLine(response.Result);
}

The problem here is that I don't think the restClient.SetCredentials("john", "test"); is working as I would expect because the webservice call restClient.Get<HelloResponse>("/hello/Leniel"); is throwing an exception. I see that I'm being redirected to the login page... I thought that setting the credentials it'd be alright to call the web service. It's not the case here.

What am I doing wrong? I plan to call this web service from a MonoTouch iOS app where the user will type his username and password in a simple dialog window, but before doing this I'd like to get it working in this simple console app.

EDIT 1

I tried this code to send the auth request (as seen on this thread):

var authResponse = restClient.Send<AuthResponse>(new Auth
{
    UserName = "john", 
    Password = "test",
    RememberMe = true, 
});

but an exception is thrown:

Type definitions should start with a '{', expecting serialized type 'AuthResponse', got string starting with:

<head>

Looks like I have a miss-configured service because it keeps redirecting me to the default ASP.NET MVC Login page (see the Fiddler screenshot below). I also changed the AuthFeature registration passing null to HtmlRedirect.

//Register AuthFeature with custom user session and Basic auth provider
Plugins.Add(new AuthFeature(
    () => new CustomUserSession(),
    new AuthProvider[] { new BasicAuthProvider() }
) {  HtmlRedirect = null });

EDIT 2

Playing with Fiddler I tried adding an Authorization request header as described here:

enter image description here

Looking at the Auth inspector in Fiddler I can see this:

Authorization Header is present: Basic am9objp0ZXN0
Decoded Username:Password= john:test

Now, why am I getting the 401 unauthorized status? Shouldn't it just work given that I set up a user in memory for testing purpose?

How can I pass the request header in my sample console app?

If I do in the console app:

restClient.AlwaysSendBasicAuthHeader = true;

I get the 401 unauthorized response...

EDIT 3

With the help of desunit's answer and code additions, I managed to get the Console App authentication working with the following code:

class Program
{
    const string BaseUrl = "http://localhost:8088/api";

    static void Main(string[] args)
    {
        var restClient = new JsonServiceClient(BaseUrl);

        restClient.SetCredentials("john", "test");

        restClient.AlwaysSendBasicAuthHeader = true;

        HelloResponse response = restClient.Get<HelloResponse>("/hello/Leniel");

        Console.WriteLine(response.Result);
    }
}

//Response DTO
//Follows naming convention
public class HelloResponse
{
    public string Result { get; set; }
}

I'd like to know the following now: In a Console App should I always send the Basic Auth Header? If I remove that line, the authentication just fails and I do not get the HelloResponse.

like image 987
Leniel Maccaferri Avatar asked Nov 26 '12 21:11

Leniel Maccaferri


1 Answers

The problem is not related to ServiceStack itself but to how ASP.NET web handles 401 error code. You can workaround it with custom IHttpModule which suppresses redirect to Login form. More details about that problem could be read here.

Since it's quite common problem we have updated CustomAuthenticationMvc project to demonstrate possible solution.

like image 122
desunit Avatar answered Nov 15 '22 07:11

desunit