Azure / azure-functions-signalrservice-extension

Azure Functions bindings for SignalR Service. Project moved to https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService .
MIT License
97 stars 46 forks source link

Azure Function using Isolated Worker - User ID not transmitted to Connected EventGrid event #291

Closed kzryzstof closed 1 year ago

kzryzstof commented 1 year ago

I have an EventGrid Trigger Azure Function (using the isolated worker on .net7) that is called each time a client connects a SignalR hub. It is setup like this:

[Function("v1-on-connectivity-events-changed")]
public async Task OnConnectivityEventsChangedAsync
(
    [EventGridTrigger]
    EventGridConnectivityEvent eventGridConnectivityEvent,
    FunctionContext functionContext
)
{
  _logger.LogWarning("Event grid trigger received. Data='{ConnectionInfo}'", JsonSerializer.Serialize(eventGridConnectivityEvent));
  ...
}

On the client side, the application connects the SignalR via the Negotiate API like this:

_hubConnection = new HubConnectionBuilder()
                .WithUrl(hubEndpoint.AbsoluteUri, options =>
                {
                    //  Adds the application token to the options' header.
                    options.AccessTokenProvider = () => Task.FromResult(user.IdentityToken.AccessToken)!;
                    options.Headers.Add("userSid", user.IdentityToken.UserSid);   
                })
                .Build();

...

await _hubConnection.StartAsync();

Here is what the Negotiate function is doing which returns the URL along with an additional :

[Function("negotiate")]
public Task<SignalRConnectionInfo> OnNegotiateAsync
(
    [HttpTrigger(AuthorizationLevel.Anonymous, "POST")]
    HttpRequestData httpRequestData,
    [SignalRConnectionInfoInput(HubName = Constants.HubName, ConnectionStringSetting = "AzureSignalRConnectionString")]
    SignalRConnectionInfo signalRConnectionInfo
)
{
     ...   

    return signalRConnectionInfo;
}

The OnConnectivityEventsChangedAsync Function is properly called when the client gets connected. However, it does not provide all the information that I need: the User ID is missing.

Question

In the SignalR documentation, it is stated that the connected event should provide the user ID along with the connection ID. Here is what I get:

{
    "Id":"56f3ab25-11d7-41d6-acbb-8429a741d746",
"Topic":"/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.SignalRService/SignalR/<signalr_instance>",
    "Subject":"hub/updates",
    "EventType":"Microsoft.SignalRService.ClientConnectionConnected",
    "EventTime":"2022-11-27T19:22:19.2349923Z",
    "Data": { 
         "timestamp":"2022-11-27T19:22:19.2349923Z",
         "hubName":"updates",
         "connectionId":"gmJM_GHVkXsQuS5C1LhdXXXXX"
    }
}

What should I do to get the User ID along with the Connection ID?

Y-Sindo commented 1 year ago

You should use binding expression {headers.userSid} to set the user ID.

[Function("negotiate")]
public Task<SignalRConnectionInfo> OnNegotiateAsync
(
    [HttpTrigger(AuthorizationLevel.Anonymous, "POST")]
    HttpRequestData httpRequestData,
    [SignalRConnectionInfoInput(HubName = Constants.HubName, ConnectionStringSetting = "AzureSignalRConnectionString", UserId = "{headers.userSid}")])]
    SignalRConnectionInfo signalRConnectionInfo
)
{
     ...   

    return signalRConnectionInfo;
}

See https://github.com/aspnet/AzureSignalR-samples/tree/main/samples/DotnetIsolated-BidirectionChat for a complete sample

kzryzstof commented 1 year ago

Exactly what I was missing! Thanks!! :)