Blazor Server で ユーザー認証機能の自動生成とコード生成(スキャフォールディング)

Blazor

Blazor Serverではメールアドレスとパスワードによるユーザー認証機能を自動生成できます。

しかし、自動生成しただけではコードまでは自動的に生成されません。

この記事では、ユーザー認証機能とそのコードの自動生成を行う方法を紹介します。

dotnet new コマンドで BlazorServerプロジェクトを作成する

認証機能を付加したBlazorServerプロジェクトの作成は以下のコマンドで行います。

dotnet new blazorserver -au Individual

-au オプションの引数は以下のものを指定できます。

-au(--auth)オプションに指定可能な値

説明
None 認証は行われません (既定)
ndividual 個別認証です
ndividualB2C Azure AD B2C での個別認証
SingleOrg 単一のテナントに対する組織認証
MultiOrg 複数のテナントに対する組織認証です
Windows Windows 認証

参考URL

dotnet new 用の .NET の既定のテンプレート - .NET CLI
dotnet SDK に付属する dotnet の新しいテンプレートに関する情報。

自動生成されたユーザー登録画面

個別認証で生成した場合のユーザー情報DB

-au Individual で生成した場合、プロジェクトフォルダ直下にapp.dbが作成され、ここにユーザー情報が格納されます。

app.db には、以下のテーブルが含まれます。

ubuntu@ip-172-31-31-143:~/remoteDir/usefuledge20211023/blazorauthsample$ sqlite3 app.db 
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
AspNetRoleClaims       AspNetUserLogins       AspNetUsers          
AspNetRoles            AspNetUserRoles        __EFMigrationsHistory
AspNetUserClaims       AspNetUserTokens     

このうち、AspNetUsersテーブルに、ユーザーIDやメールアドレス、暗号化されたパスワードなどが格納されます。

AspNetUsersテーブルの定義は以下の通りです。

sqlite> .head on
sqlite> .mode column
sqlite> pragma table_info('AspNetUsers');
cid         name        type        notnull     dflt_value  pk        
----------  ----------  ----------  ----------  ----------  ----------
0           Id          TEXT        1                       1         
1           AccessFail  INTEGER     1                       0         
2           Concurrenc  TEXT        0                       0         
3           Email       TEXT        0                       0         
4           EmailConfi  INTEGER     1                       0         
5           LockoutEna  INTEGER     1                       0         
6           LockoutEnd  TEXT        0                       0         
7           Normalized  TEXT        0                       0         
8           Normalized  TEXT        0                       0         
9           PasswordHa  TEXT        0                       0         
10          PhoneNumbe  TEXT        0                       0         
11          PhoneNumbe  INTEGER     1                       0         
12          SecuritySt  TEXT        0                       0         
13          TwoFactorE  INTEGER     1                       0         
14          UserName    TEXT        0                       0         
sqlite> 

ユーザー登録すると、以下のようなレコードが格納されます。

sqlite> select * from aspnetusers;
Id                                    AccessFailedCount  ConcurrencyStamp                      Email               EmailConfirmed  LockoutEnabled  LockoutEnd  NormalizedEmail     NormalizedUserName  PasswordHash                                                                          PhoneNumber  PhoneNumberConfirmed  SecurityStamp                     TwoFactorEnabled  UserName          
------------------------------------  -----------------  ------------------------------------  ------------------  --------------  --------------  ----------  ------------------  ------------------  ------------------------------------------------------------------------------------  -----------  --------------------  --------------------------------  ----------------  ------------------
a4cff7d9-185b-40a8-b8c9-d521b94a8b9c  0                  7279bfcc-b974-4db6-8acb-1bdb0964b74a  sxxx7@xxx.com  1               1                           SXXX7@XXX.COM  SXXX7@XXX.COM  AQAAAAEAACcQAAAAEBPeHWT8NH482ZzQJtvQ7Hgt/3xTEHohryvzPbx//gQhVK5yTMPeEfBIjjgPnxgf5A==               0                     PQ7WGLR27FIQ2537Y6DFOQFPMVU45AKI  0                 sxxx7@xxx.com
sqlite> 

NuGetパッケージと dotnet tools のインストール

コード生成のため、以下のNuGetパッケージと dotnet tools をインストールしていきます。

Microsoft.VisualStudio.Web.CodeGeneration.Design

後述するdotnet-aspnet-codegeneratorコマンドが含まれています。

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

参考URL : NuGet Gallery

Microsoft.VisualStudio.Web.CodeGeneration.Design 8.0.0
Code Generation tool for ASP.NET Core. Contains the dotnet-aspnet-codegenerator command used for generating controllers and views.

dotnet-aspnet-codegenerator

dotnet aspnet-codegeneratorコマンドを使うため、インストールしていきます。

dotnet tool install -g dotnet-aspnet-codegenerator

参考URL : dotnet-aspnet-codegenerator

dotnet aspnet-codegenerator command
The dotnet aspnet-codegenerator command scaffolds ASP.NET Core projects.

コード生成に必要な NuGet Package

dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Identity.UI
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

dotnet aspnet-codegenerator コマンドの動作確認(ヘルプ表示)

以下のコマンドを実行して、dotnet aspnet-codegeneratorコマンドが正しく使用できるか確認しましょう。

dotnet aspnet-codegenerator identity -h

正しく使用できる場合、以下のようにコマンドの使い方が表示されます。

Usage: aspnet-codegenerator [arguments] [options]

Arguments:
  generator  Name of the generator. Check available generators below.

Options:
  -p|--project             Path to .csproj file in the project.
  -n|--nuget-package-dir   
  -c|--configuration       Configuration for the project (Possible values: Debug/ Release)
  -tfm|--target-framework  Target Framework to use. (Short folder name of the tfm. eg. net46)
  -b|--build-base-path     
  --no-build               

Selected Code Generator: identity

Generator Options:
  --dbContext|-dc       : Name of the DbContext to use, or generate (if it does not exist).
  --files|-fi           : List of semicolon separated files to scaffold. Use the --listFiles option to see the available options.
  --listFiles|-lf       : Lists the files that can be scaffolded by using the '--files' option.
  --userClass|-u        : Name of the User class to generate.
  --useSqLite|-sqlite   : Flag to specify if DbContext should use SQLite instead of SQL Server.
  --force|-f            : Use this option to overwrite existing files.
  --useDefaultUI|-udui  : Use this option to setup identity and to use Default UI.
  --layout|-l           : Specify a custom layout file to use.
  --generateLayout|-gl  : Use this option to generate a new _Layout.cshtml
  --bootstrapVersion|-b : Specify the bootstrap version. Valid values: '3', '4'. Default is 4.

コード生成可能なファイルリストの表示

コード生成が可能なファイルリストは、以下のコマンドで確認できます。

dotnet aspnet-codegenerator identity -sqlite -dc MyApplication.Data.ApplicationDbContext --listFiles
Building project ...
Finding the generator 'identity'...
Running the generator 'identity'...
File List:
Account._StatusMessage
Account.AccessDenied
Account.ConfirmEmail
Account.ConfirmEmailChange
Account.ExternalLogin
Account.ForgotPassword
Account.ForgotPasswordConfirmation
Account.Lockout
Account.Login
Account.LoginWith2fa
Account.LoginWithRecoveryCode
Account.Logout
Account.Manage._Layout
Account.Manage._ManageNav
Account.Manage._StatusMessage
Account.Manage.ChangePassword
Account.Manage.DeletePersonalData
Account.Manage.Disable2fa
Account.Manage.DownloadPersonalData
Account.Manage.Email
Account.Manage.EnableAuthenticator
Account.Manage.ExternalLogins
Account.Manage.GenerateRecoveryCodes
Account.Manage.Index
Account.Manage.PersonalData
Account.Manage.ResetAuthenticator
Account.Manage.SetPassword
Account.Manage.ShowRecoveryCodes
Account.Manage.TwoFactorAuthentication
Account.Register
Account.RegisterConfirmation
Account.ResendEmailConfirmation
Account.ResetPassword
Account.ResetPasswordConfirmation
RunTime 00:00:05.95

コード自動生成の実行

以下のコマンドで、すべてのコードの自動生成(スキャフォールディング)を実行します。

このコマンドは、SQLiteDBを使用したユーザー認証のコード生成です。

MyApplicationの部分はプロジェクト名に変更してください。

dotnet aspnet-codegenerator identity -sqlite -dc MyApplication.Data.ApplicationDbContext

自動生成対象のファイルを指定する場合は、--filesオプションを使用します。

dotnet aspnet-codegenerator identity -sqlite -dc MyApplication.Data.ApplicationDbContext --files "Account.Register;Account.Login"

'ApplicationDbContext' is an ambiguous reference エラーの対処法

dotnet runしたときに、以下のように'ApplicationDbContext' is an ambiguous referenceエラーが発生することがあります。

/home/ubuntu/remoteDir/usefuledge20211023/blazorauthsample/Program.cs(15,31): error CS0104: 'ApplicationDbContext' is an ambiguous reference between 'blazorauthsample.Data.ApplicationDbContext' and 'MyApplication.Data.ApplicationDbContext' [/home/ubuntu/remoteDir/usefuledge20211023/blazorauthsample/blazorauthsample.csproj]

エラーが発生した場合は、Program.csのusing句から、using MyApplication.Data;をコメントアウトしましょう。

using blazorauthsample.Areas.Identity;
using blazorauthsample.Data;
//using MyApplication.Data;

InvalidOperationException: Cannot find the fallback endpoint specified by route values: { page: /_Host, area: }. エラーの対処法

dotnet runしたときに、以下のようにInvalidOperationExceptionが発生することがあります。

InvalidOperationException: Cannot find the fallback endpoint specified by route values: { page: /_Host, area: }.
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.DynamicPageEndpointMatcherPolicy.ApplyAsync(HttpContext httpContext, CandidateSet candidates)

この場合、プロジェクトファイル(.csprojファイル)の<PropertyGroup><UseRazorSourceGenerator>false</UseRazorSourceGenerator>を追加しましょう。

<PropertyGroup>
  <UseRazorSourceGenerator>false</UseRazorSourceGenerator>
</PropertyGroup>

参考URL

Blazor Server - Cannot find the fallback endpoint specified by route values: { page: /_Host, area: } exception with RC1 · Issue #36535 · dotnet/aspnetcore
Describe the bug I have a Blazor Server app (here) which has been running fine with all of the .Net 6 previews. It's currently deployed on Preview-7 and working...

コード自動生成後のプロジェクトフォルダ構成

コード自動生成後は、Areasフォルダなどにコードが生成されます。

.
├── ./App.razor
├── ./Areas
│   └── ./Areas/Identity
│       ├── ./Areas/Identity/Data
│       │   └── ./Areas/Identity/Data/ApplicationDbContext.cs
│       ├── ./Areas/Identity/Pages
│       │   ├── ./Areas/Identity/Pages/Account
│       │   │   ├── ./Areas/Identity/Pages/Account/AccessDenied.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/AccessDenied.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ConfirmEmail.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ExternalLogin.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ForgotPassword.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/Lockout.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/Lockout.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/LogOut.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/Login.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/Login.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/LoginWith2fa.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/Logout.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/Logout.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/Manage
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/Email.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/Email.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/Index.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/Index.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ManageNavPages.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/PersonalData.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/SetPassword.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/_Layout.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml
│       │   │   │   ├── ./Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml
│       │   │   │   └── ./Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/Register.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/Register.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/RegisterConfirmation.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ResetPassword.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ResetPassword.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml
│       │   │   ├── ./Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs
│       │   │   ├── ./Areas/Identity/Pages/Account/_StatusMessage.cshtml
│       │   │   └── ./Areas/Identity/Pages/Account/_ViewImports.cshtml
│       │   ├── ./Areas/Identity/Pages/Error.cshtml
│       │   ├── ./Areas/Identity/Pages/Error.cshtml.cs
│       │   ├── ./Areas/Identity/Pages/Shared
│       │   │   └── ./Areas/Identity/Pages/Shared/_LoginPartial.cshtml
│       │   ├── ./Areas/Identity/Pages/_ViewImports.cshtml
│       │   └── ./Areas/Identity/Pages/_ViewStart.cshtml
│       └── ./Areas/Identity/RevalidatingIdentityAuthenticationStateProvider.cs
├── ./Data
│   ├── ./Data/ApplicationDbContext.cs
│   ├── ./Data/Migrations
│   │   ├── ./Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs
│   │   ├── ./Data/Migrations/00000000000000_CreateIdentitySchema.cs
│   │   └── ./Data/Migrations/ApplicationDbContextModelSnapshot.cs
│   ├── ./Data/WeatherForecast.cs
│   └── ./Data/WeatherForecastService.cs
├── ./Pages
│   ├── ./Pages/Counter.razor
│   ├── ./Pages/Error.cshtml
│   ├── ./Pages/Error.cshtml.cs
│   ├── ./Pages/FetchData.razor
│   ├── ./Pages/Index.razor
│   ├── ./Pages/Shared
│   │   ├── ./Pages/Shared/_Layout.cshtml
│   │   ├── ./Pages/Shared/_LoginPartial.cshtml
│   │   └── ./Pages/Shared/_ValidationScriptsPartial.cshtml
│   ├── ./Pages/_Host.cshtml
│   ├── ./Pages/_Layout.cshtml
│   ├── ./Pages/_ViewImports.cshtml
│   └── ./Pages/_ViewStart.cshtml
├── ./Program.cs
├── ./Properties
│   └── ./Properties/launchSettings.json
├── ./ScaffoldingReadMe.txt
├── ./Shared
│   ├── ./Shared/LoginDisplay.razor
│   ├── ./Shared/MainLayout.razor
│   ├── ./Shared/MainLayout.razor.css
│   ├── ./Shared/NavMenu.razor
│   ├── ./Shared/NavMenu.razor.css
│   └── ./Shared/SurveyPrompt.razor
├── ./_Imports.razor
├── ./app.db
├── ./appsettings.Development.json
├── ./appsettings.json

終わりに

この記事で自動生成したユーザー認証の各ページは、カスタマイズが可能です。

詳細は以下にまとめていますので、ご覧ください。

BLAZOR SERVER で自動生成したユーザー認証機能の各画面をカスタマイズする方法

参考URL

Scaffold Identity in ASP.NET Core projects
Learn how to scaffold Identity in an ASP.NET Core project.

Blazor のおすすめ本

私もBlazorを用いていくつかウェブアプリを開発しました。フレームワークがとても分かりやすく、簡単にSPAを作れることが楽しいと感じています。

Blazor wasm であればAWS S3のようなウェブホスティングサービスでアプリを公開することもできますし、Blazor Serverアプリであれば簡単に2要素認証を構築できます。

Blazor は現在も開発が進められているフレームワークですので本の情報は少しずつ古くなってしまいますが、全体的・体系的に学ぶには、本はとても有力なツールです。

電子書籍になっていますので、まずは試し読みから始めてはいかがでしょうか。

内容
第1章 Blazorの仕組み
第2章 開発環境
第3章 最初のBlazorアプリ
第4章 コンポーネント
第5章 データバインディング
第6章 イベント処理
第7章 Razor記法
第8章 フォームと検証
第9章 データベースアクセス
第10章 Web APIの活用
第11章 SVGの活用
第12章 JavaScriptとの連携

コメント

タイトルとURLをコピーしました