Kiran
Kiran

Reputation: 3047

Why Azure SignalR function returns CORS error on localhost testing

I am trying to learn how to use Azure function and SignalR to create a server-less design. for this I have created a following class for Azure function :

    public static class NotifactionR
    {

        [FunctionName("negotiate")]
        public static SignalRConnectionInfo Negotiate(
            [HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
            [SignalRConnectionInfo(HubName = "my-hub")]
            SignalRConnectionInfo connectionInfo)
        {
            // connectionInfo contains an access key token with a name identifier claim set to the authenticated user
            return connectionInfo;
        }

        [FunctionName("NotifactionR")]
        public static Task NotifactionR([EventGridTrigger]EventGridEvent eventGridEvent,
            [SignalR(HubName = "my-hub")]IAsyncCollector<SignalRMessage> signalRMessages,
            ILogger log)
        {
            log.LogInformation(eventGridEvent.Data.ToString());

            return signalRMessages.AddAsync(
                new SignalRMessage
                {
                    // the message will only be sent to these user IDs
                    UserId = "userId1",
                    Target = "OnNewEvent",
                    Arguments = new[] { eventGridEvent.Data }
                });
        }
    }

I have used the following configuration on my local.settings.jsonto enable local testing:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "AzureSignalRConnectionString": "Endpoint=https://myservice.service.signalr.net;AccessKey=myaccess-token;Version=1.0;",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet"
  },
  "Host": {
    "CORS": "http://localhost:7071",
    "CORSCredentials": true
  }
}

and to test this, just created an HTML file which contains a following script:

const connection = new signalR.HubConnectionBuilder()
      .withUrl("http://localhost:7071/api", { headers: { 'Access-Control-Allow-Origin': 'http://localhost:7071'}})
      .configureLogging(signalR.LogLevel.Trace)
      .build();

connection.on('OnNewEvent', ProcessMyEvent);
connection.onclose(() => console.log('disconnected'));
console.log('connecting...');

connection.start()
    .then(() => data.ready = true)
    .catch(console.error);

I am seeing the following error when I open the HTML file on my Chrome (issue is almost same in firefox as well):

connecting...
Utils.ts:189 [2019-07-27T16:13:01.573Z] Debug: Starting HubConnection.
Utils.ts:189 [2019-07-27T16:13:01.573Z] Debug: Starting connection with transfer format 'Text'.
Utils.ts:189 [2019-07-27T16:13:01.575Z] Debug: Sending negotiation request: http://localhost:7071/api/negotiate.

SignalRTest.html:1 Access to XMLHttpRequest at 'http://localhost:7071/api/negotiate' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Utils.ts:182 [2019-07-27T16:13:02.147Z] Warning: Error from HTTP request. 0: .
Utils.ts:179 [2019-07-27T16:13:02.148Z] Error: Failed to complete negotiation with the server: Error
Utils.ts:179 [2019-07-27T16:13:02.148Z] Error: Failed to start the connection: Error
Error
    at new HttpError (Errors.ts:20)
    at XMLHttpRequest.xhr.onerror (XhrHttpClient.ts:76)

does anyone have any ideas as to what I am doing wrong here?

Update 1

Here is my test.html file I am using

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="https://unpkg.com/@aspnet/[email protected]/dist/browser/signalr.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
    <script>
        window.apiBaseUrl = 'http://localhost:7071';

        function initialize() {
            const connection = new signalR.HubConnectionBuilder()
                .withUrl(window.apiBaseUrl + "/api", { headers: { 'Access-Control-Allow-Origin': 'http://localhost:7071' } })
                .configureLogging(signalR.LogLevel.Trace)
                .build();

            connection.on('OnNewEvent', ProcessMyEvent);
            connection.onclose(() => console.log('disconnected'));
            console.log('connecting...');

            connection.start({ withCredentials: false })
                .then(() => console.log('ready...'))
                .catch(console.error);

        }
        function ProcessMyEvent(vehicle) {
            alert("ProcessMyEvent CALLED");
        }

        initialize();


    </script>
</head>
<body>

</body>

</html>

Update 2:

I have also tried to run this from a command prompt with the following command:

c:\Users\Kiran\AppData\Local\AzureFunctionsTools\Releases\2.26.0\cli\func host start --cors * --pause-on-error

still I get the same error

Upvotes: 1

Views: 3041

Answers (1)

Charlie.H
Charlie.H

Reputation: 479

This is a bit of a red herring and doesn't appear to be CORS related to me. I see you're using the Azure SignalR Service, the way you connect to this is different to standard SignalR.

The negotiate function behaves differently with the SignalR Service. negotiate will return some data along with with accessToken and the URL of your SignalR Service, you'll need to use this URL instead to connect.

I've added an example below of how it should work. (I haven't tested this, but hopefully you get the idea).

function initialize() {
    axios.get(window.apiBaseUrl+"/api/negotiate").then(response => {        
        const options = {
            accessTokenFactory: () => response.data.accessToken
        }
        const socket = new SignalR.HubConnectionBuilder()
            .withUrl(response.data.url, options)
            .build(SignalR.HttpTransportType.None)

        connection.on('OnNewEvent', ProcessMyEvent);
        connection.onclose(() => console.log('disconnected'));
        console.log('connecting...');

        connection.start({ withCredentials: false })
            .then(() => console.log('ready...'))
            .catch(console.error);
    });
}

Upvotes: 7

Related Questions