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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With