How to use serverless Azure SignalR (REST API / Upstream) with ReactJS?

Viewed 671

On high level I want to achieve real time communication between two ReactJS apps. I need to be able to create specific groups to separate the end users, that group will belong to another group with one or more manager users. The manager should be able to send messages (can a be string or object) to the whole end user group or just for one user within that group, which upon I can act in the end user app. The end users should be able to send a message for the specific group of managers.

Since Azure Cloud is given, a sensible choice would be to use the Azure SignalR, especially because it has a serverless plan.

  • I have created the SignalR service, set it to serverless mode.
  • Installed @microsoft/signalr package to the React apps.
  • Created a Nodejs negotiate HTTP triggered function with signalR binding.

At this point I'm connected to signalR. But I'm confused how to proceed. On one hand I should be able to use SignalR REST API, on the other hand I could use SignalR Upstream. Upstream seems more suitable, since users can invoke hub methods (in my case Nodejs SignalR bindings in the Function App) and users gets notified when client connections are connected or disconnected. That is important for the manager users to see who is connected and if the connection is still alive. As next steps:

  • defined Upstream URL
  • created connected/disconnected bindings in the Function app

function.json:

{
  "bindings": [
    {
      "type": "signalRTrigger",
      "name": "invocation",
      "hubName": "default",
      "category": "connections",
      "event": "connected",
      "direction": "in"
    },
    {
      "type": "signalR",
      "name": "signalRMessages",
      "hubName": "default",
      "connectionStringSetting": "AzureSignalRConnectionString",
      "direction": "out"
    }
  ]
}

index.js:

module.exports = function (context, invocation) {
  context.bindings.signalRMessages = [
    {
      target: "connected", // name of the client method to invoke
      arguments: [invocation.ConnectionId],
    },
  ];
  context.done();
};

In similar manner sending a broad cast message:

{
  "bindings": [
    {
      "type": "signalRTrigger",
      "name": "invocation",
      "hubName": "default",
      "category": "messages",
      "event": "broadcastMessage",
      "parameterNames": ["message"],
      "direction": "in"
    },
    {
      "type": "signalR",
      "name": "signalRMessages",
      "hubName": "default",
      "connectionStringSetting": "AzureSignalRConnectionString",
      "direction": "out"
    }
  ]
}
module.exports = function (context, invocation) {
  context.bindings.signalRMessages = [
    {
      target: "broadcastMessage", // name of the client method to invoke
      arguments: [
        context.bindingData.message, // arguments to pass to client method. binding with parameterNames
      ],
    },
  ];
  context.done();
};

After connection from the React app, the connected method is fired automatically and I could invoke other methods like: siglarConnection.invoke("broadcast", 'Hi') But this is as far as I got. No private message, no user groups and still need to manage the active connections somewhere. Also there is a suggestion, that I could use userId to send messages directly, no need to bother with the connectionId. Since other functions are using App Service Authentication and the users are stored in AD B2C:

  • enabled the App Service Authentication for the Function app

Now for the React apps to connect I need to pass an access token with to the connection builder else I get a 401. Now I can apps connect again but now using the authenticated users access token, which is a good thing, but because of this my invokations does not work anymore. Most likely because I cannot supply access token with these and I get back 401.

Security is a must, so I decided to take a look at the REST API way.

Looks fairly straight forward, I need to call the specified endpoints, but calling them actually gives 401 response www-authenticate: Bearer error="invalid_token", error_description="The signature key was not found". Figures since I call the signalR service directly, providing AD B2C access token does not help. I probably need the access token from the negotiate, but I don't know how to get it. Sure enough it's in the negotiate response, but I was not able to intercept that.

Sorry for the lengthy description, but I believe it was necessary to understand my scenario. I may have a major overlook somewhere or someone with similar experience can clear up the confusion. There are tons of examples using C#, but I cannot use that. Any help is appreciated.

0 Answers
Related