pythonazurebackendazure-signalr

Cannot connect to Azure signalr from my python backend code


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?


Solution

  • 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:

    enter image description here

    Open negotiate function and copy the function url by clicking on Get function url

    enter image description here

    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}▲