Call from Blazor an API behind Azure API Management with validate-jwt - jwt

Behind an Azure API Management I have a bunch of APIs. All my applications are using an Identity Server 4 to validate and authenticate users and applications. When a request to the API comes, I like to validate the jwt token before proceeding.
For this reason , in the API Management, under the Security section, I selected OpenID connect and then my Identity Server.
In the design of the APIs, I added the validation-jwt
and the policy is like that
<policies>
<inbound>
<validate-jwt header-name="Authorization"
failed-validation-httpcode="401" require-scheme="Bearer"
output-token-variable-name="jwt">
<openid-config url="https://idsrv4/.well-known/openid-configuration" />
</validate-jwt>
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>GET</method>
<method>POST</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Then, in the Program.cs of my Blazor WebAssembly, I added the following code
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
string apiEndpoint = builder.Configuration["Api:EndpointsUrl"];
string apiScope = builder.Configuration["Api:Scope"];
builder.Services.AddScoped<APIService>();
#region Configure HTTP Client
builder.Services.AddHttpClient("companiesAPI", cl =>
{
cl.BaseAddress = new Uri(apiEndpoint);
})
.AddHttpMessageHandler(sp =>
{
var handler = sp.GetService<AuthorizationMessageHandler>()
.ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7241" },
scopes: new[] { "220005_api" }
);
return handler;
});
builder.Services.AddScoped(sp => sp.GetService<IHttpClientFactory>().CreateClient("companiesAPI"));
#endregion
#region Configure Authentication and Authorization
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("oidc", options.ProviderOptions);
options.UserOptions.RoleClaim = "role";
})
.AddAccountClaimsPrincipalFactory<MultipleRoleClaimsPrincipalFactory<RemoteUserAccount>>();
builder.Services.AddAuthorizationCore();
#endregion
await builder.Build().RunAsync();
Finally, in the API service, I read the API.
public class APIService
{
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _options;
public APIService(HttpClient httpClient)
{
_httpClient = httpClient;
_options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}
public async Task<APIResponse> GetAttributeAsync(APIRequest apirequest)
{
try
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"typing");
var content =
new StringContent(JsonSerializer.Serialize(apirequest),
Encoding.UTF8, "application/json");
request.Content = content;
HttpResponseMessage responseMessage;
responseMessage = await _httpClient.SendAsync(request);
responseMessage.EnsureSuccessStatusCode();
if (responseMessage.IsSuccessStatusCode)
{
var responseContent = await responseMessage.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<APIResponse>(responseContent, _options);
}
else
return new APIResponse() { Success = false };
}
catch (Exception ex)
{
return new APIResponse() { Success = false };
}
}
}
Now, if I call the API, I have the following error:
TypeError: Failed to fetch
If from the API Management, I remove the validate-jwt, the application calls the API and receives the answer with no issues.
What is the correct configuration for the API Management? What is the correct code in the Blazor project to pass the jwt token?

In your application code you should get the JWT like this
// This gets the UserToken (JWT) to get data from Microsoft Graph for the scopes: User.Read & Mail.Read
// scope for API: API/GUID [YOUR API-URL-FROM-YOUR-APPREGISTRATION-IN-AAD]
var token = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "User.Read", "Mail.Read", "api://12345678-1234-1234-1234-123456789012/products" });
In the APIM => In the Inbound you are missing the required claims
<inbound>
<validate-jwt header-name="Authorization"
failed-validation-httpcode="401"
failed-validation-error-message="Unauthorized. Access token is missing or invalid!!!">
<openid-config url="https://login.microsoftonline.com/11a14169-89cc-44e8-95d7-xxxxxxxxxxxx/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>{client-id-of-Client-API-1-on-App-Registration}</value>
</claim>
</required-claims>
Check your JWT over here to set the right claim for 'aud' in your APIM
Have a look at this question for more code details. The code is GOOD!
How do I get the JWT in a Blazor Server App with Microsoft Identity Platform (AAD) to make external API-Management call and authorize with the jwt

Related

How do I get the JWT in a Blazor Server App with Microsoft Identity Platform (AAD) to make external API-Management call and authorize with the jwt

The situation I have:
Blazor Server App , .Net6.0.9 with Microsoft Identity Platform.
Blazor Server App is registered in the App Registration on Tenant-1
Client-API-1 is also resigered in the App Registration on Tenant-1
Login actions are done against/with the ClientId of the Client-API-1 registration and work fine.
In the API-Management I've added on the Inbound processing Polecies Validate-jwt like this:
(source of Microsoft)
<policies>
<inbound>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid!!!">
<openid-config url="https://login.microsoftonline.com/11a14169-89cc-44e8-95d7-xxxxxxxxxxxx/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>{client-id-of-Client-API-1-on-App-Registration}</value>
</claim>
</required-claims>
</validate-jwt>
In Service looks like this:
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Identity.Web;
using System.Net.Http.Headers;
namespace BlazorSAAppJwt.Data
{
public class ApimService : IApimService
{
private AuthenticationStateProvider _authenticationStateProvider { get; set; }
private readonly ITokenAcquisition _tokenAcquisition;
public ApimService(AuthenticationStateProvider AuthenticationStateProvider, ITokenAcquisition tokenAcquisition)
{
_authenticationStateProvider = AuthenticationStateProvider;
_tokenAcquisition = tokenAcquisition;
}
//public async Task<string?> GetResponseAsync(string path, CancellationToken cancellationToken)
public async Task<string?> GetResponseAsync(string path)
{
try
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
if (authState?.User?.Identity?.IsAuthenticated ?? false)
{
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://apimanagement.azure-api.net/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("email", authState.User.Identity.Name);
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{My APIM suvbscriptionkey}"); // APIM
httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Trace", "true");
// This gets the UserToken to get data from Microsoft Graph for the scopes: User.Read & Mail.Read
var token = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "User.Read", "Mail.Read" });
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token);
var dataRequest = await httpClient.GetAsync("https://graph.microsoft.com/beta/me");
string? userDisplayName = "";
if (dataRequest.IsSuccessStatusCode)
{
var userData = System.Text.Json.JsonDocument.Parse(await dataRequest.Content.ReadAsStreamAsync());
userDisplayName = userData.RootElement.GetProperty("displayName").GetString();
}
//Remove the previous Authorization-header for the Microsoft Graph call
httpClient.DefaultRequestHeaders.Remove("Authorization");
//Add the Application token to the Authorization for APIM
//NOTE!!! Here is where the JWT token should be used!!!!
string jwt = "How do I get the jwt here to add and send to the APIM";
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");
//HttpResponseMessage response = await httpClient.GetAsync($"{path.ToLower()}", cancellationToken);
HttpResponseMessage response = await httpClient.GetAsync($"{path.ToLower()}");
if (response.IsSuccessStatusCode)
{
string clientApiResult = await response.Content.ReadAsStringAsync();
return clientApiResult;
}
else
{
throw new UnauthorizedAccessException($"(Graph) User Display Name: {userDisplayName}" +
$"{Environment.NewLine}Response from APIM call: {response}");
}
}
else
{
// "The user is NOT authenticated.";
throw new UnauthorizedAccessException();
}
return default;
}
catch (Exception ex)
{
var iets = ex.Message;
throw;
}
}
}
}
I receive the UserDisplayName from the Graph API-call.
My program.cs
using BlazorSAAppJwt.Data;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
var builder = WebApplication.CreateBuilder(args);
var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ') ?? builder.Configuration["MicrosoftGraph:Scopes"]?.Split(' ');
var azureSection = builder.Configuration.GetSection("AzureAd");
var microsoftGraphSection = builder.Configuration.GetSection("MicrosoftGraph");
// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
//.AddMicrosoftGraph(microsoftGraphSection) // Nuget Microsoft.Identity.Web.MicrosoftGraph
.AddInMemoryTokenCaches();
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddTokenAcquisition();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddSingleton<ApimService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
What do I miss, and how do I setup my Blasor Server App to use the JWT token?
EDIT:
The API-calls on the APIM is not going to change and will call the Client-Api that is not exposed to the internet.
Who knows that the call:
var token = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "User.Read", "Mail.Read" });
Retruns the JWT... It does and what it is you need to use in the request header
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token);
And make the call
HttpResponseMessage response = await httpClient.GetAsync($"{path.ToLower()}", cancellationToken);

The authentication demand was rejected because the token had no audience attached

I want to authenticate the API resources using client credentials.
I have been able to generate the token successfully.
While sending the request for the API I logged the error and it says:
2021-06-10T00:47:19.1953056+05:45 [ERR] (OpenIddict.Validation.OpenIddictValidationDispatcher) The authentication demand was rejected because the token had no audience attached.
2021-06-10T00:47:19.1954307+05:45 [INF] (OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandler) "OpenIddict.Validation.AspNetCore" was not authenticated. Failure message: "An error occurred while authenticating the current request."
2021-06-10T00:47:19.1960031+05:45 [INF] (OpenIddict.Validation.OpenIddictValidationDispatcher) The response was successfully returned as a challenge response: "{
\"error\": \"invalid_token\",
\"error_description\": \"The specified token doesn't contain any audience.\",
\"error_uri\": \"https://documentation.openiddict.com/errors/ID2093\"
}".
2021-06-10T00:47:19.1960852+05:45 [INF] (OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandler) AuthenticationScheme: "OpenIddict.Validation.AspNetCore" was challenged.
What I am missing in my configuration? what is the correct way of using the client credentials grant type to secure the API resources with openiddict?
Resource Server Startup Configuration:
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
});
services.AddOpenIddict()
.AddValidation(options =>
{
options.SetIssuer("https://localhost:44301/");
options.AddAudiences("signal_system_web_resource");
options.UseIntrospection()
.SetClientId("signal_system_web_resource")
.SetClientSecret("846B62D0-DEF9-4215-A99D-86E6B8DAB342");
options.UseSystemNetHttp();
options.UseAspNetCore();
});
services.AddHttpClient();
return services;
}
The Client Configuration:
if (await manager.FindByClientIdAsync("nj-client") == null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "nj-client",
ClientSecret = "C4BBED05-A7C1-4759-99B5-0F84A29F0E08",
DisplayName = "Ninja Client Application",
Permissions =
{
Permissions.Endpoints.Token,
Permissions.GrantTypes.ClientCredentials
}
});
}
if (await manager.FindByClientIdAsync("signal_system_web_resource") == null)
{
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "signal_system_web_resource",
ClientSecret = "846B62D0-DEF9-4215-A99D-86E6B8DAB342",
Permissions =
{
Permissions.Endpoints.Introspection
}
};
await manager.CreateAsync(descriptor);
}
OpenIddictScopeDescriptor
var descriptor = new OpenIddictScopeDescriptor
{
Name = "signal.system.web",
Resources =
{
"signal_system_web_resource"
}
};
Resource Server API Controller
[Authorize]
[HttpGet("message")]
public async Task<IActionResult> GetMessage()
{
var identity = User.Identity as ClaimsIdentity;
if (identity == null)
{
return BadRequest();
}
return Content($"Signal System Web says that you have authorized access to resources belonging to {identity.Name}.");
}
Please help me through the error. any help or suggestion will be appreciated.
I am able to solve this problem by adding the resources while generating the token.
var principal = new ClaimsPrincipal(identity);
principal.SetResources("signal_system_web_resource");
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

Using JWT during SignalR connection with Blazor-WASM

I'm messing with Blazor + SignalR connection. I'd want to Authorize calls to SignalR by using JWT.
Basically I want to attach to SignalR calls the JWT
Here's my Blazor WASM SignalR Code
#page "/"
#using Microsoft.AspNetCore.SignalR.Client
#inject NavigationManager NavigationManager
#implements IDisposable
<div class="form-group">
<label>
User:
<input #bind="userInput" />
</label>
</div>
<div class="form-group">
<label>
Message:
<input #bind="messageInput" size="50" />
</label>
</div>
<button #onclick="Send" disabled="#(!IsConnected)">Send</button>
<hr>
<ul id="messagesList">
#foreach (var message in messages)
{
<li>#message</li>
}
</ul>
#code {
private HubConnection hubConnection;
private List<string> messages = new List<string>();
private string userInput;
private string messageInput;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
var encodedMsg = $"{user}: {message}";
messages.Add(encodedMsg);
StateHasChanged();
});
await hubConnection.StartAsync();
}
Task Send() =>
hubConnection.SendAsync("SendMessage", userInput, messageInput);
public bool IsConnected =>
hubConnection.State == HubConnectionState.Connected;
public void Dispose()
{
_ = hubConnection.DisposeAsync();
}
}
But I'm not sure how to attach JWT to this
I've seen this in Js version in section
Bearer token authentication in
this.connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat", { accessTokenFactory: () => this.loginToken })
.build();
https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1#authenticate-users-connecting-to-a-signalr-hub
What's Blazor's way of doing this?
I tried this:
var token = "eyJhb(...)";
hubConnection = new HubConnectionBuilder()
.WithUrl($"{Configuration["Url"]}/chathub", (HttpConnectionOptions x) =>
{
x.Headers.Add("Authorization", $"Bearer: {token}");
})
.Build();
But it threw error:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: The format of value 'Bearer: eyJh' is invalid.
System.FormatException: The format of value 'Bearer: eyJhbG' is invalid.
The solution was... to read the docs
var token = "eyJ";
hubConnection = new HubConnectionBuilder()
.WithUrl($"{Configuration["Url"]}/chathub?access_token={token}")
.Build();
Token is provided at connection estabilishing via url
We need to modify startup.cs to support OnMessageReceived
docs url:
https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1#authenticate-users-connecting-to-a-signalr-hub
services.AddAuthentication(options =>
{
// Identity made Cookie authentication the default.
// However, we want JWT Bearer Auth to be the default.
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// Configure the Authority to the expected value for your authentication provider
// This ensures the token is appropriately validated
options.Authority = /* TODO: Insert Authority URL here */;
// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or
// Server-Sent Events request comes in.
// Sending the access token in the query string is required due to
// a limitation in Browser APIs. We restrict it to only calls to the
// SignalR hub in this code.
// See https://learn.microsoft.com/aspnet/core/signalr/security#access-token-logging
// for more information about security considerations when using
// the query string to transmit the access token.
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});

Blazor jwt from client to server

I already have the token in local storage and ready to send to the web api where the controller or the method has en Authorize attribute this es the
Blazor client, How do I send the token ?
var token = Storage["token"];
await http.GetJsonAsync<string[]>("/api/authorizedController");
And how do I recive the token on the api ?
Does it happens automatically or do I have to do somehting ?
[Authorize]
[Route("api/[controller]")]
I found the answer here on stackoverflow in several places I just did not know how to look for it all I needed to do was to add this line of code
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
and it looked like this all together
var token = Storage["token"];
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
await http.GetJsonAsync<string[]>("/api/AutorizedController");
Mate, you also need code on the server to validate the bearer token in your request header on each request.
Try this:
[Route("api/[controller]")]
[Authorize]
public class AutorizedController: Controller
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityCore<IdentityUser>()
.AddEntityFrameworkStores<StoreContext>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(cfg =>
{
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = _config["Security:Tokens:Issuer"],
ValidateAudience = true,
ValidAudience = _config["Security:Tokens:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Security:Tokens:Key"])),
};
});
services.AddDbContext<StoreContext>();
services.AddMvc();
}

Generating a JWT token using AuthenticateAsync

I am trying to login using ClaimsPrincipal and then fetch a JWT in .net core 2.0. With my current code, I get the error from the result of the SignInAsync function:
"No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer"
Here is the controller I am currently using:
[Route("Login/{username}")]
public async Task<IActionResult> Login(string username)
{
var userClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
};
var principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
var sign = HttpContext.SignInAsync(principal);
await sign;
var res = await HttpContext.AuthenticateAsync();
var token = await HttpContext.GetTokenAsync("access_token");
return Json(token);
}
The login portion was tested and works well with cookies. However when I use the following code with JwtBearerDefaults.AuthenticationScheme in my startup.cs:
services.AddAuthentication(config => {
config.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
config.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(config =>
{
config.TokenValidationParameters = Token.tokenValidationParameters;
config.RequireHttpsMetadata = false;
config.SaveToken = true;
});
I get the error from the result of the SignInAsync function:
"No IAuthenticationSignInHandler is configured to handle sign in for the scheme: Bearer"
My Token class was created with the help of a code I found online (at JWT on .NET Core 2.0) and is defined as follows:
public static class Token
{
public static TokenValidationParameters tokenValidationParameters {
get
{
return new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = GetSignInKey(),
ValidateIssuer = true,
ValidIssuer = GetIssuer(),
ValidateAudience = true,
ValidAudience = GetAudience(),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
}
}
static private SymmetricSecurityKey GetSignInKey()
{
const string secretKey = "very_long_very_secret_secret";
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
return signingKey;
}
static private string GetIssuer()
{
return "issuer";
}
static private string GetAudience()
{
return "audience";
}
}
If I understand it correctly from looking at the source code for JwtBearerHandler, it does not implement IAuthenticationSignInHandler, which is why you are getting this error. Call to SignInAsync is designed to persist authentication information, such as created auth cookie which, for instance, is exactly what CookieAuthenticationHandler does. But for JWT there is no single well-known place to store the token, hence no reason to call SignInAsync at all. Instead of that, grab the token and pass it back to the browser. Assuming you are redirecting, you can tuck it into a query string. Assuming browser application is an SPA (i.e. Angular-based) and you need tokens for AJAX calls, you should store token in the SPA and send it with every API request. There are some good tutorials on how to use JWT with SPAs of different types, such as this: https://medium.com/beautiful-angular/angular-2-and-jwt-authentication-d30c21a2f24f
Keep in mind that JwtBearerHandler expects to find Authentication header with Bearer in it, so if your AJAX calls are placing token in query string, you will need to supply JwtBearerEvents.OnMessageReceived implementation that will take token from query string and put it in the header.
A signed token can be created using the JwtSecurityTokenHandler.
var handler = new JwtSecurityTokenHandler();
var jwt = handler.CreateJwtSecurityToken(new SecurityTokenDescriptor
{
Expires = DateTime.UtcNow.Add(Expiary),
Subject = new ClaimsIdentity(claims, "local"),
SigningCredentials = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256)
});
return handler.WriteToken(jwt);