Blazor では、テンプレートを作成する段階で「認証あり」としておくだけで、認証付きアプリのサンプルを作成することができます。
テンプレートとして実装されているのは、「Identity Server」を使用した認証機能です。
Identity Server とは、ASP.NETCore 用に認証まわりの機能を提供するオープンソースのサービスです。
本記事では、認証機能付きプロジェクトの作成方法から、認証なしで API を作成する方法まで解説しました。
これを読めば、認証まわりの基本的な動作が理解できるはずです。
それでは、さっそく見ていきましょう。
認証機能付きプロジェクトの作成
Visual Studio 2019 から新規プロジェクトを作成していきます。
Blazor WebAssenbly アプリを選択します。
次の設定にします。
- 対象のフレームワーク:.NET 5.0
- 認証:個別の認証(アプリ内)
- 詳細設定:ASP .NET Core でホストされた
好きなプロジェクト名をつけて、作成しましょう。
Client、Server、Shared の3つのプロジェクトが自動で作成され、同時に認証機能のサンプルも実装されます。
動作を確認するために、デバッグ実行をしてみましょう。
ブラウザが立ち上がり、「Authorizing…」が画面に表示されます。
裏で認証済みのユーザーかどうかのチェックをしているわけですね。
ホーム画面が表示されました。
認証されていないと判定されたので、ヘッダのところには「Register」と「Log in」のメニューが表示されています。
サンプルプロジェクトでは、「Fetch data」を参照するには認証が必要な仕組みになっているので、試しにクリックしてみましょう。
すると、「Checking login state…」が表示され、認証状態をチェックします。
認証がされていなかったので、ログインページにリダイレクトされました。
まだユーザーの登録が済んでいないので、「Register as a new user」を選択しましょう。
適当なメールアドレスとパスワードを入力して、「Register」を選択します。
登録が完了し、メール認証のステップに進みます。
デバッグの場合は、リンクをクリックするだけで認証が完了するので、表示されたリンクをクリックしてください。
「Comfirm email」が表示されると、メール認証が完了した証拠です。
「Sample.Server」をクリックして、またホーム画面に移動しましょう。
ログインしてもう一度 Fetch data をクリックすると、認証済みとして判定され、無事に画面を表示することができました。
また、ヘッダからもログインやサインインのメニューが消えて、メールアドレスが表示されていることも確認できますね。
基本的な流れとしてはこんな感じです。
認証を必要としない API アクセス
ここまで見てきたとおり、Fetch data のページを見るには認証を必要としています。
この認証を外して、ログインしていなくてもページを見れるように修正してみましょう。
まずは、現状がどのような動きになっているのかを見ていきます。
画面側の修正
まずは、画面の UI 部分を実装している FetchData.razor
です。
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Sample.Shared
@*@attribute [Authorize]*@ ←ここをコメントアウト!!
@inject HttpClient Http
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
try
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
@attribute [Authorize]
という記述がありますね。
これは何かというと、「このページを見るには認証が必要ですよ」という宣言です。
つまり、これを外せば認証が不要になるということですね。
「@**@」を書いてコメントアウトしましょう。
OnInitializedAsync
で AccessTokenNotAvailableException
をキャッチしていますが、これは「認証せずにページを表示したときに、ログインページにリダイレクトする」という処理を実現するためです。
Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
でサーバー側の API 呼び出しをしているので、ここでエラーになることを想定しています。
サーバー API の修正
次に、API 側を修正していきます。
FetchData.razor
で呼び出していたのは、WeatherForecastController.cs
の処理です。
namespace Sample.Server.Controllers
{
//[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
このクラスにも、[Authorize]
のアトリビュートがついていることが確認できますね。
画面側と同じように、「この API を呼び出すためには、認証が必要ですよ」という意味です。
ということで、[Authorize]
のアトリビュートはコメントアウトしておきましょう。
これで認証しなくても、Fetch data の画面が表示できるはずです。
動作確認
先ほどと同じようにデバッグ実行をして、Fetch data をクリックしてみます。
すると、「Checking login state…」の画面が表示されて、ログインページにリダイレクトされてしまいました。
API 実行で必ず認証されてしまう原因
原因は、クライアント側の Program.cs
にあります。
namespace Sample.Client
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
// ここ!!
builder.Services.AddHttpClient("Sample.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Sample.ServerAPI"));
builder.Services.AddApiAuthorization();
await builder.Build().RunAsync();
}
}
}
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>()
を書くことで、アクセストークンを API リクエストに追加しなくて済むようになります。
しかし、この処理が原因で HttpClient
を使った API リクエストには必ずアクセストークンが必要になり、認証なしで API を実行できません。
ではどうするかというと、認証なし用の HttpClient をもうひとつ作成します。
まずは Client 側に、PublicClient
というクラスを作成しましょう。
using System.Net.Http;
namespace Sample.Client
{
public class PublicClient
{
public HttpClient Client { get; }
public PublicClient(HttpClient httpClient)
{
Client = httpClient;
}
}
}
そしてもう一度 Program.cs
に戻り、認証なし用の HttpClient
を追加します。
namespace Sample.Client
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddHttpClient("Sample.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// ここを追加!!
builder.Services.AddHttpClient<PublicClient>(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Sample.ServerAPI"));
builder.Services.AddApiAuthorization();
await builder.Build().RunAsync();
}
}
}
最後に、Http から API を呼んでいた箇所を、先ほど作成した Public.Client から呼び出すように修正します。
PublicClient
のクラスが使えるように、上の方で inject
の宣言を追加しましょう。
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Sample.Shared
@*@attribute [Authorize]*@
@inject HttpClient Http
@*ここを追加!!*@
@inject PublicClient Public
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
try
{
@*Http ではなく Public.Client から API を呼び出す!*@
forecasts = await Public.Client.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
これで修正は完了です。
修正後の動作確認
では、実際に認証なしで Fetch data のページが見れるか確認してみましょう。
デバッグ実行してみると、認証無しでページを見ることができました。
ヘッダに「Log in」が表示されているので、認証がされてないことが確認できますね。
Controller 側に AllowAnonymous のアトリビュートをつけると「認証が不要な API」という意味になりますが、試しても認証が必要になってしまいました。
今回は使えないようですが、「そんな機能もあるんだな」ということは覚えておきましょう。
最後に
認証機能付きプロジェクトの作成方法から、認証なしで API を作成する方法まで解説しました。
実際に試した人は「重い」と感じるはずです。
オープンソースの仕組みなので仕方がないのかもしれませんが、実際の運用を考えるともう少しサクサク動いてほしいところです。
そこで、「Azure Active Directory B2C」というサービスを使うことで、もっとサクサクと認証処理を動かすことができるようになります。
認証機能を Azure Active Directory B2C で実現する方法で解説しているので、合わせてご覧ください。
入門編から EC サイトを作る応用編まで、Blazor の本を3冊執筆しました。
私が1年以上かけて学習した内容をすべて詰め込んでいるので、さらにステップアップしたい方はぜひご覧ください。