Azure-docs: Example 'Add user to group' cannot be run

Created on 2 Jul 2019  Â·  9Comments  Â·  Source: MicrosoftDocs/azure-docs

When launching an Azure Functions project with the addToGroup method this happens:

[7/2/2019 6:57:10 PM] The following 1 functions are in error:
[7/2/2019 6:57:10 PM] addToGroup: Microsoft.Azure.WebJobs.Host: Error indexing method 'addToGroup'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'userId' to type String. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

Document Details

⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Pri2 assigned-to-author azure-functionsvc doc-bug triaged

Most helpful comment

Could you provide an example of a client connecting to and adding himself to a group using this functionality? I'm not certain how Azure SignalR would associate the ID with the client. Your example appears to be assigning all clients the "some-string" UserId, which would not work too well for something which is supposed to be a unique identifier.

All 9 comments

Hi @dirthsj Thank you for your feedback! We will review and update as appropriate.

@mike-urnun-msft In the meantime, could you give a direction on how to manage SignalR groups from Azure Functions? I can't find any sample where they are used and I'm blocked.

After a lot of trial and error, I managed to get the user ID from the claims, but there are a few catch

  1. It works only after publishing the function to Azure, there is no way (that I could find) to make it work in debug
  2. It works only if you have setup the authentication Function host app service.

I'm really lost as to why we need the UserId when using the functions binding. If you use SignalR with ASP.Net, adding to a group you use the connection ID and not the user id.

Anyway, here's how I did to get the user id from the claims.

C# [FunctionName("addToGroup")] public static Task AddToGroup( [HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest req, ClaimsPrincipal claimsPrincipal, [SignalR(HubName = "chat")] IAsyncCollector<SignalRGroupAction> signalRGroupActions) { var userIdClaim = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier); return signalRGroupActions.AddAsync( new SignalRGroupAction { UserId = userIdClaim.Value, GroupName = "myGroup", Action = GroupAction.Add }); }

@dirthsj Apologies for not getting back sooner.

Thanks for working on the PR but I believe addToGroup function is intended to be used a management system / user for adding users to a group with the userId passed in as a query parameter (as in the JS sample) or route parameter (as in the Java sample). @craigshoemaker Could you confirm?

With the code you've shared, only the logged in user would be able to add themselves into the group which is likely not intended.

Unlike standalone SignalR, the connections are handled by the Azure SignalR Service instead for which we use the userId to track them to a particular user. The userId can be any unique string.

The JS and Java samples seem to require some subtle fixes too BTW.

Here is a sample that I've tried running and can confirm works

using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;

namespace funk_signalr_csharp
{
  public static class SignalRFunks
  {
    [FunctionName("Negotiate")]
    public static SignalRConnectionInfo Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "negotiate")] HttpRequest req,
        [SignalRConnectionInfo(HubName = "chat", UserId = "some-string")]SignalRConnectionInfo connectionInfo,
        ILogger log)
    {
      return connectionInfo;
    }

    [FunctionName("AddToGroup")]
    public static Task AddToGroup(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "{group}/add/{userId}")]HttpRequest req,
    string group,
    string userId,
    [SignalR(HubName = "chat")]IAsyncCollector<SignalRGroupAction> signalRGroupActions)
    {
      return signalRGroupActions.AddAsync(
          new SignalRGroupAction
          {
            UserId = userId,
            GroupName = group,
            Action = GroupAction.Add
          });
    }

    [FunctionName("SendMessageToGroup")]
    public static Task SendMessage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "{group}/send")]object message,
    string group,
    [SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
    {
      return signalRMessages.AddAsync(
          new SignalRMessage
          {
            // the message will be sent to the group with this name
            GroupName = group,
            Target = "newMessage",
            Arguments = new[] { message }
          });
    }
  }
}

Could you provide an example of a client connecting to and adding himself to a group using this functionality? I'm not certain how Azure SignalR would associate the ID with the client. Your example appears to be assigning all clients the "some-string" UserId, which would not work too well for something which is supposed to be a unique identifier.

@dirthsj Sure. As for the some-string in my example, the intention was to show that userId can be any string.

For the negotiate function, you could simply use the header value from an authenticated request as shown in the document and the other functions don't really have to do that unless its for allowing the same user to add/remove themselves from groups.

The functions I've shared would be used by another user/system that wants to add/remove users from groups with just their userId.

I believe how these functions are implemented would really depend on the use case at hand but to answer the original issue raised, the userId just has to be a unique way to represent a user. It could be the client principal id as shown in the doc, a username, an email id, etc.

As for the client example, App Service Authentication is providing the required headers in the doc, but if you have your own authentication, you would have to either

  • Have a proxy which extracts the information you need from the bearer token and puts it into a header
  • Use runtime binding to fetch the upn from the token

And for a client adding themselves into a group, they would have to the call the function @Gimly shared.

@dirthsj Hope my previous comment clears things up. We are assigning this issue to the content author for further review on the updates required on this doc.

@PramodValavala-MSFT Could you please provide an example of client(JS) connecting/calling the "AddToGroup" function

@PramodValavala-MSFT It seems like the "addtogroup" functionality is not working when adding to multiple groups. https://github.com/Azure/azure-functions-signalrservice-extension/issues/89

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ianpowell2017 picture ianpowell2017  Â·  3Comments

AronT-TLV picture AronT-TLV  Â·  3Comments

Agazoth picture Agazoth  Â·  3Comments

paulmarshall picture paulmarshall  Â·  3Comments

spottedmahn picture spottedmahn  Â·  3Comments