azure-webpubsub icon indicating copy to clipboard operation
azure-webpubsub copied to clipboard

Metadata on the connection

Open riccardobecker opened this issue 3 years ago • 5 comments

At this moment a connection (ConnectionContext) consists of a UserId only. For scenarios I have in mind, it should be possible to add some metadata or additional context to the connection at the point of creation.

var clientUri = _client.GetClientAccessUri("John", keyvaluepair);

on the server side we then should be able to get this information by getting it from the connection context again.

riccardobecker avatar May 08 '21 18:05 riccardobecker

Thanks for the feedback. Do you have some scenarios for such metadata, for example, can clients edit such metadata or should it be readonly to the clients?

ConnectionContext contains the headers and query strings of the client URL when the client connects. So one way is to append some query string to the generated URI so that connection. But in this way, such metadata is editable by the clients.

Another way would be for the SDK to add such metadata into claims.

vicancy avatar May 13 '21 06:05 vicancy

At this point, i add some metadata like subprotocol and some other claim-like values. So this metadata is something that the server would retrieve one time from a database or cosmos and then 'add' it to the connection. This way when the client starts sending messages, the server should have access to that metadata without having to look it up every single time.

Right now, i do the following and put the metadata inside the "clientid". This way i can get that data out again on the server side using the ConnectionContext. But this is not a neat way to do it.

var url = _webPubSubServiceClient.GetClientAccessUri($"{metadata1}:{metadata2}:{subProtocol}").AbsoluteUri;

Maybe its a nice feature to be able to manipulate the ConnectionContext and add some contextual or metadata information after the client has connected. Something like below in an Azure function:

[FunctionName("connected")] public async Task Connected([WebPubSubTrigger(WebPubSubEventType.System, "connected")] ConnectionContext connectionContext) { // lookup the metadata from some store var metaData = await _cosmosClient.GetMetaDataAsync(connectionContext.UserId); await connectionContext.AddMetaDataAsync(metaData) }

after this, we should be able to get that metadata from the connectionContext inside the Azure Function that triggers upon a message receive.

Hope my feature is clear?

riccardobecker avatar May 14 '21 07:05 riccardobecker

Yes, the feature makes total sense to me.

The concern of adding the metadata to GetClientAccessUri is that it actually sets the info inside the jwt token which then is a query parameter of the URL, there are some limitations of this approach that:

  1. URL has a length limit
  2. People can extract what the metadata is with the token, it might also be a concern when the server does not want to expose the metadata to the client.

Proposal

How about adding such metadata in connect event so that such metadata is just between the WebPubSub service and the server (e.g. Azure Function)?

Detail:

So for now we can set userId, subprotocol, groups and roles in connect response for a connection https://azure.github.io/azure-webpubsub/references/protocol-cloudevents#success-response-format How about adding a metadata field there like:

{
    "groups": [],
    "userId": "",
    "roles": [],
    "subprotocol": "",
    "metadata": {
        "foo": "bar",
        "more": { }
    }
}

Later on, such metadata can be fetched from ConnectionContext.

I can see that "claim-like" metadata can still be useful, maybe we can support both ways. Comments?

vicancy avatar May 17 '21 02:05 vicancy

Yes i agree with you that adding the metadata to the GetClientAccessUri is not clean.

Upon the "connect" event would be the perfect place. A client connects once (or a few times, depending on how stable the internet is e.g.) and the "backend" retrieves the metadata from somewhere and stores it on the ConnectionContext.

This solution would perfectly fit our use-case.

Thanks!

riccardobecker avatar May 17 '21 08:05 riccardobecker

set state is now available in https://www.npmjs.com/package/@azure/web-pubsub-express/v/1.0.0-beta.3

Sample usage:

const handler = new WebPubSubEventHandler("chat", ["https://xxx.webpubsub.azure.com"], {
  handleConnect(req, res) {
    // You can set the state for the connection, it lasts throughout the lifetime of the connection
    res.setState("calledTime", 1);
    res.success();
  },
  handleUserEvent(req, res) {
    var calledTime = req.context.states.calledTime++;
    console.log(calledTime);
    // You can also set the state here
    res.setState("calledTime", calledTime);
    res.success();
  }
});

vicancy avatar Aug 02 '21 08:08 vicancy