How do we set the ServerSideEvents Subscription UserId?

We are trying to implement the ServerSideEvents and running into some issues getting the subscription.UserId to be the correct value. Currently, we get the following response when connecting to the SSE endpoint:

cmd.onConnect {"userId":"1","isAuthenticated":"true","displayName":"authenticated user","channels":"test","createdAt":"1533104783495","profileUrl":"https://raw.githubusercontent.com/ServiceStack/Assets/master/img/apps/no-profile64.png","id":"kHic8tbx5cKDJlAEFHVO","unRegisterUrl":"http://localhost:5000/secure/event-unregister?id=kHic8tbx5cKDJlAEFHVO","heartbeatUrl":"http://localhost:5000/secure/event-heartbeat?id=kHic8tbx5cKDJlAEFHVO","updateSubscriberUrl":"http://localhost:5000/secure/event-subscribers/kHic8tbx5cKDJlAEFHVO","heartbeatIntervalMs":"10000","idleTimeoutMs":"30000"}

We are using JWT tokens and implemented our own CognitoAuthProvider. We connect to SSE with the ss-tok query string, and we can intercept and set the session.UserAuthId, session.DisplayName, session.Username.

When the request lands in the SSE.OnCreated(), I can see the subscription has the correct DisplayName, Username, but not the UserId. Unfortunately, by this time in the pipeline, the UserId is read-only, and I’m unable to correctly set it.

I feel like we are 99% where we need to be and are just missing something simple. Any ideas?

The UserId is retrieved from your AuthUserSession.UserAuthId, when using JWT it’s stored in the sub field of the JWT payload.

Ahh Thank-you!

The next issue we are having is with regards to the IMessageProducer.Publish(). It appears I am getting a deserialization error. When I trace it back, it’s because the Publish() is serializing with type info included. I’ve tried to tell service stack to NOT to include typeinfo but it still is showing up when the message gets on the queue.

using (JsConfig.With(includeNullValues: false, includeTypeInfo: false))
{
    messageProducer.Publish(requestorMessage);
}

What I see in the queue:

{"__type":"Services.Api.Events.ServiceModel.Types.DataStoreCommand,...

I can through postman send in proper json (i.e. no __type properties) and all works as expected.

Any ideas?

Which MQ broker are you using? If you’re using Redis MQ it needs to include the type info of the message body so it knows what Type to deserialise the body into. What’s the full Exception StackTrace?

We are using SQS, and it’s blowing up with the __type info included;

Question: What is the difference between these two, and why are there two?

JsConfig.ExcludeTypeInfo = true;
JsConfig.IncludeTypeInfo = false;

So my initial issue was that I was setting the IncludeTypeInfo to false, and the type info was still be included in the json. Setting the ExcludeTypeInfo resolved our issue.

Here was the exception we were getting with the typeinfo included:

[13:39:48 ERR] Error serializing message from mq server: DataStoreCommand is not an allowed Runtime Type. Whitelist Type with [RuntimeSerializable] or IRuntimeSerializable.
System.NotSupportedException: DataStoreCommand is not an allowed Runtime Type. Whitelist Type with [RuntimeSerializable] or IRuntimeSerializable.
   at ServiceStack.Text.Common.JsWriter.AssertAllowedRuntimeType(Type type)
   at ServiceStack.Text.Common.DeserializeType`1.ExtractType(StringSegment strType)
   at ServiceStack.Text.Common.DeserializeType`1.ObjectStringToType(StringSegment strType)
   at ServiceStack.Text.Common.TypeAccessor.<>c__DisplayClass6_0.<GetPropertyMethod>b__0(StringSegment value)
   at ServiceStack.Text.Common.DeserializeTypeRefJson.StringToType(TypeConfig typeConfig, StringSegment strType, EmptyCtorDelegate ctorFn, Dictionary`2 typeAccessorMap)
   at ServiceStack.Text.Common.DeserializeType`1.<>c__DisplayClass2_0.<GetParseStringSegmentMethod>b__1(StringSegment value)
   at ServiceStack.Text.Common.DeserializeTypeRefJson.StringToType(TypeConfig typeConfig, StringSegment strType, EmptyCtorDelegate ctorFn, Dictionary`2 typeAccessorMap)
   at ServiceStack.Text.Common.DeserializeType`1.<>c__DisplayClass2_0.<GetParseStringSegmentMethod>b__1(StringSegment value)
   at ServiceStack.Text.Json.JsonReader`1.Parse(StringSegment value)
   at ServiceStack.Text.Json.JsonReader`1.Parse(String value)
   at ServiceStack.Text.JsonSerializer.DeserializeFromString[T](String value)
   at ServiceStack.StringExtensions.FromJson[T](String json)
   at ServiceStack.Aws.Sqs.SqsExtensions.FromSqsMessage[T](Message sqsMessage, String queueName)
   at ServiceStack.Aws.Sqs.SqsMqClient.GetMessage[T](String queueName, Int32 waitSeconds)
   at ServiceStack.Aws.Sqs.SqsMqClient.GetAsync[T](String queueName)
   at ServiceStack.Messaging.MessageHandler`1.ProcessQueue(IMessageQueueClient mqClient, String queueName, Func`1 doNext)
[Information] Microsoft.AspNetCore.Hosting.Internal.WebHost: Request starting HTTP/1.1 POST http://event-api.co.uk/event-unregister?id=KVRKmHD9HmFJS8bSn2wH  0

By default the type info is only emitted when required, the 2 config options will force it to either always include it or always exclude it, but you can’t change the Type Info behavior of messages because we always include it as it’s needed.

The issue you’re running into is a security issue for deserializing unknown object types, please see the docs on how to whitelist your message types:

E.g. if your DTOs had either [DataContract]/[DataMember] attributes or implemented the IReturn<T> or IReturnVoid interfaces it will be whitelisted by default.

Hmmm… so we ran into several disconnects then. While trying to figure out the issue, we tested various options. The original request looks as follows:

[Route("/secure/message", "POST")]
public class EmitMessageRequest : IReturnVoid
{
    public EventDestination EventDestination { get; set; }
    public Data Data { get; set; }
}

This is what was failing at first. We then added [DataContract] / [DataMemer] to this class, and that still produced the error

It was only after using the ExcludeTypeInfo = true, where we able to resolve the issue. We since went back in and removed the [DataContract]/[DataMember] attributes, as we haven’t decorated any other Request models with these attributes.

According to your last post, because we used the IReturnVoid, it should have been whitelisted, when we decorated with the [DataContract] attributes, it should have been whitelisted by default… but it wasn’t.

We have several tests verifying this behaviour.

It should be on the Type that’s sent in an object property. Are you using object elsewhere?

The Exception says DataStoreCommand is not whitelisted.

Our Data class has an object type:

public class Data
{
    public Data(EventTypes type, object payload)
    {
        Type = type.ToString();
        Payload = payload;
    }

    public Data()
    {
    }

    public string Type { get; set; }
    public object Payload { get; set; }
}

So if I read you correctly, the [DataMember] should be on the object Payload property, and that would have resolved our issue?

No it has to be on every Type you want to include in Payload, e.g. DataStoreCommand like the Exception says.

Please read the link in my previous comment about whitelisting runtime Types.