SignalR-Client-Swift icon indicating copy to clipboard operation
SignalR-Client-Swift copied to clipboard

Headers (Authorization) Not Sent in Invoke Requests on WebSocket Connection

Open stilus-tayyipguzel opened this issue 9 months ago • 5 comments

@moozzyk Hi,

When using HubConnectionBuilder with .withHttpConnectionOptions(), the Authorization header is properly set in the connection request. However, it is not included in invoke requests sent over the WebSocket connection.

connection = HubConnectionBuilder(url: url)
    .withLogging(minLogLevel: .debug)
    .withHubConnectionDelegate(delegate: self)
    .withHttpConnectionOptions { options in
        options.accessTokenProvider = { self.token }
        options.headers["Authorization"] = "Bearer \(self.token)"
    }
    .withHttpConnectionOptions { options in
        options.authenticationChallengeHandler = { session, challenge, completionHandler in
            session.configuration.httpAdditionalHeaders?["Authorization"] = "Bearer \(self.token)"
            completionHandler(.performDefaultHandling, nil)
        }
    }
    .build()

Expected Behavior: •The Authorization header should be included in all requests, including invoke method calls.

Additional Notes: • The header works in the initial connection request but disappears when invoking methods. • It appears that URLSessionWebSocketTask does not retain the header for WebSocket messages.

stilus-tayyipguzel avatar Mar 05 '25 15:03 stilus-tayyipguzel

The token should be preserved. It is being set here: https://github.com/moozzyk/SignalR-Client-Swift/blob/655e8bde45b4e3fc737c8ced9da242e3a76f3e69/Sources/SignalRClient/WebsocketsTransport.swift#L41

I am not sure why it isn't working for you but there is a lot going on in your setup and maybe you overwrite or send duplicate headers. The intention is that the following code should work:

connection = HubConnectionBuilder(url: url)
    .withLogging(minLogLevel: .debug)
    .withHubConnectionDelegate(delegate: self)
    .withHttpConnectionOptions { options in
        options.accessTokenProvider = { self.token }
    }
    .build()

moozzyk avatar Mar 05 '25 15:03 moozzyk

I have tried multiple approaches to include the Authorization header, but none of them are working. I don’t have access to the server, so I can’t debug the request directly. The server developer confirmed that the header is empty in the received requests.

How Can I Log Headers in This Library?

connection = HubConnectionBuilder(url: url)
    .withLogging(minLogLevel: .debug)
    .withHubConnectionDelegate(delegate: self)
    .withHttpConnectionOptions { options in
        options.accessTokenProvider = { self.token }
        options.headers["Authorization"] = "Bearer \(self.token)"
    }
    .build()
connection = HubConnectionBuilder(url: url)
    .withLogging(minLogLevel: .debug)
    .withHubConnectionDelegate(delegate: self)
    .withHttpConnectionOptions { options in
        options.accessTokenProvider = { self.token }
    }
    .build()
connection = HubConnectionBuilder(url: url)
    .withLogging(minLogLevel: .debug)
    .withHubConnectionDelegate(delegate: self)
    .withHttpConnectionOptions { options in
        options.headers["Authorization"] = "Bearer \(self.token)"
    }
    .build()

stilus-tayyipguzel avatar Mar 05 '25 16:03 stilus-tayyipguzel

@moozzyk hi, I debugged it using Proxyman. The HTTP request header contains the Bearer token, but the HTTP switching and WebSocket headers do not include the Bearer token.

HTTP Header Image

Socket Header Image

stilus-tayyipguzel avatar Mar 06 '25 06:03 stilus-tayyipguzel

I haven't had a chance to look into this except for finding a test that validates that the token is sent to the server and is accessible:

https://github.com/moozzyk/SignalR-Client-Swift/blob/655e8bde45b4e3fc737c8ced9da242e3a76f3e69/Tests/SignalRClientTests/HubConnectionTests.swift#L971-L999

Here is the server code: https://github.com/moozzyk/SignalR-Client-Swift/blob/655e8bde45b4e3fc737c8ced9da242e3a76f3e69/Examples/TestServer/TestHub.cs#L84-L88

There seems to be some disconnect why the test passes but it doesn't work for you. I am not sure where it is.

IIRC, as an alternative, you can try sending the token in the query params (?access_token=xxx). This is how the JavaScript/TypeScript client works because the Websocket API available in the browser doesn't allow setting headers.

moozzyk avatar Mar 06 '25 07:03 moozzyk

Recently, I found the official package https://github.com/dotnet/signalr-client-swift

bingtice avatar Apr 01 '25 06:04 bingtice