Starting with ASP.NET Core 7.0, .NET SDK 7 has built-in support for the dotnet user-jwts command, which helps you manage the keys and JWT Tokens you need during development.

Create example project
-
lock the .NET SDK version to
7.0.203(and subsequent versions)1dotnet new globaljson --sdk-version 7.0.203 --roll-forward latestFeature -
Building ASP.NET Core Web API Projects
Initial authentication and authorization
-
Install the Microsoft.AspNetCore.Authentication.JwtBearer package.
1dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer -
Modify
Program.csto register the JwtBearer service to the DI container -
Modify the
Program.cssetting to require Bearer Token authentication and authorization for all Controllers.1app.MapControllers().RequireAuthorization(); -
Theoretically, all APIs are inaccessible
First adjust the Properties/launchSettings.json file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15{ "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { "http": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": false, "launchUrl": "WeatherForecast", "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }Launch the website.
1dotnet runSend the request via cURL.
1curl -i http://localhost:5000/WeatherForecastAs a result, you will get the
HTTP/1.1 401 Unauthorizedresponse!
Use dotnet user-jwts create to create a JWT Token
-
Run the following command to create a JWT Token
1dotnet user-jwts createThe execution results are as follows:
1 2 3 4New JWT saved with ID '3165f978'. Name: wakau Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Indha2F1Iiwic3ViIjoid2FrYXUiLCJqdGkiOiIzMTY1Zjk3OCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIsIm5iZiI6MTY4MjY4ODMxNiwiZXhwIjoxNjkwNTUwNzE2LCJpYXQiOjE2ODI2ODgzMTcsImlzcyI6ImRvdG5ldC11c2VyLWp3dHMifQ.WergzBiHz7UFAeBotkpaM9lpn8is0J5Fpm0D2yCfB-ADefault Issuer is
dotnet-user-jwts. -
Check if you can use this Token to request the API
Restart the site (be sure to start in the
Developmentenvironment)1dotnet runSend the request via cURL.
1curl -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Indha2F1Iiwic3ViIjoid2FrYXUiLCJqdGkiOiIzMTY1Zjk3OCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIsIm5iZiI6MTY4MjY4ODMxNiwiZXhwIjoxNjkwNTUwNzE2LCJpYXQiOjE2ODI2ODgzMTcsImlzcyI6ImRvdG5ldC11c2VyLWp3dHMifQ.WergzBiHz7UFAeBotkpaM9lpn8is0J5Fpm0D2yCfB-A" http://localhost:5000/WeatherForecastAs a result, you will get the
HTTP/1.1 200 OKresponse!1 2 3 4 5 6 7HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 28 Apr 2023 13:20:48 GMT Server: Kestrel Transfer-Encoding: chunked [{"date":"2023-04-29","temperatureC":-18,"temperatureF":0,"summary":"Bracing"},{"date":"2023-04-30","temperatureC":54,"temperatureF":129,"summary":"Bracing"},{"date":"2023-05-01","temperatureC":51,"temperatureF":123,"summary":"Balmy"},{"date":"2023-05-02","temperatureC":35,"temperatureF":94,"summary":"Warm"},{"date":"2023-05-03","temperatureC":5,"temperatureF":40,"summary":"Chilly"}]Wouldn’t you think, “It’s that simple?”
About what dotnet user-jwts create does behind the scenes
In fact, this dotnet user-jwts create command does more than just create a Token.
-
Automatically add the following
Authentication:Schemes:Bearersetting to theappsettings.Development.jsonconfiguration file. -
Automatically register a set of
<UserSecretsId>secret numbers required by the Secret Manager inAspNetCoreUserJwtsDemo.csproj.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <UserSecretsId>98ca3101-9491-4d1c-98d9-d44e2da12ea0</UserSecretsId> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.5" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.5" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> </ItemGroup> </Project> -
Automatically create
%APPDATA%\Microsoft\UserSecrets\<secrets_GUID>\secrets.jsonfile (Secret Manager)For Linux/macOS it would be here
~/.microsoft/usersecrets/<secrets_GUID>/secrets.json.This contains the JWT SigningKeys required during development! (only used in the
Developmentenvironment). -
Automatically create the
%APPDATA%\Microsoft\UserSecrets\<secrets_GUID>\user-jwts.jsonfileFor Linux/macOS it would be here
~/.microsoft/usersecrets/<secrets_GUID>/user-jwts.jsonThis has all the JWT Tokens created by
dotnet user-jwts create, examples are below.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15{ "3165f978": { "Id": "3165f978", "Scheme": "Bearer", "Name": "wakau", "Audience": "http://localhost:5000", "NotBefore": "2023-04-28T13:25:16+00:00", "Expires": "2023-07-28T13:25:16+00:00", "Issued": "2023-04-28T13:25:17+00:00", "Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Indha2F1Iiwic3ViIjoid2FrYXUiLCJqdGkiOiIzMTY1Zjk3OCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIsIm5iZiI6MTY4MjY4ODMxNiwiZXhwIjoxNjkwNTUwNzE2LCJpYXQiOjE2ODI2ODgzMTcsImlzcyI6ImRvdG5ldC11c2VyLWp3dHMifQ.WergzBiHz7UFAeBotkpaM9lpn8is0J5Fpm0D2yCfB-A", "Scopes": [], "Roles": [], "CustomClaims": {} } }It is expected that JWT Tokens will be issued for 91 days
Common dotnet user-jwts command usage
Assume our JWT ID is 3165f978.
-
List all issued JWT Tokens (
dotnet user-jwts list)1dotnet user-jwts list1 2 3 4 5 6 7Project: 'G:\Projects\AspNetCoreUserJwtsDemo\AspNetCoreUserJwtsDemo.csproj' User Secrets ID: '98ca3101-9491-4d1c-98d9-d44e2da12ea0' --------------------------------------------------------------------------------------------------------------------------------------- | ID | Scheme | Audience(s) | Issued On | Expires On | --------------------------------------------------------------------------------------------------------------------------------------- | 3165f978 | Bearer | http://localhost:5000 | 2023-04-28T13:25:17.0000000+00:00 | 2023-07-28T13:25:16.0000000+00:00 | --------------------------------------------------------------------------------------------------------------------------------------- -
Show full information of JWT Token (
dotnet user-jwts print)1dotnet user-jwts print 3165f978 --show-all -
Delete the issued JWT Token (
dotnet user-jwts remove)1dotnet user-jwts remove 3165f978 -
Obtain the current JWT Token issuing key (
dotnet user-jwts key)1dotnet user-jwts key1Signing Key: '3oovYRPOGgclHgCL78QLhk0gYFoQro+4UQZripf02ec=' -
Reset the JWT Token Issuing Key currently in use
1dotnet user-jwts key --reset --force1New signing key created: 'p3Nr5hi9yJtuH5/FAzKnoMrw09RYgE5ERLaM5WvgKok=' -
Reset the currently used JWT Token issuance key and use the customized Issuer name
1dotnet user-jwts key --reset --force --issuer AspNetCoreUserJwtsDemo1New signing key created: 'p3Nr5hi9yJtuH5/FAzKnoMrw09RYgE5ERLaM5WvgKok='If you create multiple sets of Issuer with different names, he will create multiple signature keys in
secrets.json. -
Create Issuer as JWT Token for AspNetCoreUserJwtsDemo (
dotnet user-jwts create)1dotnet user-jwts create --issuer AspNetCoreUserJwtsDemo1 2 3 4 5New JWT saved with ID '75bf89c7'. Name: wakau Issuer: AspNetCoreUserJwtsDemo Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Indha2F1Iiwic3ViIjoid2FrYXUiLCJqdGkiOiI3NWJmODljNyIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIsIm5iZiI6MTY4MjY5MDQ2MCwiZXhwIjoxNjkwNTUyODYwLCJpYXQiOjE2ODI2OTA0NjEsImlzcyI6IkFzcE5ldENvcmVVc2VySnd0c0RlbW8ifQ.qGWR-uVUDaaBJfJAgNJGNcchLwPtmKI0W6W4OsX5KfAIf you create multiple sets of Issuer with different names, he will create multiple signature keys in
secrets.json. -
Clear all issued JWT Token (
dotnet user-jwts clear)1dotnet user-jwts clear
More application tips
-
Issuing JWT Token with
adminanduserrolesAdjust API Controller to add
[Authorize(Roles = "admin, manager")]attributeSet either
adminormanagerto be authorized to access this APICreate a JWT Token and write Claims for
rolein the Token.1dotnet user-jwts create --role admin --role userRestart the site (be sure to start in the
Developmentenvironment).1dotnet runSend the request via cURL.
1curl -i -H "Authorization: Bearer {token}" http://localhost:5000/WeatherForecastAs a result, you will get the
HTTP/1.1 200 OKresponse!1 2 3 4 5 6 7HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 28 Apr 2023 13:20:48 GMT Server: Kestrel Transfer-Encoding: chunked [{"date":"2023-04-29","temperatureC":-18,"temperatureF":0,"summary":"Bracing"},{"date":"2023-04-30","temperatureC":54,"temperatureF":129,"summary":"Bracing"},{"date":"2023-05-01","temperatureC":51,"temperatureF":123,"summary":"Balmy"},{"date":"2023-05-02","temperatureC":35,"temperatureF":94,"summary":"Warm"},{"date":"2023-05-03","temperatureC":5,"temperatureF":40,"summary":"Chilly"}]Create a JWT Token and write Claims for
rolein the Token (onlyuserrole).1dotnet user-jwts create --role userRestart the site (be sure to start in the
Developmentenvironment).1dotnet runSend the request via cURL.
1curl -i -H "Authorization: Bearer {token}" http://localhost:5000/WeatherForecastAs a result, you will get the
HTTP/1.1 200 OKresponse! -
Sign JWT Token containing
myapi:secretsScopeWhen issuing a JWT Token, you can restrict the scope of authorization of a specific JWT Token according to the issued
scopeclaim, and of course we need to define the corresponding Policy authorization policy in the program.First, declare the policy in the DI container: (
Program.cs).1 2 3 4 5 6 7builder.Services.AddAuthentication("Bearer").AddJwtBearer(); builder.Services.AddAuthorization(options => { options.AddPolicy("MyAPIOnly", policy => policy.RequireClaim("scope","myapi:secrets")); options.AddPolicy("AdminOnly", policy => policy.RequireClaim(ClaimTypes.Role, "admin")); options.AddPolicy("UserOnly", policy => policy.RequireClaim(ClaimTypes.Role, "user")); });
Create JWT Token and write Claims to scope in Token (only myapi:secrets range)
|
|
Adjust the API Controller to add [Authorize(Roles = "admin, manager", Policy = "MyAPIOnly")] properties.
Note: The above syntax needs to be interpreted in such a way that the role is either
adminormanagerand that it also conforms to theMyAPIOnlypolicy!