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)1
dotnet 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.
1
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
-
Modify
Program.cs
to register the JwtBearer service to the DI container -
Modify the
Program.cs
setting to require Bearer Token authentication and authorization for all Controllers.1
app.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.
1
dotnet run
Send the request via cURL.
1
curl -i http://localhost:5000/WeatherForecast
As a result, you will get the
HTTP/1.1 401 Unauthorized
response!
Use dotnet user-jwts create
to create a JWT Token
-
Run the following command to create a JWT Token
1
dotnet user-jwts create
The execution results are as follows:
1 2 3 4
New JWT saved with ID '3165f978'. Name: wakau Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Indha2F1Iiwic3ViIjoid2FrYXUiLCJqdGkiOiIzMTY1Zjk3OCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIsIm5iZiI6MTY4MjY4ODMxNiwiZXhwIjoxNjkwNTUwNzE2LCJpYXQiOjE2ODI2ODgzMTcsImlzcyI6ImRvdG5ldC11c2VyLWp3dHMifQ.WergzBiHz7UFAeBotkpaM9lpn8is0J5Fpm0D2yCfB-A
Default 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
Development
environment)1
dotnet run
Send the request via cURL.
1
curl -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Indha2F1Iiwic3ViIjoid2FrYXUiLCJqdGkiOiIzMTY1Zjk3OCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIsIm5iZiI6MTY4MjY4ODMxNiwiZXhwIjoxNjkwNTUwNzE2LCJpYXQiOjE2ODI2ODgzMTcsImlzcyI6ImRvdG5ldC11c2VyLWp3dHMifQ.WergzBiHz7UFAeBotkpaM9lpn8is0J5Fpm0D2yCfB-A" http://localhost:5000/WeatherForecast
As a result, you will get the
HTTP/1.1 200 OK
response!1 2 3 4 5 6 7
HTTP/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:Bearer
setting to theappsettings.Development.json
configuration 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.json
file (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
Development
environment). -
Automatically create the
%APPDATA%\Microsoft\UserSecrets\<secrets_GUID>\user-jwts.json
fileFor Linux/macOS it would be here
~/.microsoft/usersecrets/<secrets_GUID>/user-jwts.json
This 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
)1
dotnet user-jwts list
1 2 3 4 5 6 7
Project: '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
)1
dotnet user-jwts print 3165f978 --show-all
-
Delete the issued JWT Token (
dotnet user-jwts remove
)1
dotnet user-jwts remove 3165f978
-
Obtain the current JWT Token issuing key (
dotnet user-jwts key
)1
dotnet user-jwts key
1
Signing Key: '3oovYRPOGgclHgCL78QLhk0gYFoQro+4UQZripf02ec='
-
Reset the JWT Token Issuing Key currently in use
1
dotnet user-jwts key --reset --force
1
New signing key created: 'p3Nr5hi9yJtuH5/FAzKnoMrw09RYgE5ERLaM5WvgKok='
-
Reset the currently used JWT Token issuance key and use the customized Issuer name
1
dotnet user-jwts key --reset --force --issuer AspNetCoreUserJwtsDemo
1
New 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
)1
dotnet user-jwts create --issuer AspNetCoreUserJwtsDemo
1 2 3 4 5
New JWT saved with ID '75bf89c7'. Name: wakau Issuer: AspNetCoreUserJwtsDemo Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6Indha2F1Iiwic3ViIjoid2FrYXUiLCJqdGkiOiI3NWJmODljNyIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCIsIm5iZiI6MTY4MjY5MDQ2MCwiZXhwIjoxNjkwNTUyODYwLCJpYXQiOjE2ODI2OTA0NjEsImlzcyI6IkFzcE5ldENvcmVVc2VySnd0c0RlbW8ifQ.qGWR-uVUDaaBJfJAgNJGNcchLwPtmKI0W6W4OsX5KfA
If 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
)1
dotnet user-jwts clear
More application tips
-
Issuing JWT Token with
admin
anduser
rolesAdjust API Controller to add
[Authorize(Roles = "admin, manager")]
attributeSet either
admin
ormanager
to be authorized to access this APICreate a JWT Token and write Claims for
role
in the Token.1
dotnet user-jwts create --role admin --role user
Restart the site (be sure to start in the
Development
environment).1
dotnet run
Send the request via cURL.
1
curl -i -H "Authorization: Bearer {token}" http://localhost:5000/WeatherForecast
As a result, you will get the
HTTP/1.1 200 OK
response!1 2 3 4 5 6 7
HTTP/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
role
in the Token (onlyuser
role).1
dotnet user-jwts create --role user
Restart the site (be sure to start in the
Development
environment).1
dotnet run
Send the request via cURL.
1
curl -i -H "Authorization: Bearer {token}" http://localhost:5000/WeatherForecast
As a result, you will get the
HTTP/1.1 200 OK
response! -
Sign JWT Token containing
myapi:secrets
ScopeWhen issuing a JWT Token, you can restrict the scope of authorization of a specific JWT Token according to the issued
scope
claim, 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 7
builder.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
admin
ormanager
and that it also conforms to theMyAPIOnly
policy!