Issues after upgrading from ServiceStack 4.5.14 to 6.9

In ServiceStack version 4.5.14, we are using Authenticate.Continue property for logging out the logged in user account.

Our code on 4.5.14 version:

request.Continue = $"{config.EndSessionEndpoint}?post_logout_redirect_uri={HttpUtility.UrlEncode(redirectTo)}";

			return base.Logout(service, request);

In 6.9 I see the continue property is removed, and I have found the replacement is to use Authenticate.Meta property Authenticate Continue removed from this link.

But Meta is not helping us to redirect to log out user page as like continue property did.


            var _meta = new Dictionary<string, string>();
            _meta.Add("Continue", $"{config.EndSessionEndpoint}?post_logout_redirect_uri={HttpUtility.UrlEncode(redirectTo)}");

            return base.LogoutAsync(service, request, token);

Could you please help us out what will be correct way to redirect to logout endpoint with Meta property? or in 6.9 version do we have an alternative way to update this?

If the redirect url is consistent you can configure it in:

Plugins.Add(new AuthFeature() {
    HtmlLogoutRedirect = "...."
});

Otherwise the easiest way would be to populate it on the session.ReferrerUrl, i.e:

var session = service.GetSession();
session.ReferrerUrl = "...";

Thanks for the response. ReferrelUrl helped resolving the problem.

There is an another issue, We have a console application which calls servicestack apis for authentication and authorization when trying to run that application we are getting an unhandled exception:

Value cannot be null. Parameter name: provider
at ServiceStack.Auth.AuthenticateService.GetAuthProvider(String provider)
at ServiceStack.AuthUserSession.IsAuthorized(String provider)
at System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 predicate)
at ServiceStack.AuthenticateAttribute.d__12.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at ServiceStack.ServiceStackHost.d__435.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ServiceStack.ServiceStackHost.d__434.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ServiceStack.Host.RestHandler.d__14.MoveNext()”

Looks it is looking for provider parameter. Where we want to update this? Can you please help me with this stack trace and exception message and tell what we have missed?

It’s calling session.IsAuthorized(provider) to validate if there’s an AuthProvider that the session is authorized for:

Do you have any custom AuthProviders that don’t have its Provider populated?

We are using ServiceStack.Service for running this windows service.

[SlidingSessionFilter()]
[ValidateHttpAntiForgeryToken()]
[Authenticate()]
public class TrimServiceBase : ServiceStack.Service|

As per your comments, i think the [Authenticate()] attribute causing this problem. But in our previous ServiceStack version this was not happening.

On the startup of the console application, we have configured IAuthProvider like below:

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

To resolve this issue, what will be the solution? can you advise? If you need some more information please let me know.

What does your custom WindowsIntegratedAuth inherit? Does it have a populated Provider property? The Exception suggests it doesn’t.

WindowsIntegratedAuth inherit AuthProvider.

Below is the code reference for WindowsIntegratedAuth:

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

        public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
        {
			ClaimsPrincipal cp = ClaimsPrincipal.Current;

			var nm = cp.Identities.Last().Name;


			session.UserAuthName = nm;
            return true;
        }
    }

So what could be the provider value for Windows Integrated service? and where i need to update that provider value in the WindowsIntegratedAuth class?

You should keep it consistent with the built-in AspNetWindowsAuthProvider class which also provides Windows Auth authentication, e.g:

public class WindowsIntegratedAuth : AuthProvider
{
    public override string Type => "NTLM";
    
    public WindowsIntegratedAuth()
    {
        Provider = AuthenticateService.WindowsAuthProvider;
        Realm = "/auth/" + AuthenticateService.WindowsAuthProvider;
    }
    //...
}

Thanks for your quick responses, your recommended changes resolved the problem.

When doing further testing found another issue. We have different services for each Object, each service will help us to make POST, GET API calls.

When using POST calls, we will deserialize the http request using the below code:

On version 4.5.14:

ServiceStackHandlerBase.DeserializeHttpRequest(operationType, httpReq, httpReq.ContentType);

With latest version 6.9 changed to:

var tt = ServiceStackHandlerBase.DeserializeHttpRequestAsync(operationType, httpReq, httpReq.ContentType);
				tt.Wait();
				return tt;

With the above code, the tt.Wait(); is on continuous process, that doesn’t returning the value at all, the wait process is not getting completed. I almost waited for 3-5mins but no results.

I checked the source code, the method call CreateContentTypeRequestAsync inside the DeserializeHttpRequestAsync method causing this problem I guess. Can you please check from your end and let me know? Or is there any different API call we can use for this purpose? Please advise.

It sounds like an issue with use of .Wait in an this context, you should use await where possible. tt is also a Task<object> rather than your original code which would have returned the object. We don’t have any known issues. Change your use to avoid .Wait and .Result to use await keyword instead.

If you can create a reproduction of the issue with your code and how you are handling your specific use case we might be able to help more, however at this stage I’m not able to follow your description of how your application is using DeserializeHttpRequestAsync. The more code you can share, the more we will be able to help with your specific issue.

Thanks, I have changed my method to async await and it introduced the new problem when executing the POST request call we are getting an unhandled exception, below is the image reference with stack traces.

ServiceController.GetService method throws Unable to resolve service ‘Task`1’ error message. I am not sure the reason behind this failure.

We are sending the POST request for updating our object(Record Object).

 [Authenticate()]
        public virtual object Post(Record request)
        {
            if (request.NewType.HasValue && request.NewType != NewType.Default)
            {
                return updateVersionPartOrCopy(request);
            }
            return DoPost(request);
        }

We have included a request binder which will help to execute the deserialization process before executing the POST call:

apphost.RequestBinders.Add(
						mainObjectType,
						(Func<IRequest, object>)deserializeTrimMainObject
						.MakeGenericMethod(mainObjectType)
						.CreateDelegate(typeof(Func<IRequest, object>)));

we will deserialize the http request to the Record object using below code.

return await ServiceStackHandlerBase.DeserializeHttpRequestAsync(operationType, httpReq, httpReq.ContentType);

We are inherited ServiceStack.Service in our class for running these actions. Need to know the reason behind this error message and how i can resolve it?

Please let me know if you need more information or code from our end which will help.

I’m assuming it’s because your custom Request Binder is returning a Task<RequestDto> instead of a RequestDto.

Yes, the method return type is Task. But i can not have a void method for async await, its returning the required object only and not task. May be before await call completes, the exception is occurring? How can we overcome this? Below is the code reference

public static async Task<T> DeserializeTrimMainObject<T>(IRequest httpReq) where T : TrimMainObject, new()
{
	ILog Log = LogManager.GetLogger(typeof(TrimUtils));


	string methodData = null;
	var isFormData = httpReq.HasAnyOfContentTypes(MimeTypes.FormUrlEncoded, MimeTypes.MultiPartFormData);

	if (!isFormData)
	{
		using (MemoryStream tempStream = new MemoryStream())
		{
			httpReq.InputStream.CopyTo(tempStream);
			httpReq.InputStream.Position = 0;
			tempStream.Position = 0;

			using (var reader = new StreamReader(tempStream))
			{
				methodData = reader.ReadToEnd();
			}
		}
	}

	T mainObject;

	mainObject = await DeserializeHttpRequest(ServiceStackHandlerBase.GetOperationType(httpReq.OperationName), httpReq, httpReq.ContentType) as T;

	return mainObject;
}
private static async Task<object> DeserializeHttpRequest(Type operationType, IRequest httpReq, string contentType)
{
	var isFormData = httpReq.HasAnyOfContentTypes(MimeTypes.FormUrlEncoded, MimeTypes.MultiPartFormData);
	Dictionary<string, string> tempCollection = new Dictionary<string, string>();
return await ServiceStackHandlerBase.DeserializeHttpRequestAsync(operationType, httpReq, httpReq.ContentType);

}

The method can’t return a Task, it needs to return the Request DTO instance you want to convert it to.

What’s the purpose of this method?

Misspelled my previous comment. It wont return Task, it will return a instance of an object.

Purpose of this method: Our product is Document management system(Content Manager). We will create different kinds of objects and store it. To create and update each objects(for example: Record, Locations) we will deserialize the httprequest and convert it our Object with required details.

That’s not what the method signature says:

public static async Task<T> DeserializeTrimMainObject<T>(IRequest httpReq)  ...

I don’t really understand the purpose of the method and why you have to deserialize it again, but all serializers support sync deserialization, e.g:

var deserializer = HostContext.ContentTypes.GetStreamDeserializer(contentType);

Which you can use to avoid needing async Task<T> methods.

I was using async await to return the ServiceStackHandlerBase.DeserializeHttpRequestAsync(operationType, httpReq, httpReq.ContentType);

since above code causing different problems, I am using below approach for deserializing the httprequest to the object:

var deserializer = HostContext.ContentTypes.GetStreamDeserializerAsync(httpReq.ContentType);
            if (deserializer != null)
            {
                httpReq.AllowSyncIO();
                var instance = deserializer(ServiceStackHandlerBase.GetOperationType(httpReq.OperationName), httpReq.InputStream);
                instance.Wait();
                return instance.Result;
            }
			return null;

Along with your suggestion i also included two lines of code which will achieve what ServiceStackHandlerBase.DeserializeHttpRequestAsync does. Is this approach is the right way? Please confirm.

It is unclear what context your code is running or why which would enable us to give you better advice.

In general, again, using async methods in the way above isn’t ideal, you are much better to either use the sync options, or use the await keyword rather than .Wait and .Result. If this is unclear, here is your above rewritten using await.

var deserializer = await HostContext.ContentTypes.GetStreamDeserializerAsync(httpReq.ContentType);
if (deserializer != null)
{
    httpReq.AllowSyncIO();
    var instance = deserializer(ServiceStackHandlerBase.GetOperationType(httpReq.OperationName), httpReq.InputStream);
    return instance;
}
return null;

Can you provide specific error details, a stack trace and a way to reproduce the problem? We want to help, but it is difficult if we don’t have the required info.

Again, context about why you need to use DeserializeHttpRequestAsync (eg, what your application is trying to achieve) would enable us better to understand the nature of the problem to are seeing. Hope you can provide more info.

What our application trying to do:

We developed a web service using ServiceStack, which helps to send POST, GET requests and exposed an SDK in a way suitable for the communication.

We included a RequestBinder which helps to trigger whenever we send an POST request.

RequestBinder code:

apphost.RequestBinders.Add(
						mainObjectType,(Func<IRequest, object>)deserializeTrimMainObject.MakeGenericMethod(mainObjectType)
						.CreateDelegate(typeof(Func<IRequest, object>)));

We are trying to call this deserialize API, since the httprequest payload input stream contains required information and we will bind those data with the trimmainobject. Example object:

Public class TrimMainObject
{
     public string Id {get; set;}
     public object Container {get; set;}
}

the API ServiceStackHandlerBase.DeserializeHttpRequestAsync(operationType, httpReq, httpReq.ContentType); will help us to deserialize the request and return the Trimmainobject with updating the input data(Id and Container input datas).

Issues faced after using async await:

Since i need to use async await, the return type of the method i have changed from object to Task<object>, since the RequestBinder is expecting the return type object but instead returning Task<object> i am getting below error, also included stack trace image: Please refer:

Error Message: Unable to resolve service ‘Task’1’’

If still my explanation are unclear and you need more information, let me know.

Request Binders are used when you want to change the default binding ServiceStack uses to populate the Request DTO which is not clear that’s what you want to do since you’re just using ServiceStack to deserialize the request anyway.

Why do you need to deserialize the request yourself? i.e. Can you get what you want from the populated Request DTO and do whatever customizations you need from an async Global Request Filter?