Daniele Dalla Valle
Daniele Dalla Valle

Reputation: 13

ServiceStack, authentication and passing session header with request

I need to validate a user against an application with custom UserName and Password. The credentials are compared with those in database and then the user can be authorized.

I configured my AppHost adding the plugin for authentication:

Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[]{
            new CredentialsAuthProvider()
}));

I have decorated the my DTO with [Authenticate] attribute

I then created a service to handle the Authenticate call:

    public AuthenticateResponse Any(Authenticate request = null)
    {

        var response = new AuthenticateResponse();

        // code to get user from db
        //...

        // check if credentials are ok
            if (passInDB == request.Password)
            {
                var session = this.GetSession();

                session.IsAuthenticated = true;
                session.UserName = userFromDBEntity.Username;
                response.UserId = userFromDBEntity.ID.ToString();
            }

        return response;
    }

In the client app I created a call to the service to provides me authentication:

        AuthenticateResponse authResponse = client.Post(new Authenticate
        {
            provider = Axo.WebServiceInterface.AxoAuthProvider.Name, //= credentials
            UserName = username,
            Password = password,
            RememberMe = true
        });

Then, still in the client, I have written something like:

            if (authResponse.UserId != null)
            {
                client.AlwaysSendBasicAuthHeader = true;
                client.SessionId = authResponse.SessionId;
            }

..with the hope to get aware the client that now I am an authenticated user, but after debugging to death I'm still having an UNAUTHORIZED Exception.

I am able to reach the Authenticate Service I created, and check the credentials against the db, but after that it seems the jsonclient needs something more than "SessionId" to know that it is authenticated, because I get the error for any other request. I suppose that headers are missing something.

I read a lot of posts, and I tried also to define my custom AuthProvider and then override TryAuthenticate to see if may be helpful (for someone it was) but the method doesn't even get fired..

Upvotes: 1

Views: 1381

Answers (1)

mythz
mythz

Reputation: 143319

There's an example of using ServiceStack's Authentication to implement a Custom Auth Provider by inheriting CredentialsAuthProvider and overriding TryAuthenticate() to determine whether the userName/password is valid and OnAuthenticated() to populate the Users IAuthSession with info from the existing DB:

public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
    public override bool TryAuthenticate(IServiceBase authService, 
        string userName, string password)
    {
        //Add here your custom auth logic (database calls etc)
        //Return true if credentials are valid, otherwise false
    }

    public override IHttpResult OnAuthenticated(IServiceBase authService, 
        IAuthSession session, IAuthTokens tokens, 
        Dictionary<string, string> authInfo)
    {
        //Fill IAuthSession with data you want to retrieve in the app eg:
        session.FirstName = "some_firstname_from_db";
        //...

        //Call base method to Save Session and fire Auth/Session callbacks:
        return base.OnAuthenticated(authService, session, tokens, authInfo);

        //Alternatively avoid built-in behavior and explicitly save session with
        //authService.SaveSession(session, SessionExpiry);
        //return null;
    }
}

Then to get ServiceStack to use your AuthProvider you need to register it with the AuthFeature plugin, e.g:

//Register all Authentication methods you want enabled for this web app
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
    new IAuthProvider[] {
        new CustomCredentialsAuthProvider(), 
    }
));

If everything's configured correctly you'll then be able to Authenticate with any of the Service Clients, e.g:

var authResponse = client.Post(new Authenticate
{
    provider = "credentials",
    UserName = username,
    Password = password,
    RememberMe = true
});

If successful this will return a populated authResponse, the ss-id/ss-pid Session cookies will also be populated on the client instance which will then let you call AuthOnly Services that are protected with [Authenticate] attribute.

Don't implement Authenticate Service

You never want to implement your own Any(Authenticate request) which ServiceStack already implements. The way to plug into ServiceStack's Authentication is to use a custom provider shown above. You can instead choose to ignore ServiceStack's Authentication in which case you should implement your own Custom Authentication Service but you should not use the existing Authenticate DTO's or [Authenticate] attribute which are apart of ServiceStack's Authentication support and assume that you're calling a registered AuthProvider.

Request DTO's are never nullable

Although unrelated, you also never want to make your Request DTO's nullable, e.g. Any(Authenticate request = null). ServiceStack will always call your Services with a populated Request DTO, or an empty one if no parameters were passed, it will never call your Service without a Request DTO or with a null Request DTO.

Upvotes: 1

Related Questions