I'm attempting to pass an access_token through a HubConnection
in a C# .NET Client. However, the result is not consistent with what I'm seeing through a TypeScript client. And this inconsistency results in a failed Authorization in the C# .NET Client but a successful authorization in the TypeScript client.
Here is the relevant code:
TypeScript
var builder = new signalr.HubConnectionBuilder();
builder.withUrl(hubUrl, {accessTokenFactory: () => token});
C#
var builder = new HubConnectionBuilder();
builder.WithUrl(url, o => {
o.AccessTokenProvider = () => Task.FromResult(_token);
//I've tried the following as well
//o.Headers.Add("Authorization", "Bearer " + _token);
});
The TypeScript code generates an HTTP request as such:
POST http://localhost:5000/machine/negotiate?negotiateVersion=1 HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Content-Length: 0
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJleHAiOjE1NzYxODYwMTYsImlzcyI6Im5TY3J5cHQsIEluYy4iLCJhdWQiOiJuU3R1ZGlvIFVzZXIifQ.qxAzu-NgzlnfCqyysiML4Z0_s6UBTeRb7wcuGno9rk4
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) nstudio-pro/1.0.0 Chrome/78.0.3905.1 Electron/7.0.0 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://localhost:3000
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost:3000/main_window
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US
Cookie: .AspNetCore.Identity.Application=CfDJ8Dp0ZrsdLf1KpYnUb_iitp4gyw5TR5uJhBjiI8pFnzSSEM9ALjY6XbRVrMURJ_NrkK9IAD0Xlqy3l6HEQNJkfGG7vwCRfj5x4NjEW4Msm5GoQMuhG6epa3Q3r8QDhdC4z3tJSS0bRZ_EXvmnnnVWYvw4lILddLABWnf3leeMXrKUmq6AUAJPy1SVgj4fJQ8BGOo5HLPZDLZxN-m3ZV0jUaDkOf_mosTAz7JTjI53bAlxD0hi78YYzkVfpa8dEs8gXOTD85f96_m5DGGoMMCnvsjMP6ST1Q87rWHCCsxUPPLaH_A2xc6JpUqvzV-Pur6KtE8oFmcen4jq7h0kL2akXWUvTApZIxY7lFvFx7x4-8andT1DP7T3tRNdWnoRNRotSoQCp4HtS3Cz0GRwcyaKyhuFBjdUFMj1H0FKDYOEJEiarVMX0bElqgTjGGr7ZiOPyTJq1yHmCOraqdbP7YMycTWfC4F1tPXS0v4KxxNo8F2o31MYlhCx_sTIgEjJUHjdh9iugr401GYzazV3reL4M64YAliZ3fynzXf7ZNVqwUg-OvDzXd0nba4E3BVd_hQDwlssaWYq0DAZvrwO56iUwv1y9e-wehaH6OzocmvujVLX_HYG20BsXN6YdLiPPfhqNdkay50AaTuvXF2kq-exJTEnYBuc9U6eTWn9--mEFXKe4VQlTnn97AGvLfnOt_QrhUK4Pc88Z9q3hHNu7MNhfnE
Whereas the C# code creates:
POST http://localhost:5000/machine/negotiate?negotiateVersion=1 HTTP/1.1
Host: localhost:5000
User-Agent: Microsoft.AspNetCore.Http.Connections.Client/3.1.0
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJleHAiOjE1NzYxODYxOTIsImlzcyI6Im5TY3J5cHQsIEluYy4iLCJhdWQiOiJuU3R1ZGlvIFVzZXIifQ.i_m-hnyZfPmFoUSX9VHPjSk-LP7UtpJlFafEuJBR66Q
X-Requested-With: XMLHttpRequest
Content-Length: 0
Then, the next requests are as such:
TypeScript
GET http://localhost:5000/machine?id=n2xReT4vy3KPuakBsaSBuA&access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJleHAiOjE1NzYxODYwMTYsImlzcyI6Im5TY3J5cHQsIEluYy4iLCJhdWQiOiJuU3R1ZGlvIFVzZXIifQ.qxAzu-NgzlnfCqyysiML4Z0_s6UBTeRb7wcuGno9rk4 HTTP/1.1
Host: localhost:5000
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) nstudio-pro/1.0.0 Chrome/78.0.3905.1 Electron/7.0.0 Safari/537.36
Upgrade: websocket
Origin: http://localhost:3000
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US
Cookie: .AspNetCore.Identity.Application=CfDJ8Dp0ZrsdLf1KpYnUb_iitp4gyw5TR5uJhBjiI8pFnzSSEM9ALjY6XbRVrMURJ_NrkK9IAD0Xlqy3l6HEQNJkfGG7vwCRfj5x4NjEW4Msm5GoQMuhG6epa3Q3r8QDhdC4z3tJSS0bRZ_EXvmnnnVWYvw4lILddLABWnf3leeMXrKUmq6AUAJPy1SVgj4fJQ8BGOo5HLPZDLZxN-m3ZV0jUaDkOf_mosTAz7JTjI53bAlxD0hi78YYzkVfpa8dEs8gXOTD85f96_m5DGGoMMCnvsjMP6ST1Q87rWHCCsxUPPLaH_A2xc6JpUqvzV-Pur6KtE8oFmcen4jq7h0kL2akXWUvTApZIxY7lFvFx7x4-8andT1DP7T3tRNdWnoRNRotSoQCp4HtS3Cz0GRwcyaKyhuFBjdUFMj1H0FKDYOEJEiarVMX0bElqgTjGGr7ZiOPyTJq1yHmCOraqdbP7YMycTWfC4F1tPXS0v4KxxNo8F2o31MYlhCx_sTIgEjJUHjdh9iugr401GYzazV3reL4M64YAliZ3fynzXf7ZNVqwUg-OvDzXd0nba4E3BVd_hQDwlssaWYq0DAZvrwO56iUwv1y9e-wehaH6OzocmvujVLX_HYG20BsXN6YdLiPPfhqNdkay50AaTuvXF2kq-exJTEnYBuc9U6eTWn9--mEFXKe4VQlTnn97AGvLfnOt_QrhUK4Pc88Z9q3hHNu7MNhfnE
Sec-WebSocket-Key: 7XIeUeqljhqwC4AencqJxg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
C#
GET http://localhost:5000/machine?id=ZB_6NJvMFDpt0cRhoqiWkw HTTP/1.1
Host: localhost:5000
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJleHAiOjE1NzYxODYxOTIsImlzcyI6Im5TY3J5cHQsIEluYy4iLCJhdWQiOiJuU3R1ZGlvIFVzZXIifQ.i_m-hnyZfPmFoUSX9VHPjSk-LP7UtpJlFafEuJBR66Q
X-Requested-With: XMLHttpRequest
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: yUAfoo7mcEy8e/j4irLl5w==
My Startup.cs on the Server looks like:
services.AddAuthentication(jwtAuthScheme)
.AddJwtBearer(jwtAuthScheme, options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
if (context.Request.Query.ContainsKey("access_token"))
{
context.Token = context.Request.Query["access_token"];
}
else if (context.Request.Headers.TryGetValue("Authorization", out var value) && value.Count > 0)
{
context.Token = value[0].Substring("Bearer ".Length);
}
return Task.CompletedTask;
}
};
options.TokenValidationParameters = new TokenValidationParameters
{
LifetimeValidator = (before, expires, token, param) =>
{
return expires > DateTime.UtcNow;
},
ValidateAudience = true,
ValidAudience = jwtHandler.TokenAudience,
ValidateIssuer = true,
ValidIssuer = jwtHandler.TokenIssuer,
ValidateActor = false,
ValidateLifetime = true,
IssuerSigningKey = jwtKey,
SaveSigninToken = true
};
options.SaveToken = true;
options.Audience = jwtHandler.TokenAudience;
});
services.AddAuthorization();
Is there anything that I'm doing wrong? Why does the Server fail to grab the token from the header and why does the C# .NET Client not place the token in the query string?