title: HubConnection Authentication Using Identity From Windows Service
body: <p>I have a Hub that serves up near real-time data. I want the provider of the real-time data to be able to send messages to all connected clients. The problem I am running into is authenticating the connection because the provider is a Windows Service. So, I am creating a client using <code>HubConnectionBuilder</code>. When I attempt to connect, I am getting a 401 authentication failure which is expected.</p>
  <p>I am trying to figure out how to use the existing Identity authentication that exists in the shared Entity Framework database. Is there a way to get an authentication token using UserManager or SigninManager from this stand-alone service? Or is there a better approach to allow the HubConnection to authenticate?</p>
  <p>Here is the connection code I am using:</p>
  <pre><code>var notifyConnection = new HubConnectionBuilder()
      .WithUrl(&quot;https://localhost:5001/notify&quot;, options =&gt;
      {
          options.AccessTokenProvider = () =&gt; Task.FromResult(_myAccessToken);
      })
      .WithAutomaticReconnect()
      .Build();
  
  notifyConnection.Closed += async (error) =&gt;
  {
      if (Debug) _logger.LogInformation(&quot;Hub connection closed: &quot; + error.Message);
      await Task.Delay(new Random().Next(0, 5) * 1000);
      await notifyConnection.StartAsync();
  };
  try
  {
      var task = notifyConnection.StartAsync();
      task.Wait();
  }
  catch (Exception ex)
  {
      if (Debug) _logger.LogInformation(&quot;Hub connection start error: &quot; + ex.Message);
  }
  </code></pre>
  <p><code>_myAccessToken</code> is not defined. I am trying to figure out how to get a valid access token that will authenticate properly.</p>
  <p>EDIT:</p>
  <p>I have tried adding JWT authentication. I can verify that the bearer token is getting generated properly. But it doesn't appear to be validated on the <code>Hub</code>.</p>
  <p>I have added the following to the services configuration:</p>
  <pre><code>var key = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes(Configuration[&quot;JwtKey&quot;]));
  services.AddAuthentication().AddJwtBearer(options =&gt;
  {
      options.TokenValidationParameters = new TokenValidationParameters
      {
          LifetimeValidator = (before, expires, token, parameters) =&gt;
              expires &gt; DateTime.UtcNow,
          ValidateAudience = false,
          ValidateIssuer = false,
          ValidateActor = false,
          ValidateLifetime = true,
          IssuerSigningKey = key,
          NameClaimType = ClaimTypes.NameIdentifier
      };
  
      options.Events = new JwtBearerEvents
      {
          OnMessageReceived = context =&gt;
          {
              var accessToken = context.Request.Query[&quot;access_token&quot;];
              if (!string.IsNullOrEmpty(accessToken))
              {
                  context.Token = accessToken;
              }
              return Task.CompletedTask;
          }
      };
  });
  
  services.AddAuthorization(options =&gt;
  {
      options.FallbackPolicy = new AuthorizationPolicyBuilder()
          .RequireAuthenticatedUser()
          .Build(); 
      options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =&gt;
          {
              policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
              policy.RequireClaim(ClaimTypes.NameIdentifier);
          });
  });
  </code></pre>
  <p>I have also updated the <code>HubConnectionBuilder</code> to use:</p>
  <pre><code>notifyConnection = new HubConnectionBuilder()
    .WithUrl(baseUrl + &quot;/notify&quot;, options =&gt;
    {
        options.AccessTokenProvider = async () =&gt;
        {
            var stringData = JsonConvert.SerializeObject(new { username = &quot;****&quot;, password = &quot;****&quot; });
            var content = new StringContent(stringData);
            content.Headers.ContentType = new MediaTypeHeaderValue(&quot;application/json&quot;);
            var response = await httpClient.PostAsync(baseUrl + &quot;/api/token&quot;, content);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        };
    })
    .WithAutomaticReconnect()
    .Build();
  </code></pre>
  <p>It still returns a 401. When I tried to force the <code>Authorize</code> attribute to:</p>
  <pre><code>[Authorize(AuthenticationSchemes = &quot;Bearer,Cookies&quot;)]
  </code></pre>
  <p>It then returns a 500 error.</p>