Webアプリ

【C#、Blazor】Webアプリ開発入門編(Ex4)Azure AD B2C ~アプリへ簡単・多機能なユーザ認証を追加!~【ASP.NET Core】

今回はC# ASP.NET Core BlazorアプリAzure AD B2Cによるユーザ認証を追加する方法を解説します。

本記事では、以下について説明します。

  • Azure AD B2Cとは何か?
  • BlazorアプリにAzure AD B2Cを組み込む方法

次のような人に役立つ内容になっています。

  • Azure AD B2C認証を行うBlazorアプリを作りたい。
  • 一般消費者向けの外部認証をBlazorアプリへ簡単に組込みたい。

以下のようなAzure AD B2C認証が行える簡単なBlazorアプリを作ります。

Azure AD B2Cは一般消費者向けアプリの認証基盤です。

Azure AD B2C認証を使うと、Google、Facebook、Twitterなどのソーシャルアカウントでのログインをシームレスにアプリへ組込むこともできます。

プロ太

Azure AD B2C認証を学ぶと、世の中のエンドユーザ向けサービスでよく見かける認証方式を自分のアプリにも実装できるようになります!

演習コード一式はGitHubに置いてありますので、参考にしてください。

YouTubeの動画もあります。

講義:Azure AD B2Cとは?

Azure AD B2Cの概要

Microsoftは以下の2つの外部認証プロバイダを用意しています。今回は(2)についての説明です。

  • (1) Entra ID:主に企業や組織内の社員やパートナー向け。
  • (2) Azure Active Directory B2C: 一般消費者向け。

(1)Entra IDについては以下の記事で解説しました。こちらもあわせて読むことで理解が深まるかと思います。

【C#、Blazor】Webアプリ開発入門編(Ex3)Microsoft Entra ID(旧AzureAD)~アプリへ簡単に認証機能を追加!~【ASP.NET Core】 今回はC#BlazorアプリへMicrosoft EntraID(旧Azure Active Directory)認証を組み込む方法を...

Azure AD B2Cは以下の特徴があります。

  1. ソーシャルIDプロバイダとの連携: Google、Facebook、Twitterなどのアカウントでのログインをサポート
  2. カスタマイズ可能なUI: ログイン画面などをブランドに合わせてカスタマイズ可能
  3. セキュリティ機能: 多要素認証、不正検出などの高度なセキュリティ機能を提供
  4. スケーラビリティ: 数百万ユーザーまでスケール可能

Entra IDとAzure AD B2Cの比較

Entra IDとAzure AD B2Cの主な違いは以下のとおりです。

プロ美

Azure AD B2Cを使うと、ソーシャルID(Google、Facebook等)でもログインできるんだ!

プロ太

その通りです。多様なログイン方式を一元管理できるのも、一般消費者向けサービスの認証機能として嬉しい点ですね。

演習:Azure AD B2C認証機能を作る

以下の手順で、Azure AD B2C認証をBlazorアプリへ実装します。

  • 手順1:Azure AD B2Cの設定
  • 手順2:Blazorアプリを実装

手順1:Azure AD B2Cの設定

Azure全体の初期設定については、Entra ID認証の記事手順1-1:アカウント作成とサブスクリプション・テナント作成)を参考にしてください。

ここでは、Azure AD B2C特有の設定を中心に説明します。

  • 手順1-1:Azure AD B2Cテナントの作成
  • 手順1-2:アプリを登録
  • 手順1-3:ユーザフローの作成

Azureの構成は以下のようになります。

Microsoftアカウントに対応したAzure環境が1つ存在し、Azure環境内ではテナント(1つの組織に対応)を複数作れます。

サブスクリプションはAzureのサービス・リソースを使うための契約単位(支払用のクレジットカードに対応)です。

今回はAzure AD B2C用にテナントを新規作成します。

BlazorアプリはこのAzureADB2Cに登録された「アプリ」と「ユーザフロー」を経由してユーザ認証/管理を行います。

Azure AD B2Cは基本的な機能を無料で使えます。詳しくはAzure Active Directory B2C の価格を参照してください。

実は、Azure AD B2C用のテナントでサブスクリプションは存在しません。

B2C用のテナントを作成するときに指定したサブスクリプションの配下に、B2C用のリソースグループ・リソースが作成されます。

そのため、B2Cテナントの利用で料金が発生した場合、この指定したサブスクリプションへ請求がいきます。

プロ太

付けるオプションによって料金が発生するので注意しましょう!

手順1-1:Azure AD B2Cテナントの作成

AzurePortalの画面で、リソース作成を選びましょう。

「Azure Active Directory B2C」で検索し、選びましょう。

「新しいAzure AD B2Cコンテナを作成する」を選びます。

以下のように組織名・初期ドメイン・場所、サブスクリプションなどを設定して「確認及び作成」をクリックします。そして、次の確認画面で「作成」をクリックしましょう。

設定した初期ドメイン名(ここでは、protademo1)はメモしておきましょう。

以下のように「テナントが正常に作成されました」と表示されます。「ここ」をクリックして、作成したAzure AD B2C用テナントへ切り替えましょう。

作成したテナントが表示されました!

元からあるテナント「Default Directory」と新しく作成したテナントは、Azure Portalの画面右上から切り替えられます。

手順1-2:アプリを登録

プロ太

アプリ登録の流れは、EntraIDの場合(こちらの手順1-2)と同じです。

Azure AD B2C用のテナントへ切り替えている状態で、「Azure AD B2C」で検索をして選択しましょう。

Azure AD B2Cの設定画面になるので、「アプリ登録>新規登録」を選択します。

以下のようにアプリ登録をしましょう。リダイレクトURIはWebを選択し、「https://localhost:5001/signin-oidc」としておきましょう。

次にクライアントシークレットを新規で作成し、その「値」をメモしておきましょう。(「説明」は何でもよいです)

アプリの「概要」を選び、アプリケーションID(クライアントID)をメモしておきましょう。

これでアプリ作成は終わりです。

手順1-3:ユーザフローの作成

プロ太

こちらはEntraIDのときはなかった手順ですね。

Azure AD B2Cの設定画面で「ユーザフロー>新しいユーザフロー」を選びます。

「サインアップとサインイン」、「推奨」を選び「作成」をクリックします。

以下のように設定して作成します。名前は「BlazorAzureADB2CApp1」としました。

ユーザフロー名は「B2C_1_BlazorAzureADB2CApp1」となるので、これをメモしておいてください。

5.ユーザ属性とトークン要求で、「属性を収集する」はサインアップ時にユーザへ登録を要求する項目です。

「要求を返す」は、ユーザがログインしたときにBlazorアプリ側で取得する項目となります。

他にも様々な項目を設定できます。

プロ太

これで、Azure側の設定は完了です!

手順2:Blazorアプリを実装

以下の手順でAzure AD B2C認証を行うBlazorアプリを作ります。

  • 手順2-1:プロジェクトひな型を準備
  • 手順2-2:コードを修正
  • 手順2-3:設定ファイルを修正

プロジェクトひな型で、以下の部分を修正します。

手順2-1:プロジェクトひな型を準備

以下のコマンドで、Blazor Severモードのプロジェクトを作成しましょう。

dotnet new blazor -n BlazorAzureADB2CApp1 -o BlazorAzureADB2CApp1 -f net8.0 --interactivity Server

Visual Studioでプロジェクトファイル「BlazorAzureADB2CApp1.csproj」を開きます。

「ファイル>全て保存」で、「BlazorAzureADB2CApp1.csproj」と同じフォルダへソリューションファイル「BlazorAzureADB2CApp1.sln」を保存しておきましょう。

以下の2つのパッケージをインストールしておきます。

dotnet add package Microsoft.Identity.Web
dotnet add package Microsoft.Identity.Web.UI

手順2-2:コードを修正

Program.csへ以下のように追記します。

using BlazorAzureADB2CApp1.Components;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;

var builder = WebApplication.CreateBuilder(args);

//★(a) Configure Azure AD B2C Authentication using OpenID Connect
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

//★(b) Add authorization policy requiring authentication for all controllers
builder.Services.AddControllersWithViews(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
});

//★(c) Add Razor Pages and Microsoft Identity UI for authentication-related pages
builder.Services.AddRazorPages()
    .AddMicrosoftIdentityUI();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

//★(d) Enable routing for authentication-related UI pages
app.MapControllers();
app.MapRazorPages();

app.Run();

(a)、(b)、(c)で、アプリでAzure AD B2C認証を使うための主要な設定を行っています。

(d) では認証関連のUI ページへのルーティングを有効にしています。これにより、ユーザーが認証関連のページにアクセスできるようになります。

次に、Components/Layout/NavMenu.razorへログイン/ログアウト用のリンクを追加します。それぞれ(a),(b)の部分です。

<div class="top-row ps-3 navbar navbar-dark">
    <div class="container-fluid">
        <a class="navbar-brand" href="">BlazorAzureADB2CApp1</a>
    </div>
</div>

<input type="checkbox" title="Navigation menu" class="navbar-toggler" />

<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="weather">
                <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
            </NavLink>
        </div>

        @*★(a)*@
        <div class="nav-item px-3">
            <a href="MicrosoftIdentity/Account/SignIn" class="nav-link">Login</a>
        </div>

        @*★(b)*@
        <div class="nav-item px-3">
            <a href="MicrosoftIdentity/Account/SignOut" class="nav-link">Logout</a>
        </div> 
    </nav>
</div>

Compornents/Pages/Home.razorを以下のように修正しましょう。認証されていればユーザ名を、そうでなければGuestと表示するようにしています。

@page "/"
@using Microsoft.AspNetCore.Components.Authorization
@using System.Security.Claims
@inject AuthenticationStateProvider AuthenticationStateProvider

<PageTitle>Home</PageTitle>

<h1>Hello, @UserName!</h1>
<p>Welcome to your new app.</p>

@code {
    private string UserName { get; set; } = "Guest";

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity?.IsAuthenticated == true)
        {
            //★(a)
            var givenName = user.FindFirst(ClaimTypes.GivenName)?.Value ?? string.Empty;
            var surname = user.FindFirst(ClaimTypes.Surname)?.Value ?? string.Empty;
            UserName = $"{givenName} {surname}".Trim();
        }
    }
}

(a)の部分では、ユーザがサインアップするときに登録する名字(Surname)・名前(GivenName)を取得しています。

手順2-3:設定ファイルを修正

appsettings.jsonへ(a)のようにAzureADB2C用の項目を追記します。

(b)ドメイン名(例:protademo1)、(c)クライアントID、(d)ユーザフロー名、(e)クライアントシークレットはメモしておいたものを記載しましょう。

{
  //★(a)
  "AzureAdB2C": {
    "Instance": "https://【★(b)ドメイン名】.b2clogin.com",
    "Domain": "【★(b)ドメイン名】.onmicrosoft.com",
    "ClientId": "★(c)クライアントID",
    "SignUpSignInPolicyId": "★(d)ユーザフロー名",
    "ClientSecret": "★(e)クライアントシークレット",
    "ResponseType": "code"  // 認可コードフローを使う
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
プロ太

クライアントシークレットは機密性が高いため、実際の開発では環境変数にするなどコードベースから分離するのがよいでしょう。

Properties/launchSettings.jsonで、profiles/https/applicationUrlで起動ポート((a)の部分)を「https://localhost:5001」と修正します。

  …
      "https": {
        "commandName": "Project",
        "dotnetRunMessages": true,
        "launchBrowser": true,
        "applicationUrl": "https://localhost:5001;http://localhost:5107", //★(a)
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development"
        }
 …

ここでは、Azureでアプリ登録の設定時のリダイレクトURIで設定したポート番号とあっていれば何でもよいです。

アプリ実行

プロ美

(EntraIDのときよりちょっと長かった…)

さあ、これでデバッグ実行してみよう!

Visual Studioでプロファイルをhttps(デフォルト設定)としてデバッグ実行しましょう。

未ログインではHomeは以下のような画面です。

Loginボタンを押して、サインアップしてみましょう。

メールアドレス(Gmailなど、何でもよいです)を入力し、バリデーションコード入力を行い、パスワード入力、氏名入力などを行ってアカウントを作成しましょう。

ログインすると以下のように表示されます。

プロ太

Azure AD B2C認証をBlazorアプリへ実装できましたね!

プロ美

エンドユーザ向けサービス・アプリをBlazorで作る第一歩だね!

まとめ

Blazorへ一般消費者向けの外部認証プロバイダであるAzure AD B2Cを組み込む方法を学びました。

Azure AD B2Cでは、様々な外部認証プロバイダと連携させることも可能です。

今回学んだ内容を土台として、普段自分たちがよく使っているような認証機能を備えたWebサービスを作れるようになるかと思います。

プロ太

引き続き、Webアプリ開発とBlazorを一緒に学んでいきましょう!

ABOUT ME
プロ太
プログラミングを勉強している人へ情報を発信していきます! ・情報工学分野で博士(工学)の学位取得 ・言語:C# 、Java、C/C++、Python、JavaScript/TypeScript等 ・仕事は主に上流工程(WF開発・Agile開発、OSS開発経験あり) ・趣味で開発:3Dゲーム、Webアプリ、言語処理系等

ご依頼・ご相談について

プログラミング学習のご相談、お仕事のご依頼については、
こちらのお問い合わせページをご確認ください。