This article explains how to authenticate and send real-time messages to clients connected to Azure SignalR Service by using SignalR Service bindings in Azure Functions. Azure Functions supports input and output bindings for SignalR Service.
This is reference information for Azure Functions developers. If you're new to Azure Functions, start with the following resources:
To use the SignalR Service annotations in Java functions, you need to add a dependency to the azure-functions-java-library-signalr artifact (version 1.0 or higher) to your pom.xml.
Before a client can connect to Azure SignalR Service, it must retrieve the service endpoint URL and a valid access token. The SignalRConnectionInfo input binding produces the SignalR Service endpoint URL and a valid token that are used to connect to the service. Because the token is time-limited and can be used to authenticate a specific user to a connection, you should not cache the token or share it between clients. An HTTP trigger using this binding can be used by clients to retrieve the connection information.
For more information on how this binding is used to create a "negotiate" function that can be consumed by a SignalR client SDK, see the Azure Functions development and configuration article in the SignalR Service concepts documentation.
The following example shows a SignalR connection info input binding in a function.json file and a C# Script function that uses the binding to return the connection information.
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalRConnectionInfo",
"name": "connectionInfo",
"hubName": "chat",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "in"
}
Here's the C# Script code:
#r "Microsoft.Azure.WebJobs.Extensions.SignalRService"
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
public static SignalRConnectionInfo Run(HttpRequest req, SignalRConnectionInfo connectionInfo)
{
return connectionInfo;
}
The following example shows a SignalR connection info input binding in a function.json file and a JavaScript function that uses the binding to return the connection information.
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalRConnectionInfo",
"name": "connectionInfo",
"hubName": "chat",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "in"
}
The following example shows a SignalR connection info input binding in a function.json file and a Python function that uses the binding to return the connection information.
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalRConnectionInfo",
"name": "connectionInfo",
"hubName": "chat",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "in"
}
If the function is triggered by an authenticated client, you can add a user ID claim to the generated token. You can easily add authentication to a function app using App Service Authentication.
App Service Authentication sets HTTP headers named x-ms-client-principal-id and x-ms-client-principal-name that contain the authenticated user's client principal ID and name, respectively.
You can set the UserId property of the binding to the value from either header using a binding expression: {headers.x-ms-client-principal-id} or {headers.x-ms-client-principal-name}.
[FunctionName("negotiate")]
public static SignalRConnectionInfo Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
[SignalRConnectionInfo
(HubName = "chat", UserId = "{headers.x-ms-client-principal-id}")]
SignalRConnectionInfo connectionInfo)
{
// connectionInfo contains an access key token with a name identifier claim set to the authenticated user
return connectionInfo;
}
You can set the userId property of the binding to the value from either header using a binding expression: {headers.x-ms-client-principal-id} or {headers.x-ms-client-principal-name}.
#r "Microsoft.Azure.WebJobs.Extensions.SignalRService"
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
public static SignalRConnectionInfo Run(HttpRequest req, SignalRConnectionInfo connectionInfo)
{
// connectionInfo contains an access key token with a name identifier
// claim set to the authenticated user
return connectionInfo;
}
You can set the userId property of the binding to the value from either header using a binding expression: {headers.x-ms-client-principal-id} or {headers.x-ms-client-principal-name}.
module.exports = async function (context, req, connectionInfo) {
// connectionInfo contains an access key token with a name identifier
// claim set to the authenticated user
context.res.body = connectionInfo;
};
You can set the userId property of the binding to the value from either header using a binding expression: {headers.x-ms-client-principal-id} or {headers.x-ms-client-principal-name}.
def main(req: func.HttpRequest, connectionInfoJson: str) -> func.HttpResponse:
# connectionInfo contains an access key token with a name identifier
# claim set to the authenticated user
return func.HttpResponse(
connectionInfoJson,
status_code=200,
headers={
'Content-type': 'application/json'
}
)
You can set the userId property of the binding to the value from either header using a binding expression: {headers.x-ms-client-principal-id} or {headers.x-ms-client-principal-name}.
Use the SignalR output binding to send one or more messages using Azure SignalR Service. You can broadcast a message to all connected clients, or you can broadcast it only to connected clients that have been authenticated to a given user.
You can also use it to manage the groups that a user belongs to.
Broadcast to all clients
The following example shows a function that sends a message using the output binding to all connected clients. The target is the name of the method to be invoked on each client. Arguments is an array of zero or more objects to be passed to the client method.
[FunctionName("SendMessage")]
public static Task SendMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]object message,
[SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
// the message will only be sent to this user ID
UserId = "userId1",
Target = "newMessage",
Arguments = new [] { message }
});
}
Example function.json:
{
"type": "signalR",
"name": "signalRMessages",
"hubName": "<hub_name>",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "out"
}
Here's the C# Script code:
#r "Microsoft.Azure.WebJobs.Extensions.SignalRService"
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
public static Task Run(
object message,
IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
// the message will only be sent to this user ID
UserId = "userId1",
Target = "newMessage",
Arguments = new [] { message }
});
}
Example function.json:
{
"type": "signalR",
"name": "signalRMessages",
"hubName": "<hub_name>",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "out"
}
Here's the JavaScript code:
module.exports = async function (context, req) {
context.bindings.signalRMessages = [{
// message will only be sent to this user ID
"userId": "userId1",
"target": "newMessage",
"arguments": [ req.body ]
}];
};
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalR",
"name": "out_message",
"hubName": "<hub_name>",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "out"
}
Here's the Python code:
def main(req: func.HttpRequest, out_message: func.Out[str]) -> func.HttpResponse:
message = req.get_json()
out_message.set(json.dumps({
#message will only be sent to this user ID
'userId': 'userId1',
'target': 'newMessage',
'arguments': [ message ]
}))
[FunctionName("SendMessage")]
public static Task SendMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]object message,
[SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
// the message will be sent to the group with this name
GroupName = "myGroup",
Target = "newMessage",
Arguments = new [] { message }
});
}
Example function.json:
{
"type": "signalR",
"name": "signalRMessages",
"hubName": "<hub_name>",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "out"
}
Here's the C# Script code:
#r "Microsoft.Azure.WebJobs.Extensions.SignalRService"
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
public static Task Run(
object message,
IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
// the message will be sent to the group with this name
GroupName = "myGroup",
Target = "newMessage",
Arguments = new [] { message }
});
}
Example function.json:
{
"type": "signalR",
"name": "signalRMessages",
"hubName": "<hub_name>",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "out"
}
Here's the JavaScript code:
module.exports = async function (context, req) {
context.bindings.signalRMessages = [{
// message will only be sent to this group
"groupName": "myGroup",
"target": "newMessage",
"arguments": [ req.body ]
}];
};
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalR",
"name": "out_message",
"hubName": "<hub_name>",
"connectionStringSetting": "<name of setting containing SignalR Service connection string>",
"direction": "out"
}
Here's the Python code:
def main(req: func.HttpRequest, out_message: func.Out[str]) -> func.HttpResponse:
message = req.get_json()
out_message.set(json.dumps({
#message will only be sent to this group
'groupName': 'myGroup',
'target': 'newMessage',
'arguments': [ message ]
}))
SignalR Service allows users to be added to groups. Messages can then be sent to a group. You can use the SignalR output binding to manage a user's group membership.