I want to send a message to my Azure SignalR using. I am using 'CommunicationIdentityClient' to get the acess token. But when I try to do
identity = client.create_user()
I am getting 403 error. How do I fix it?
Here is my code
from datetime import timedelta
from azure.communication.identity import CommunicationIdentityClient
connection_string = "Endpoint=https://signalrpro.service.signalr.net;AccessKey=acc;Version=1.0;"
client = CommunicationIdentityClient.from_connection_string(connection_string)
identity = client.create_user()
print("\nCreated an identity with ID: " + identity.properties['id'])
# Issue an access token with a validity of 24 hours and the "voip" scope for an identity
# Issue an access token with a validity of an hour and the "voip" scope for an identity
token_expires_in = timedelta(hours=1)
token_result = client.get_token(identity, ["voip"], token_expires_in=token_expires_in)
#token_result = client.get_token(identity, ["voip"])
print("\nIssued an access token with 'voip' scope that expires at " + token_result.expires_on + ":")
print(token_result.token)
Edit: I looks like CommunicationIdentityClient is not a valid library for signalr. In that case how should I connect to SignalR?
As mentioned in the comments, CommunicationIdentityClient
can't be used for Azure SignalR connection strings, it is specific to Azure Communication Services.
You can use Azure Functions to send messages and use its Endpoint URL to receive messages using SignalR Core.
Refer Microsoft Documentation to connect and send messages to Azure SignalR Service using Python Azure Functions.
Create an Azure function App and deploy Signal R function code to it. Refer MSDOC to deploy the function to Azure.
Deployed function to Azure:
Open negotiate function and copy the function url by clicking on Get function url
Create a python file app.py
and add below code which uses the signalrcore
library to connect to Azure SignalR and send/receive messages using Azure Function URL.
Thanks for the code and insights @Sampath.
app.py:
import logging
import sys
import requests
from signalrcore.hub_connection_builder import HubConnectionBuilder
def input_with_default(input_text, default_value):
value = input(input_text.format(default_value))
return default_value if value is None or value.strip() == "" else value
url = "https://function_app_name.azurewebsites.net/api/negotiate"
payload = {
"hubName": "serverless"
}
headers = {
'Content-Type': 'application/json'
}
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
connection_info = response.json()
print(f"Successfully received connection information: {connection_info}")
else:
print(f"Failed to get response: {response.status_code}, {response.text}")
sys.exit(1)
signalr_url = connection_info['url']
access_token = connection_info['accessToken']
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
hub_connection = HubConnectionBuilder() \
.with_url(signalr_url, options={
"verify_ssl": False,
"headers": {
"Authorization": f"Bearer {access_token}"
}
}) \
.configure_logging(logging.DEBUG, socket_trace=True, handler=handler) \
.with_automatic_reconnect({
"type": "interval",
"keep_alive_interval": 10,
"intervals": [1, 3, 5, 6, 7, 87, 3]
}).build()
hub_connection.on_open(lambda: print("Connection opened and handshake received, ready to send messages"))
hub_connection.on_close(lambda: print("Connection closed"))
hub_connection.on("newMessage", print)
hub_connection.start()
while True:
message = input("Enter a message (type 'exit()' to exit): ")
if message == "exit()":
break
elif message:
requests.post("https://function_app_name.azurewebsites.net/api/api/messages", json={"sender": "client", "text": message})
hub_connection.stop()
sys.exit(0)
Output:
Successfully received connection information: {'url': 'https://kpsignalr.service.signalr.net/client/?hub=serverless', 'accessToken': 'eyJhbGciOiJIUzI1NiIsInR5cCXXVCIsXXW-q9ZndKZSHgUCBoJW4TXFgSFV_1ac'}
2025-04-15 16:42:00,157 - SignalRCoreClient - DEBUG - Handler registered started newMessage
2025-04-15 16:42:00,158 - SignalRCoreClient - DEBUG - Connection started
2025-04-15 16:42:00,158 - SignalRCoreClient - DEBUG - Negotiate url:https://kpsignalr.service.signalr.net/client/negotiate?hub=serverless
C:\Users\uname\pyfunction\env\Lib\site-packages\urllib3\connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'kpsignalr.service.signalr.net'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
warnings.warn(
2025-04-15 16:42:01,146 - SignalRCoreClient - DEBUG - Response status code200
2025-04-15 16:42:01,152 - SignalRCoreClient - DEBUG - start url:wss://kpsignalr.service.signalr.net/client/?hub=serverless&id=U2SLWH7bm-YhlwjaBBZ79wLSYocwn02
Enter a message (type 'exit()' to exit): 2025-04-15 16:42:01,598 - websocket - DEBUG - --- request header ---
2025-04-15 16:42:01,599 - websocket - DEBUG - GET /client/?hub=serverless&id=U2SLWH7bm-YhlwjaBBZ79wLSYocwn02 HTTP/1.1
Upgrade: websocket
Host: kpsignalr.service.signalr.net
Origin: http://kpsignalr.service.signalr.net
Sec-WebSocket-Key: uapizlgwPXxny6Zq6iWoTQ==
Sec-WebSocket-Version: 13
Connection: Upgrade
Authorization: Bearer eyJhbGciOiJIUzXXQ.w9MzQpYoUMaS4W-q9ZndKZSHgUCBoJW4TXFgSFV_1ac
2025-04-15 16:42:01,599 - websocket - DEBUG - -----------------------
2025-04-15 16:42:01,600 - websocket - DEBUG - --- response header ---
2025-04-15 16:42:01,812 - websocket - DEBUG - HTTP/1.1 101 Switching Protocols
2025-04-15 16:42:01,813 - websocket - DEBUG - Date: Tue, 15 Apr 2025 11:12:01 GMT
2025-04-15 16:42:01,814 - websocket - DEBUG - Connection: upgrade
2025-04-15 16:42:01,815 - websocket - DEBUG - access-control-allow-credentials: true
2025-04-15 16:42:01,816 - websocket - DEBUG - access-control-allow-origin: http://kpsignalr.service.signalr.net
2025-04-15 16:42:01,818 - websocket - DEBUG - upgrade: websocket
2025-04-15 16:42:01,820 - websocket - DEBUG - vary: Origin
2025-04-15 16:42:01,823 - websocket - DEBUG - sec-websocket-accept: ajYw1W+6EPHydgKQ+0cFQCseheA=
2025-04-15 16:42:01,824 - websocket - DEBUG - Strict-Transport-Security: max-age=31536000; includeSubDomains
2025-04-15 16:42:01,825 - websocket - DEBUG - -----------------------
2025-04-15 16:42:01,827 - SignalRCoreClient - DEBUG - -- web socket open --
2025-04-15 16:42:01,828 - SignalRCoreClient - DEBUG - Sending message <signalrcore.messages.handshake.request.HandshakeRequestMessage object at 0x000001C99E7E4890>
2025-04-15 16:42:01,829 - SignalRCoreClient - DEBUG - {"protocol": "json", "version": 1}▲
2025-04-15 16:42:01,832 - websocket - DEBUG - ++Sent raw: b'\x81\xa3s\x03\xb2$\x08!\xc2V\x1cw\xddG\x1co\x90\x1eS!\xd8W\x1cm\x90\x08S!\xc4A\x01p\xdbK\x1d!\x88\x04B~\xac'
2025-04-15 16:42:01,834 - websocket - DEBUG - ++Sent decoded: fin=1 opcode=1 data=b'{"protocol": "json", "version": 1}\x1e'
2025-04-15 16:42:02,050 - websocket - DEBUG - ++Rcv raw: b'\x81\x03{}\x1e'
2025-04-15 16:42:02,051 - websocket - DEBUG - ++Rcv decoded: fin=1 opcode=1 data=b'{}\x1e'
2025-04-15 16:42:02,052 - SignalRCoreClient - DEBUG - Message received{}▲
2025-04-15 16:42:02,053 - SignalRCoreClient - DEBUG - Evaluating handshake {}▲
Connection opened and handshake received, ready to send messages
2025-04-15 16:42:02,649 - websocket - DEBUG - ++Rcv raw: b'\x81\x0b{"type":6}\x1e'
2025-04-15 16:42:02,650 - websocket - DEBUG - ++Rcv decoded: fin=1 opcode=1 data=b'{"type":6}\x1e'
2025-04-15 16:42:02,651 - SignalRCoreClient - DEBUG - Message received{"type":6}▲
2025-04-15 16:42:02,651 - SignalRCoreClient - DEBUG - Raw message incomming:
2025-04-15 16:42:02,653 - SignalRCoreClient - DEBUG - {"type":6}▲