Cookies starting with ss- not set after successful authentication from .NET Core 3.1 based REST service

I am migrating a retro ServiceStack .NET framework 4.8 REST service project to .NET Core 3.1. I am using custom authentication. Now after copying the relevant parts not any authentication cookies are set in the browser. On the server I see a stored session, so this part is working.
My code:

–> AppHost:

public class AppHost : AppHostBase
{
public AppHost() : base(“Something.Services”, typeof(SomeService).Assembly) { }

    // Configure your AppHost with the necessary configuration and dependencies your App needs
    public override void Configure(Container container)
    {
        SetConfig(new HostConfig
        {
            UseSameSiteCookies = true,
            UseSecureCookies = true,
            DefaultRedirectPath = "/metadata",
            DebugMode = AppSettings.Get(nameof(HostConfig.DebugMode), false)
        });

        Plugins.Add((new AuthFeature(() =>
                new CustomUserSession(),
            new IAuthProvider[] {
                new CustomAuthProvider(),
            }, "http://www.somesite.com/login")));
    }

–> CustomUserSession:

    public class CustomAuthProvider : AuthProvider
    {
        public CustomAuthProvider()
        {
            this.Provider = "Gloneco Custom AuthProvider";
        }

        public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
        {
            return session.IsAuthenticated;
        }

        public override Task<object> AuthenticateAsync(IServiceBase authService, IAuthSession session, Authenticate request, CancellationToken token = new CancellationToken())
        {
            throw new NotImplementedException();
        }
    }

    [DataContract]
    public class CustomUserSession : AuthUserSession
    {
        [DataMember]
        public string Something { get; set; }
    }

–> Validation REST Method (stripped code):

  public object Get(ValidateSomethingRequest request)
  {
        //BUG: No cookies!
        base.Request.AddSessionOptions(SessionOptions.Permanent);
        var session = base.SessionAs<AuthUserSession>();
        session.IsAuthenticated = true;
        session.Roles = new List<string>();
        session.Roles.Add(user.Role);
        this.SaveSession(session, new TimeSpan(_serviceAppSettings.AuthenticationExpirationDays, 0, 0, 0, 0));
  }

I checked everything like correct namespace of ‘public AppHost() : base(“Something.Services”, typeof(SomeService).Assembly) { }’ but I am missing something I guess.

SIDENOTE:
VS complains that the ‘SaveSession’ is obsolete and I should use ‘SaveSessionAsync’. I have looked and even tried an mvcauth example from your latest .NET 5 template ‘x’ tool but could not find an async sample for a ‘object Get’. Or is it as simple as ‘async Task< object > Get’?

You should start with a more useful Auth Provider like CredentialsAuthProvider and you don’t want to throw a NotImplementedException. If you want to customize the session override AuthenticateAsync() then return base.AuthenticateAsync() to execute normal behavior like raising the appropriate Session & Auth Events or null if you don’t want any default behavior.

I’m not sure what the purpose of ValidateSomethingRequest is, but any modifications to the Session like AddSessionOptions() should be done either before or during Authentication. Changing the session options will get ServiceStack to look at the wrong Cookie which the Session is saved against.

You your Get() method can return a Task<object> to make the Service async or from v5.10 it can optionally have an *Async suffix.

If it’s still an issue please post the HTTP Request/Response headers of the requests so we can actually verify what Cookies is being sent, if you have the AuthFeature registered the Cookies should be created automatically, there’s something wrong if there isn’t.

Thanks for the quick reply. To give some background information:

ValidateSomethingRequest does a SMS based login check. I do not use password authentication because the users are of older age and do not have Facebook or Linkedin like logins. So a user enters the received SMS code which I check in ValidataSomethingRequest and then use the above code to login ‘into ServiceStack’.

I created an exact 1 to 1 copy of the 4.8 version which works with the above code for years.
I always use a NotImplemented exception for things I do not use or not quite understand (yet).

But I will have a look into your suggestions.

Final note: It is a Blazor frontend talking to the ServiceStack REST service. Both are running in IIS and the ServiceStack REST services are running as an application below the Blazor site.

And I found an interesting difference:
If I directly call the REST service from the browser I see the cookies, example:

www.somesite.com/services/smsauthentication/validate/{someparameter1}/[smscode}

But via Blazor not. I am using the json service client:

  var result = await _jsonServiceClient.GetAsync(new ValidateSomethingRequest() { SomeParameter1 = "abcdefg", SmsCode = smsCode });

If you’re doing a custom solution you should provide exactly what you’re doing as these posts and code samples are highly confusing which makes hard when trying to identify what’s actually happen before I’m able to help identify & resolve issues. Throwing NotImplementedException means your Custom AuthProvider does not allow Authentication via a normal Authenticate Request, so I don’t see how it could ever work. Then you have a ValidateSomethingRequest which looks off named as it looks like it’s doing your own Authentication, but I don’t understand what populated the original session that you’re retrieving with SessionAs<AuthUserSession>()? Switching over to use the permanent ss-pid Cookie with SessionOptions.Permanent before retrieving the session is confusing as well it could only work if the Session was saved against the permanent Cookie previously even though the Request is marked to use the temporary ss-id Cookie.

You can read about the Async Auth Provider changes in the latest v5.10 Release, e.g. you can use continue to use your previous sync Auth Providers with AuthProviderSync. But I don’t see how your code samples were supposed to work so I’m not sure if using the existing sync Auth Provider would make a difference. Maybe you’re relying on a prior behavior of new Authentication Requests not generating new Cookies which you can revert with AuthFeature.GenerateNewSessionCookiesOnAuthentication=false but then you don’t appear to be able to authenticate normally so maybe that’s not going to have any effect.

You also always need to specify if you’re using Blazor in each Post, esp. Blazor WASM which is a crippled .NET Runtime which has limited support & capabilities, e.g. the HttpClient is a shim over W3C’s JS fetch API (i.e. not the real HttpClient library implementation), you can refer to this previous answer on how to sent fetch’s credentials:‘include’ in the Blazor JsonHttpClient which may or may not resolve your issue.

My code was based on a solution posted 5 years ago (sorry for my nickname, at that time I did not know in English it means something different than in dutch :wink: ), link:

Next time I will try to be as thorough as possible in my questions. I will try the suggestions you are proposing and if it fails I will write a simple stripped down Blazor Server example.

So you’re basically implementing Custom Authentication in your Service & not the Custom Auth Provider.

To force using the permanent session Cookie ss-pid I would add a Global Request Filter so the Cookie is initialized the same time as the ss-id/ss-pid cookies, e.g:

GlobalRequestFilters.Add((req, res, dto) => {
    if (!req.IsPermanentSession())
        req.AddSessionOptions(SessionFeature.PermanentSessionId);
});

If ValidateSomethingRequest is what you’re using to authorize the request then SessionAs<T> will be returning a new session. Although I’d now recommend generating the cookies with GenerateNewSessionCookiesAsync so it saves the new session under new cookies, e.g:

public async Task<object> Get(CustomAuth request)
{
    var newSession = Request.SessionAs<AuthUserSession>();
    newSession.IsAuthenticated = true;
    newSession.Roles = new List<string> {
        "user.role"
    };
    await Request.GenerateNewSessionCookiesAsync(newSession);
    await Request.SaveSessionAsync(newSession);
    //...
}

I don’t think this will change the behavior that will resolve your issue which I’m assuming is due to the Blazor client not sending Cookies, but this would be the recommended approach for saving a new Session with a Custom method.

Thank you and you are correct, and again sorry for posting too much incomplete info.
In the meantime this Blazor newbie already red some postings about Blazor Server (so I am not using WASM) being websocket based and there seems to be no httpcontext. Now I get the eerie feeling it is not possible at all to authenticate this way because the cookies are not send/received. I will dive further into this and post a solution if I can find one.

And I found a very interesting github project which I can use for my project, link:

Maybe this helps anyone else: I am using a very simple (temporary) solution and was very easy to implement. I use One Time Passwords to authenticate each request. So everything is custom built and do not need to use ServiceStack and .NET core authentication mechanisms. This is possible because my rest services and Blazor Server itself executes its code on the server.