ASP.NET Core 3.0 アプリケーションで JWT を使用する
このページは、ASP.NET Core 2.0 アプリケーションを JWT でセキュアする (Auth0) の記事をベースに、ASP.NET Core 3.0 で JWT を使用する例を示すページです。
注意
- ASP.NET Core 3.0 の API アプリケーションを新規作成したときに追加されているサンプルコントローラ (WeatherForecastController) を JWT を使って認証付きにするものです。
- API へのアクセスを認証する までの流れを試しています。
例
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; // [追加]
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer; // [追加]
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens; // [追加]
namespace JWTAuthApi1
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// [追加] https://auth0.com/blog/jp-securing-asp-dot-net-core-2-applications-with-jwts/ と同じように認証設定をセットします。
// 「JwtBearerDefaults」はそのままだとエラーになるので、エラーの下線にカーソルを合わせたときに出てくるヒントアイコンから
// 「パッケージ 'Microsoft.AspNetCore.Authentication.JwtBearer' のインストール」 > 「最新バージョンのインストール」で導入します。
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication(); // [追加] 認証を使用するようにします。
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"Jwt": {
"Key": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
"Issuer": "https://example.com",
}
}
"Jwt"
の設定を追加しています。- Key は短すぎるとトークン生成処理の時 (後述の TokenController BuildToken() 時) にエラーになります。16文字以上あるとエラーにならないようです。
- Issuer は稼働するサービスの URL にするとよいですが、特に制約はありません。 (
"Issuer": "Issuer"
でもいい)
System.ArgumentOutOfRangeException: 'IDX10603: Decryption failed. Keys tried: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
Exceptions caught:
'[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]' '
Controller/WeatherForecastController.cs
(サンプルとして最初から追加されているコントローラ)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; // [追加]
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace JWTAuthApi1.Controllers
{
[Authorize] // [追加] 認証を必要にします。([Authorize])
[ApiController]
[Route("api/[controller]")] // [変更] 必要に応じてエンドポイントを変更します
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
}
- もともとのこのコントローラのルート設定は
[Route("[controller]")]
ですが、変えたほうがわかりやすい気がします。(下記の理由)- コントロールの追加 (Controllers フォルダを右クリック > 追加 > コントローラ) で追加したコントローラの初期のルート設定は
[Route("api/[controller]")]
- この後追加するトークン発行用のコントローラ (Auth0の記事に記載) のルート設定も
[Route("api/[controller]")]
- コントロールの追加 (Controllers フォルダを右クリック > 追加 > コントローラ) で追加したコントローラの初期のルート設定は
実行したときに最初に表示されるURLは、Properties/launchSettings.json
の「launchUrl
」の設定を変更することで変更できます。
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51735",
"sslPort": 44362
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"JWTAuthApi1": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/weatherforecast",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Controller/TokenController.cs
(トークン発行用のコントローラ。新規追加)
Auth0の記事に記載のある、トークン発行用のコントローラです。
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
namespace JWTAuthApi1.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class TokenController : ControllerBase
{
private IConfiguration _config;
public TokenController(IConfiguration config)
{
_config = config;
}
[AllowAnonymous]
[HttpPost]
public IActionResult CreateToken([FromBody]LoginModel login)
{
IActionResult response = Unauthorized();
var user = Authenticate(login);
if (user != null)
{
var tokenString = BuildToken(user);
response = Ok(new { token = tokenString });
}
return response;
}
private string BuildToken(UserModel user)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private UserModel Authenticate(LoginModel login)
{
UserModel user = null;
if (login.Username == "mario" && login.Password == "secret")
{
user = new UserModel { Name = "Mario Rossi", Email = "mario.rossi@domain.com" };
}
return user;
}
public class LoginModel
{
public string Username { get; set; }
public string Password { get; set; }
}
private class UserModel
{
public string Name { get; set; }
public string Email { get; set; }
public DateTime Birthdate { get; set; }
}
}
}
確認
下記を確認して、問題なければ OK です。
- 実行してみて /api/weatherforecast にアクセスした時、ブラウザに 401 エラーが表示される
- API へのアクセスを認証する のように Postman や cURL などで /api/token にアクセスしてみてトークンが取得できる
- 取得したトークンをもとに Postman や cURL などで /api/weatherforecast にアクセスしてみて JSON の応答がある