title: Advise on setting up ASP.NET Core Identity for server side Blazor with Web API
body: <p>I'm starting the process of modernising an ASP.NET MVC 5 project (with Web API) written in Visual Basic to a C# version utilising server side Blazor and would be grateful if my approach to using identity is correct, if there are better ways to achieve what I am trying to do or if I'm fundamentally misunderstanding what I'm doing!</p>
  <p>To separate concerns and improve security I have split the solution into three projects:</p>
  <ol>
  <li><strong>API</strong>: Web API project that does all the database work. Requests could come from the Blazor application (below) or from other applications</li>
  <li><strong>Blazor</strong>: server-side Blazor app. This is the 'main' UI for the application. It will need to call methods on the API</li>
  <li><strong>Models</strong>: This contains the shared model classes that will be used by the other projects</li>
  </ol>
  <p>Before I get into the nitty-gritty of the project itself I wanted some advise on how to implement authentication. This is how I have gone about it so far:</p>
  <ul>
  <li>The API application is using JWT bearer tokens:</li>
  </ul>
  <p>Program.cs</p>
  <pre><code>// Identity
  builder.Services.AddIdentity&lt;PTSUser, IdentityRole&gt;()
      .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()
      .AddDefaultTokenProviders();
  
  // Authentication
  builder.Services.AddAuthentication(options =&gt;
  {
      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
      options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
      options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
  })
  
  // JWT bearer
  .AddJwtBearer(options =&gt;
  {
      options.SaveToken = true;
      options.RequireHttpsMetadata = false;
      options.TokenValidationParameters = new TokenValidationParameters()
      {
          ValidateIssuer = false,
          ValidateAudience = false,
          ValidateLifetime = true,
          ValidateIssuerSigningKey = true,
          ClockSkew = TimeSpan.FromSeconds(10),
          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration[&quot;JWT:SecretKey&quot;]))
      };
  });
  </code></pre>
  <p>I have created an endpoint to login that generates a token that can be used to call endpoints protected with the [Authorize] attribute:</p>
  <pre><code>[HttpPost]
  [Route(&quot;Login&quot;)]
  public async Task&lt;IActionResult&gt; Login([FromBody] LoginModel model)
  {
      PTSUser user = await _userManager.FindByNameAsync(model.Username);
      Microsoft.AspNetCore.Identity.SignInResult result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, true);
  
      if (result.Succeeded)
      {
          SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor
                  {
                      Subject = (await _signInManager.CreateUserPrincipalAsync(user)).Identities.First(),
                      Expires = DateTime.Now.AddMinutes(double.Parse(_configuration[&quot;JWT:ValidMins&quot;])),
                      SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
                          _configuration[&quot;JWT:SecretKey&quot;])),
                          SecurityAlgorithms.HmacSha256Signature)
                  };
  
          JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
          SecurityToken secToken = new JwtSecurityTokenHandler().CreateToken(descriptor);
          return Ok(new { token = handler.WriteToken(secToken) });
      }
  
      return Unauthorized();
  }
  </code></pre>
  <p>I have tested calls to protected API routes using Postman and get the results that I would expect.</p>
  <p>For the Blazor project, I used the Identity Scaffolding as a starting point to generate cookie authentication:</p>
  <pre><code>// Identity configuration
  builder.Services.AddDefaultIdentity&lt;PTSUser&gt;(options =&gt; options.SignIn.RequireConfirmedAccount = true)
      .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();
  </code></pre>
  <p>I then modified the login Razor view code to generate and store a JWT Token that can be used (via a service) when I need to call protected API endpoints:</p>
  <pre><code>private async Task&lt;Microsoft.AspNetCore.Identity.SignInResult&gt; Login(string Username, string Password)
          {
              var result = await _signInManager.PasswordSignInAsync(Username, Password, false, false);
              if (result.Succeeded)
              {
                  PTSUser user = await _userManager.FindByNameAsync(Username);
                  SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor
                  {
                      Subject = (await _signInManager.CreateUserPrincipalAsync(user)).Identities.First(),
                      Expires = DateTime.Now.AddMinutes(double.Parse(_configuration[&quot;JWT:ValidMins&quot;])),
                      SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
                      _configuration[&quot;JWT:SecretKey&quot;])),
                      SecurityAlgorithms.HmacSha256Signature)
                  };
                  JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                  SecurityToken secToken = new JwtSecurityTokenHandler().CreateToken(descriptor);
  
                  await _userManager.RemoveAuthenticationTokenAsync(user, &quot;PTSApi&quot;, &quot;APIToken&quot;);
                  await _userManager.SetAuthenticationTokenAsync(user, &quot;PTSApi&quot;, &quot;APIToken&quot;, handler.WriteToken(secToken));
                  _logger.LogInformation(&quot;User logged in.&quot;);
                  return result;// LocalRedirect(returnUrl);
              }
  
              if (result.IsLockedOut)
              {
                  _logger.LogWarning(&quot;User account locked out.&quot;);
                  return result; //RedirectToPage(&quot;./Lockout&quot;);
              }
              else
              {
                  ModelState.AddModelError(string.Empty, &quot;Invalid login attempt.&quot;);
                  return result;// Page();
              }
          }
  </code></pre>
  <p>The service then uses an httpClient to call the API: Using the Token as the JWT Bearer Token the API expects:</p>
  <pre><code>public async Task&lt;IEnumerable&lt;Prescription&gt;&gt; GetPrescriptions()
          {
              _apiUserToken = await SetToken();
  
              _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(&quot;Bearer&quot;, _apiUserToken);
              return await _httpClient.GetFromJsonAsync&lt;Prescription[]&gt;(&quot;api/prescriptions/GetPrescriptions&quot;);
          }
  
  
  private async Task&lt;string&gt; SetToken() {
              //All API calls will need a JWT Token for authentication: get, check &amp; regenerate token here.
              var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);
              var apiToken = await _userManager.GetAuthenticationTokenAsync(user, &quot;PTSApi&quot;, &quot;APIToken&quot;);
              
              JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
              SecurityToken curToken = new JwtSecurityTokenHandler().ReadToken(apiToken);
  
              if (DateTime.Now &lt;= curToken.ValidTo)
                  {
                      return apiToken;
                  }
                  else
                  {
                      //regenerate token
                      SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor
                      {
                          Subject = (await _signInManager.CreateUserPrincipalAsync(user)).Identities.First(),
                          Expires = DateTime.Now.AddMinutes(double.Parse(_configuration[&quot;JWT:ValidMins&quot;])),
                          SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
                              _configuration[&quot;JWT:SecretKey&quot;])),
                              SecurityAlgorithms.HmacSha256Signature)
                      };
                      
                      SecurityToken secToken = new JwtSecurityTokenHandler().CreateToken(descriptor);
  
                      await _userManager.RemoveAuthenticationTokenAsync(user, &quot;PTSApi&quot;, &quot;APIToken&quot;);
                      await _userManager.SetAuthenticationTokenAsync(user, &quot;PTSApi&quot;, &quot;APIToken&quot;, handler.WriteToken(secToken));
                      apiToken = await _userManager.GetAuthenticationTokenAsync(user, &quot;PTSApi&quot;, &quot;APIToken&quot;);
  
                      return apiToken;
                  }
          }
  </code></pre>
  <p>Here is where I get to my first concern that I'm not understanding something: I'm only able to check if the Token has expired here - not if it's valid: I think I used a method on the UserManager to verify the token but it always came back invalid.</p>
  <p>Also - Is there any way for the Blazor App to pass the authentication cookie to the WebAPI rather than / instead of using a JWT bearer token? That way I wouldn't have to bother with Tokens at all in the Blazor app?</p>
  <p>My next task is to try getting 'Mixed-mode' authentication up and running (which I accomplished using MohammadYounes.Owin.Security.MixedAuth in the previous MVC iteration of the app)....but I wanted to sanity check my approach so far before going any further.</p>
  <p>Any advice / best practice would be much appreciated!</p>