How to get ServiceStack authentication to work? (w

2019-04-06 15:20发布

问题:

We have hired a contractor who is writing an iPhone app for us, and I'm starting to write the backend service for it with ServiceStack.

I'm struggling with authorization in general: what kind of authorization to use and how to implement it.
I don't know much about ServiceStack, HTTP and authorization (yet).. I've read this, but I'm probably still doing something wrong.

I will use usernames and passwords from an existing legacy database to authenticate (but nothing else - no registering of new users, no different permissions. Just authenticating).
So I need to write my own provider.

I've managed to implement a working CredentialsAuthProvider with the help from this tutorial.
It works when I test it in the browser:

  • call my service and get a 401
  • POST to auth/credentials
  • call my service again and get the correct result

However, I noticed that the same workflow doesn't work when I try it in Fiddler.

The POST to auth/credentials works. I POST this:

POST http://localhost:52690/auth/credentials?format=json HTTP/1.1
User-Agent: Fiddler
Host: localhost:52690
Content-Length: 74
Content-Type: application/json; charset=utf-8

{
  "UserName": "chspe",
  "Password": "xyz",
  "RememberMe": true
}

...and get this:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Fri, 14 Jun 2013 13:07:35 GMT
X-AspNet-Version: 4.0.30319
X-Powered-By: ServiceStack/3,949 Win32NT/.NET
Set-Cookie: ss-id=3YUUgfwIeJd7PedFK5Th; path=/; HttpOnly
Set-Cookie: ss-pid=zQJ5Z4Vq7AY+BpVwbttj; expires=Tue, 14-Jun-2033 13:07:35 GMT; path=/; HttpOnly
Set-Cookie: ss-opt=perm; expires=Tue, 14-Jun-2033 13:07:35 GMT; path=/; HttpOnly
Set-Cookie: X-UAId=; expires=Tue, 14-Jun-2033 13:07:35 GMT; path=/; HttpOnly
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 75
Connection: Close

{"sessionId":"zQJ5Z4Vq7AY+BpVwbttj","userName":"chspe","responseStatus":{}}

Looks good to me.
But then the call to my actual service still returns a 401:

GET http://localhost:52690/hello/world?format=json HTTP/1.1
User-Agent: Fiddler
Host: localhost:52690
Content-Length: 0
Content-Type: application/json; charset=utf-8

Response:

HTTP/1.1 401 Unauthorized
Server: ASP.NET Development Server/10.0.0.0
Date: Fri, 14 Jun 2013 13:07:44 GMT
X-AspNet-Version: 4.0.30319
WWW-Authenticate: credentials realm="/auth/credentials"
X-Powered-By: ServiceStack/3,949 Win32NT/.NET
Cache-Control: private
Content-Length: 0
Connection: Close

(this is the HelloService from the ServiceStack.Host.AspNet package, I just added the [Authorize] attribute)

The actual request is correct, because the very same call works when I remove the [Authorize] attribute.

I noticed that the CredentialsAuthProvider seems to work with cookies (there are several Set-Cookie: ... lines in the first response).

First question: Is the CredentialsAuthProvider even the right choice for a client that's not a browser?

Apparently Fiddler does not recognize the cookies, how do I know if an iPhone (or any other mobile device) would?

Next, I tried to use Basic Authentication instead.
Here is my BasicAuthProvider:

public class MyBasicAuthProvider : BasicAuthProvider
{
    public override object Authenticate(IServiceBase authService, IAuthSession session, Auth request)
    {
        if (request.UserName == "MyUser")
        {
            return true;
        }
    }
}

But I'm stumped - I don't even get this to work from the browser.

When I load the URL of my service in the browser, a window pops up and asks for username and password.
I enter the correct username and press Enter, and the same window pops up again immediately. And again, and again...and so on, no matter how often I enter the (correct) data.

However, I can see that ServiceStack actually uses my MyBasicAuthProvider, because when I set a breakpoint in Visual Studio, I see that it recognizes the username and returns True.

Second question: What am I doing wrong?

Do I need to do something more to get working Basic Auth with my own database? Isn't overriding Authenticate enough?

回答1:

I'll try to answer the ServiceStack end of your questions but I think you need to look into handling authentication over HTTP as well as how iPhone apps handle HTTP requests/response. Also, I think this provides some insight into how ServiceStack handles authentication.

Is the CredentialsAuthProvider even the right choice for a client that's not a browser?
I think you can use Credentials or Basic. You need some type of Custom Authentication since you have usernames/passwords in your own database (you can subclass either CredentialsAuthProvider or BasicAuthProvider and override TryAuthenticate). The difference between the two is where you would like the client (in this case the iPhone app) to put the username/password in the HTTP request to your Service. With Credentials it's part of the body. With Basic it's part of the Authorization Header. On the 'server side' ServiceStack abstracts what you need to do into the *Provider class/code you choose.

However, I noticed that the same workflow doesn't work when I try it in Fiddler.
Correct. Fiddler does not hold onto Session cookies and send them along in subsequent requests (ie. like a browser does). You would need to 'manually' supply them when using Fiddler.

Second question: What am I doing wrong?
I don't think ServiceStack has anything that launches a 'popup window' requesting authentication. This sounds like an 'Integrated Windows Authentication' issue. Not really sure, though.