Azure SignalR Service の認証

このチュートリアルは、「SignalR Service を使ってチャット ルームを作成する」で導入したチャット ルーム アプリケーションを続行します。 最初にこのクイックスタートを完了して、チャット ルームを設定します。

このチュートリアルでは、Microsoft Azure SignalR Service を使用して認証方法を作成して統合する方法について説明します。

クイック スタートのチャット ルーム アプリケーションで最初に使用した認証は、現実のシナリオでは単純すぎます。 アプリケーションでは各クライアントが自分の ID を主張することができ、サーバーは単にそれを受け入れます。 悪意のあるユーザーが偽の ID を使用して機密データにアクセスできるため、このアプローチは実際には無効です。

GitHub は、OAuth と呼ばれる一般的な業界標準プロトコルに基づく認証 API を提供します。 これらの API により、サード パーティ アプリケーションは GitHub アカウントを認証できます。 このチュートリアルでは、これらの API を使用して、チャット ルーム アプリケーションへのクライアント ログインを許可する前の GitHub アカウントによる認証を実装できます。 GitHub アカウント認証後、アカウント情報は Web クライアントが認証に使用できるように Cookie として追加されます。

GitHub を通じて提供される OAuth 認証 API の詳細については、「Basics of Authentication (認証の基礎)」を参照してください。

このクイック スタートの手順は、任意のコード エディターを使用して実行できます。 ただし、推奨のエディターは Visual Studio Code です (Windows、macOS、および Linux プラットフォームで使用できます)。

このチュートリアルのコードは、GitHub リポジトリの AzureSignalR-samples からダウンロードできます。

OAuth Complete hosted in Azure

このチュートリアルでは、次の作業を行う方法について説明します。

  • GitHub アカウントに新しい OAuth アプリを登録する
  • GitHub 認証をサポートする認証コントローラーを追加する
  • Azure に ASP.NET Core Web アプリをデプロイする

Azure サブスクリプションをお持ちでない場合は、開始する前に Azure 無料アカウントを作成してください。

前提条件

このチュートリアルを完了するには、次の前提条件を満たしている必要があります。

OAuth アプリを作成する

  1. Web ブラウザーを開き、https://github.com に移動してアカウントにサインインします。

  2. アカウントで [設定]>[開発者向け設定]>[OAuth アプリ] に移動し、[OAuth アプリ] の下の [新しい OAuth アプリ] を選択します。

  3. 新しい OAuth アプリには次の設定を使用し、[アプリケーションの登録] を選択します。

    設定名 推奨値 説明
    アプリケーション名 Azure SignalR チャット GitHub ユーザーは、認証しているアプリを認識して信頼することができる必要があります。
    ホームページ URL https://localhost:5001
    アプリケーションの説明 Azure SignalR サービスと GitHub 認証を使用するチャット ルーム サンプル 使用されている認証のコンテキストをアプリケーションのユーザーが理解するために役立つ、アプリケーションの有益な説明。
    認証コールバックの URL https://localhost:5001/signin-github この設定は、OAuth アプリケーションの最も重要な設定です。 認証の成功後に GitHub がユーザーに返すコールバック URL です。 このチュートリアルでは、/signin-githubAspNet.Security.OAuth.GitHub パッケージの既定のコールバック URL を使用する必要があります。
  4. 新しい OAuth アプリの登録が完了したら、次のコマンドを使用して、クライアント IDクライアント シークレットを Secret Manager に追加します。 Your_GitHub_Client_IdYour_GitHub_Client_Secret を実際の OAuth アプリの値に置き換えます。

    dotnet user-secrets set GitHubClientId Your_GitHub_Client_Id
    dotnet user-secrets set GitHubClientSecret Your_GitHub_Client_Secret
    

OAuth フローを実装する

チュートリアル「SignalR Service を使ってチャット ルームを作成する」で作成したチャット アプリを再利用しましょう。

GitHub 認証をサポートするように Program.cs を更新する

  1. 最新の AspNet.Security.OAuth.GitHub パッケージへの参照を追加し、すべてのパッケージを復元します。

    dotnet add package AspNet.Security.OAuth.GitHub
    
  2. Program.cs を開き、コードを次のコード スニペットに更新します。

    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Authentication.OAuth;
    
    using System.Net.Http.Headers;
    using System.Security.Claims;
    
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services
        .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie()
        .AddGitHub(options =>
        {
            options.ClientId = builder.Configuration["GitHubClientId"] ?? "";
            options.ClientSecret = builder.Configuration["GitHubClientSecret"] ?? "";
            options.Scope.Add("user:email");
            options.Events = new OAuthEvents
            {
                OnCreatingTicket = GetUserCompanyInfoAsync
            };
        });
    
    builder.Services.AddControllers();
    builder.Services.AddSignalR().AddAzureSignalR();
    
    var app = builder.Build();
    
    app.UseHttpsRedirection();
    app.UseDefaultFiles();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapControllers();
    app.MapHub<ChatSampleHub>("/chat");
    
    app.Run();
    
    static async Task GetUserCompanyInfoAsync(OAuthCreatingTicketContext context)
    {
        var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
    
        var response = await context.Backchannel.SendAsync(request,
            HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
        var user = await response.Content.ReadFromJsonAsync<GitHubUser>();
        if (user?.company != null)
        {
            context.Principal?.AddIdentity(new ClaimsIdentity(new[]
            {
                new Claim("Company", user.company)
            }));
        }
    }
    
    class GitHubUser
    {
        public string? company { get; set; }
    }
    

    コード内で、AddAuthenticationUseAuthentication を使用して GitHub OAuth アプリを使用する認証サポートを追加します。GetUserCompanyInfoAsync ヘルパー メソッドは、GitHub OAuth から会社情報を読み込んで、ユーザー ID に保存する方法を示すサンプル コードです。 GitHub OAuth はセキュリティで保護された https スキームにのみパススルーされる secure Cookie を設定したため、UseHttpsRedirection() が使用されていることにも気付く場合があります。 また、https エンドポイントを追加するためにローカル Properties/lauchSettings.json を忘れずに更新してください。

    {
      "profiles": {
        "GitHubChat" : {
          "commandName": "Project",
          "launchBrowser": true,
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
          },
          "applicationUrl": "http://0.0.0.0:5000/;https://0.0.0.0:5001/;"
        }
      }
    }
    

認証コントローラーを追加する

このセクションでは、GitHub OAuth アプリを使用してクライアントを認証する Login API を実装します。 認証されると、API はクライアントをチャット アプリにリダイレクトして戻す前に、Web クライアントの応答に Cookie を追加します。 その Cookie は、後でクライアントを識別するために使用されます。

  1. GitHubChat\Controllers ディレクトリに新しいコントローラー コード ファイルを追加します。 ファイルに AuthController.cs という名前を付けます。

  2. 認証コントローラーに、次のコードを追加します。 プロジェクト ディレクトリが GitHubChat でなかった場合は、名前空間を更新してください。

    using AspNet.Security.OAuth.GitHub;
    
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Mvc;
    
    namespace GitHubChat.Controllers
    {
        [Route("/")]
        public class AuthController : Controller
        {
            [HttpGet("login")]
            public IActionResult Login()
            {
                if (User.Identity == null || !User.Identity.IsAuthenticated)
                {
                    return Challenge(GitHubAuthenticationDefaults.AuthenticationScheme);
                }
    
                HttpContext.Response.Cookies.Append("githubchat_username", User.Identity.Name ?? "");
                HttpContext.SignInAsync(User);
                return Redirect("/");
            }
        }
    }
    
  3. 変更を保存します。

Hub クラスを更新する

既定では、Web クライアントは、Azure SignalR SDK によって自動的に生成されたアクセス トークンを使用して SignalR Service に接続します。

このセクションでは、ハブ クラスに Authorize 属性を追加することで実際の認証ワークフローを統合し、認証されたユーザーの要求からユーザー名を読み取るようにハブ メソッドを更新します。

  1. Hub\ChatSampleHub.cs を開き、次のコード スニペットにコードを更新します。 このコードは、Authorize 属性を ChatSampleHub クラスに追加し、ハブ メソッドでユーザーの認証された ID を使用します。 また、OnConnectedAsync メソッドも追加されます。このメソッドは、新しいクライアントが接続するたびに、システム メッセージをチャット ルームに記録します。

    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.SignalR;
    
    [Authorize]
    public class ChatSampleHub : Hub
    {
        public override Task OnConnectedAsync()
        {
            return Clients.All.SendAsync("broadcastMessage", "_SYSTEM_", $"{Context.User?.Identity?.Name} JOINED");
        }
    
        // Uncomment this line to only allow user in Microsoft to send message
        //[Authorize(Policy = "Microsoft_Only")]
        public Task BroadcastMessage(string message)
        {
            return Clients.All.SendAsync("broadcastMessage", Context.User?.Identity?.Name, message);
        }
    
        public Task Echo(string message)
        {
            var echoMessage = $"{message} (echo from server)";
            return Clients.Client(Context.ConnectionId).SendAsync("echo", Context.User?.Identity?.Name, echoMessage);
        }
    }
    
  2. 変更を保存します。

Web クライアント コードを更新する

  1. wwwroot\index.html を開き、ユーザー名の入力を求めるコードを、認証コントローラーから返された Cookie を使用するコードに置き換えます。

    index.html の関数 getUserName 内のコードを、Cookie を使用するように次のように更新します。

    function getUserName() {
      // Get the user name cookie.
      function getCookie(key) {
        var cookies = document.cookie.split(";").map((c) => c.trim());
        for (var i = 0; i < cookies.length; i++) {
          if (cookies[i].startsWith(key + "="))
            return unescape(cookies[i].slice(key.length + 1));
        }
        return "";
      }
      return getCookie("githubchat_username");
    }
    
  2. ハブ メソッド broadcastMessageecho を呼び出すときに、username パラメーターを削除するように onConnected 関数を更新します。

    function onConnected(connection) {
      console.log("connection started");
      connection.send("broadcastMessage", "_SYSTEM_", username + " JOINED");
      document.getElementById("sendmessage").addEventListener("click", function (event) {
        // Call the broadcastMessage method on the hub.
        if (messageInput.value) {
          connection.invoke("broadcastMessage", messageInput.value)
            .catch((e) => appendMessage("_BROADCAST_", e.message));
        }
    
        // Clear text box and reset focus for next comment.
        messageInput.value = "";
        messageInput.focus();
        event.preventDefault();
      });
      document.getElementById("message").addEventListener("keypress", function (event) {
        if (event.keyCode === 13) {
          event.preventDefault();
          document.getElementById("sendmessage").click();
          return false;
        }
      });
      document.getElementById("echo").addEventListener("click", function (event) {
        // Call the echo method on the hub.
        connection.send("echo", messageInput.value);
    
        // Clear text box and reset focus for next comment.
        messageInput.value = "";
        messageInput.focus();
        event.preventDefault();
      });
    }
    
  3. index.html の末尾で、以下のように connection.start() のエラー ハンドラーを更新して、ユーザーにサインインを求めるメッセージを表示するようにします。

    connection.start()
      .then(function () {
        onConnected(connection);
      })
      .catch(function (error) {
        console.error(error.message);
        if (error.statusCode && error.statusCode === 401) {
          appendMessage(
            "_BROADCAST_",
            "You\"re not logged in. Click <a href="/login">here</a> to login with GitHub."
          );
        }
      });
    
  4. 変更を保存します。

アプリをビルドし、ローカルで実行する

  1. すべてのファイルの変更を保存します。

  2. 次のコマンドを実行して、Web アプリをローカルで実行します。

    dotnet run
    

    既定では、アプリはポート 5000 でローカルにホストされます。

    info: Microsoft.Hosting.Lifetime[14]
          Now listening on: http://0.0.0.0:5000
    info: Microsoft.Hosting.Lifetime[14]
          Now listening on: https://0.0.0.0:5001
    info: Microsoft.Hosting.Lifetime[0]
          Application started. Press Ctrl+C to shut down.
    info: Microsoft.Hosting.Lifetime[0]
          Hosting environment: Development
    
  3. ブラウザー ウィンドウを起動して、https://localhost:5001 に移動します。 上部の [here] リンクを選択して、GitHub にサインインします。

    OAuth Complete hosted in Azure

    GitHub アカウントへのチャット アプリのアクセスを承認するように求めるメッセージが表示されます。 [承認] ボタンを選択します。

    Authorize OAuth App

    リダイレクトされてチャット アプリケーションに戻り、GitHub アカウント名でログインされます。 Web アプリケーションは、追加された新しい認証を使用してユーザーを認証することによって、アカウント名を判別しました。

    Account identified

    チャット アプリが GitHub で認証を実行し、認証情報を Cookie として格納するようになったため、次の手順ではこれを Azure にデプロイします。 この方法により、他のユーザーはそれぞれのアカウントを使用して認証を行い、さまざまなワークステーションから通信できます。

Azure にアプリケーションをデプロイする

Azure CLI の環境を準備する:

  • Azure Cloud Shell で Bash 環境を使用します。 詳細については、「Azure Cloud Shell の Bash のクイックスタート」を参照してください。

  • CLI リファレンス コマンドをローカルで実行する場合、Azure CLI をインストールします。 Windows または macOS で実行している場合は、Docker コンテナーで Azure CLI を実行することを検討してください。 詳細については、「Docker コンテナーで Azure CLI を実行する方法」を参照してください。

    • ローカル インストールを使用する場合は、az login コマンドを使用して Azure CLI にサインインします。 認証プロセスを完了するには、ターミナルに表示される手順に従います。 その他のサインイン オプションについては、Azure CLI でのサインインに関するページを参照してください。

    • 初回使用時にインストールを求められたら、Azure CLI 拡張機能をインストールします。 拡張機能の詳細については、Azure CLI で拡張機能を使用する方法に関するページを参照してください。

    • az version を実行し、インストールされているバージョンおよび依存ライブラリを検索します。 最新バージョンにアップグレードするには、az upgrade を実行します。

このセクションでは、Azure CLI を使用して、Azure で ASP.NET アプリケーションをホストするための新しい Web アプリを Azure App Service に作成します。 Web アプリは、ローカル Git デプロイを使用するように構成されます。 また、Web アプリは、SignalR 接続文字列、GitHub OAuth のアプリ シークレット、およびデプロイ ユーザーによっても構成されます。

以下のリソースを作成する場合は、SignalR Service リソースと同じリソース グループを使用してください。 このようにすると、後ですべてのリソースを削除する場合に、クリーンアップが容易になります。 この例では、前のチュートリアルで推奨されたグループ名である SignalRTestResources が使用されたと仮定しています。

Web アプリと計画を作成する

以下のコマンド用のテキストをコピーし、パラメーターを更新します。 更新したスクリプトを Azure Cloud Shell に貼り付け、Enter キーを押して、新しい App Service プランと Web アプリを作成します。

#========================================================================
#=== Update these variable for your resource group name.              ===
#========================================================================
ResourceGroupName=SignalRTestResources

#========================================================================
#=== Update these variable for your web app.                          ===
#========================================================================
WebAppName=myWebAppName
WebAppPlan=myAppServicePlanName

# Create an App Service plan.
az appservice plan create --name $WebAppPlan --resource-group $ResourceGroupName \
    --sku FREE

# Create the new Web App
az webapp create --name $WebAppName --resource-group $ResourceGroupName \
    --plan $WebAppPlan
パラメーター 説明
ResourceGroupName このリソース グループ名は、前のチュートリアルで指定されたものです。 すべてのチュートリアル リソースをまとめてグループ化しておくことをお勧めします。 前のチュートリアルで使用したのと同じリソース グループを使用します。
WebAppPlan 新しい一意の App Service プラン名を入力します。
WebAppName このパラメーターが新しい Web アプリの名前になり、URL の一部になります。 これを一意になるようにします。 たとえば、signalrtestwebapp22665120 です。

アプリ設定を Web アプリに追加する

このセクションでは、次のコンポーネントのアプリ設定を追加します。

  • SignalR Service リソースの接続文字列
  • GitHub OAuth アプリのクライアント ID
  • GitHub OAuth アプリのクライアント シークレット

以下のコマンド用のテキストをコピーし、パラメーターを更新します。 更新したスクリプトを Azure Cloud Shell に貼り付け、Enter キーを押して、アプリ設定を追加します。

#========================================================================
#=== Update these variables for your GitHub OAuth App.                ===
#========================================================================
GitHubClientId=1234567890
GitHubClientSecret=1234567890

#========================================================================
#=== Update these variables for your resources.                       ===
#========================================================================
ResourceGroupName=SignalRTestResources
SignalRServiceResource=mySignalRresourcename
WebAppName=myWebAppName

# Get the SignalR primary connection string
primaryConnectionString=$(az signalr key list --name $SignalRServiceResource \
  --resource-group $ResourceGroupName --query primaryConnectionString -o tsv)

#Add an app setting to the web app for the SignalR connection
az webapp config appsettings set --name $WebAppName \
    --resource-group $ResourceGroupName \
    --settings "Azure__SignalR__ConnectionString=$primaryConnectionString"

#Add the app settings to use with GitHub authentication
az webapp config appsettings set --name $WebAppName \
    --resource-group $ResourceGroupName \
    --settings "GitHubClientId=$GitHubClientId"
az webapp config appsettings set --name $WebAppName \
    --resource-group $ResourceGroupName \
    --settings "GitHubClientSecret=$GitHubClientSecret"
パラメーター 説明
GitHubClientId この変数に GitHub OAuth アプリのシークレット クライアント ID を割り当てます。
GitHubClientSecret この変数に GitHub OAuth アプリのシークレット パスワードを割り当てます。
ResourceGroupName この変数を、前のセクションで使用したのと同じリソース グループ名に更新します。
SignalRServiceResource この変数を、クイック スタートで作成した SignalR Service リソースの名前に更新します。 たとえば、signalrtestsvc48778624 です。
WebAppName この変数を、前のセクションで作成した新しい Web アプリの名前に更新します。

ローカル Git デプロイ用に Web アプリを構成する

Azure Cloud Shell に、次のスクリプトを貼り付けます。 このスクリプトは、Git で Web アプリにコードをデプロイするときに使用する新しいデプロイ ユーザー名とパスワードを作成します。 また、このスクリプトはローカル Git リポジトリによるデプロイ用に Web アプリを構成し、Git デプロイ URL を返します。

#========================================================================
#=== Update these variables for your resources.                       ===
#========================================================================
ResourceGroupName=SignalRTestResources
WebAppName=myWebAppName

#========================================================================
#=== Update these variables for your deployment user.                 ===
#========================================================================
DeploymentUserName=myUserName
DeploymentUserPassword=myPassword

# Add the desired deployment user name and password
az webapp deployment user set --user-name $DeploymentUserName \
    --password $DeploymentUserPassword

# Configure Git deployment and note the deployment URL in the output
az webapp deployment source config-local-git --name $WebAppName \
    --resource-group $ResourceGroupName \
    --query [url] -o tsv
パラメーター 説明
DeploymentUserName 新しいデプロイ ユーザー名を選択します。
DeploymentUserPassword 新しいデプロイ ユーザーのパスワードを選択します。
ResourceGroupName 前のセクションで使用したのと同じリソース グループ名を使用します。
WebAppName このパラメーターが、前に作成した新しい Web アプリの名前になります。

このコマンドから返される Git デプロイ URL を書き留めておきます。 この URL は、後で使用します。

Azure Web アプリにコードをデプロイする

コードをデプロイするには、Git Shell で次のコマンドを実行します。

  1. プロジェクト ディレクトリのルートに移動します。 Git リポジトリを使用してプロジェクトを初期化していない場合は、次のコマンドを実行します。

    git init
    
  2. 前に書き留めておいた Git デプロイ URL のリモートを追加します。

    git remote add Azure <your git deployment url>
    
  3. 初期化されたリポジトリ内のすべてのファイルをステージングし、コミットを追加します。

    git add -A
    git commit -m "init commit"
    
  4. Azure の Web アプリにコードをデプロイします。

    git push Azure main
    

    Azure にコードをデプロイするための認証を求めるメッセージが表示されます。 前に作成したデプロイ ユーザーのユーザー名とパスワードを入力します。

GitHub OAuth アプリを更新する

最後に、GitHub OAuth アプリのホームページ URL認証コールバックの URL を、ホストされている新しいアプリを指すように更新する必要があります。

  1. ブラウザーで https://github.com を開き、アカウントの [設定]>[開発者向け設定]>[Oauth Apps] (Oauth アプリ) の順に移動します。

  2. 認証アプリを選択し、[Homepage URL]\(ホームページ URL\)[Authorization callback URL]\(認証コールバックの URL\) を次のように更新します。

    設定
    ホームページ URL https://signalrtestwebapp22665120.azurewebsites.net
    認証コールバックの URL https://signalrtestwebapp22665120.azurewebsites.net/signin-github
  3. Web アプリの URL に移動し、アプリケーションをテストします。

    OAuth Complete hosted in Azure

リソースをクリーンアップする

次のチュートリアルに進む場合は、このクイックスタートで作成したリソースを維持して、次のチュートリアルで再利用することができます。

クイックスタートのサンプル アプリケーションの使用を終える場合は、課金を避けるために、このクイックスタートで作成した Azure リソースを削除することができます。

重要

いったん削除したリソース グループを元に戻すことはできません。リソース グループとそこに存在するすべてのリソースは完全に削除されます。 間違ったリソース グループやリソースをうっかり削除しないようにしてください。 このサンプルのホストとなるリソースを、保持するリソースが含まれている既存のリソース グループ内に作成した場合は、リソース グループを削除するのではなく、個々のブレードから各リソースを個別に削除することができます。

Azure portal にサインインし、 [リソース グループ] を選択します。

[名前でフィルター] ボックスにリソース グループの名前を入力します。 この記事の手順では、SignalRTestResources という名前のリソース グループを使用しました。 結果一覧でリソース グループの [...] をクリックし、 [リソース グループの削除] をクリックします。

Delete

リソース グループの削除の確認を求めるメッセージが表示されます。 確認のためにリソース グループの名前を入力し、 [削除] を選択します。

しばらくすると、リソース グループとそこに含まれているすべてのリソースが削除されます。

次のステップ

このチュートリアルでは、Azure SignalR Service を使用してより良い認証方法を提供するために、OAuth による認証を追加しました。 Azure SignalR Server の使用方法の詳細については、SignalR Service の Azure CLI サンプルを参照してください。