Search code examples
c#asp.net-coresignalrbearer-token

HttpConnectionOptions.AccessTokenProvider not evaluated on every request


I have the following ASP.NET Core (.NET 7) SignalR test hub:

public class TestHub : Hub
{
    public async Task<string> GetToken()
    {
        return await Task.FromResult("testtoken");
    }

    [Authorize]
    public async Task<string> Echo(string message)
    {
        return await Task.FromResult($"Echoing {message}");
    }
}

As you can see, I want SignalR clients to retrieve a token from an initial GetToken call not requiring authorization, which is then passed (with a custom authentication scheme) in successive calls to authorize for them (Echo).

However, this does not seem possible if I create the SignalR connection in the client as follows:

string? token = null;

HubConnection connection = new HubConnectionBuilder()
    .WithUrl("http://localhost:5100/test", options =>
    {
        options.AccessTokenProvider = () => Task.FromResult(token);
    })
    .Build();
await connection.StartAsync();

Debugging shows the function set for the AccessTokenProvider is only executed when starting the connection initially, at which point token is still null - so no token will ever be passed for any call. I expected the function to run for every request (judging from similar functionality in gRPC).

This results in the initial call from the client succeeding as it does not need authorization, but the second failing:

token = await connection.InvokeAsync<string>("GetToken"); // fine
string result = await connection.InvokeAsync<string>("Echo", "Hello?"); // fails

What is the intended way to update a token in a single SignalR connection?

  • Is there a way to run the function set for the AccessTokenProvider again?
  • Can I set the token manually in the client?
  • Or do I have to create a separate hub and use two connections, one just for retrieving the token, and the other requiring the token from the start?

(I eventually also have to include this functionality in a C++ SignalR client, so if there is a solution and someone knows how to add it to the C++ SignalR client aswell, this information is very welcome, too.)


Solution

  • A previous answer (no longer available apparently due to AI plagiarism) made me realize that I can simply recreate the connection once I've retrieved the token with the unauthorized connection.

    That way, the AccessTokenProvider function - that is only executed once starting a connection - is run again for the new one, now with the token.

    This is an option that works fine so far for me (since I have no other ongoing calls than getting the token before going authorized). I do not know if this is the intended solution, so if someone knows how to set a token on running connections or there are other ways to accomplish this, please let me know in a better answer.