Azure SignalR Service 인증

이 자습서는 SignalR Service를 사용하여 채팅방 만들기에 도입된 채팅방 애플리케이션을 계속 진행합니다. 먼저 이 빠른 시작을 완료하여 채팅방을 설정합니다.

이 자습서에서는 Microsoft Azure SignalR Service를 사용하여 인증 방법을 만들고 통합하는 방법을 알아봅니다.

빠른 시작의 채팅방 애플리케이션에서 처음에 사용된 인증은 실제 시나리오에 비해 너무 간단합니다. 애플리케이션을 사용하면 각 클라이언트가 자신이 누구인지 클레임할 수 있으며 서버는 이를 수락하기만 하면 됩니다. 악의적인 사용자가 가짜 ID를 사용하여 중요한 데이터에 액세스할 수 있으므로 이 방법은 실제 환경에서는 효과가 없습니다.

GitHubOAuth라는 인기 있는 업계 표준 프로토콜에 따른 인증 API를 제공합니다. 이러한 API를 사용하면 타사 애플리케이션이 GitHub 계정을 인증할 수 있습니다. 이 자습서에서는 이러한 API를 사용하여 채팅방 애플리케이션에 클라이언트 로그인을 허용하기 전에 GitHub 계정을 통해 인증을 구현할 수 있습니다. GitHub 계정 인증 후 계정 정보는 웹 클라이언트에서 인증하는 데 사용할 쿠키로 추가됩니다.

GitHub를 통해 제공되는 OAuth 인증 API에 대한 자세한 내용은 인증의 기본 사항을 참조하세요.

모든 코드 편집기를 사용하여 이 빠른 시작의 단계를 완료할 수 있습니다. 그러나 Visual Studio Code 는 Windows, macOS 및 Linux 플랫폼에서 사용할 수 있는 훌륭한 옵션입니다.

이 자습서의 코드는 AzureSignalR 샘플 GitHub 리포지토리에서 다운로드할 수 있습니다.

OAuth Complete hosted in Azure

이 자습서에서는 다음을 하는 방법을 알아볼 수 있습니다.

  • GitHub 계정에 새 OAuth 앱 등록
  • GitHub 인증을 지원하기 위한 인증 컨트롤러 추가
  • Azure에 ASP.NET Core 웹앱 배포

Azure를 구독하고 있지 않다면 시작하기 전에 Azure 체험 계정을 만듭니다.

필수 조건

이 자습서를 완료하려면 다음 필수 구성 요소가 있어야 합니다.

OAuth 앱 만들기

  1. 웹 브라우저를 열고 계정으로 이동하여 https://github.com 로그인합니다.

  2. 계정의 경우 설정>Developer 설정>OAuth 앱으로 이동하고 OAuth 앱 아래에서 새 OAuth 앱 선택합니다.

  3. 새 OAuth 앱 다음 설정을 사용한 다음 애플리케이션 등록을 선택합니다.

    설정 이름 제안 값 설명
    애플리케이션 이름 Azure SignalR Chat GitHub 사용자는 인증하는 앱을 인식하고 신뢰할 수 있어야 합니다.
    홈페이지 URL https://localhost:5001
    애플리케이션 설명 GitHub 인증과 함께 Azure SignalR Service를 사용하는 채팅방 샘플 애플리케이션 사용자가 사용 중인 인증의 컨텍스트를 이해하는 데 도움이 되는 애플리케이션에 대한 유용한 설명입니다.
    권한 부여 콜백 URL https://localhost:5001/signin-github 이 설정은 OAuth 애플리케이션에 대한 가장 중요한 설정입니다. GitHub가 인증에 성공한 후 사용자를 반환하는 콜백 URL입니다. 이 자습서에서는 AspNet.Security.OAuth.GitHub 패키지 /signin-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 했기 때문에 사용되는 것을 확인할 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 앱을 사용하여 클라이언트를 인증하는 API를 구현 Login 합니다. 인증되면 API는 클라이언트를 다시 채팅 앱으로 리디렉션하기 전에 웹 클라이언트 응답에 쿠키를 추가합니다. 그런 다음 해당 쿠키를 사용하여 클라이언트를 식별합니다.

  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. 변경 내용을 저장합니다.

허브 클래스 업데이트

기본적으로 웹 클라이언트는 Azure SignalR SDK에서 자동으로 생성된 액세스 토큰을 사용하여 SignalR Service에 연결합니다.

이 섹션에서는 허브 클래스에 특성을 추가하여 Authorize 실제 인증 워크플로를 통합하고 인증된 사용자의 클레임에서 사용자 이름을 읽도록 허브 메서드를 업데이트합니다.

  1. Hub\ChatSampleHub.cs 열고 코드를 아래 코드 조각으로 업데이트합니다. 이 코드는 클래스에 AuthorizeChatSampleHub 특성을 추가하고 허브 메서드에서 사용자의 인증된 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. 변경 내용을 저장합니다.

웹 클라이언트 코드 업데이트

  1. wwwroot\index.html 열고 사용자 이름을 묻는 코드를 인증 컨트롤러에서 반환된 쿠키를 사용하는 코드로 바꿉니다.

    쿠키를 사용하도록 index.html 함수 getUserName내 코드를 다음으로 업데이트합니다.

    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. 허브 메서드 broadcastMessage 를 호출할 때 매개 변수를 username 제거하도록 함수를 업데이트 onConnected 하고 echo다음을 수행합니다.

    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. 다음 명령을 실행하여 웹앱을 로컬로 실행합니다.

    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 맨 위에 있는 여기 링크를 선택하여 GitHub로 로그인합니다.

    OAuth Complete hosted in Azure

    GitHub 계정에 대한 채팅 앱의 액세스 권한을 부여하라는 메시지가 표시됩니다. 권한 부여 단추를 선택합니다.

    Authorize OAuth App

    채팅 애플리케이션으로 다시 리디렉션되고 GitHub 계정 이름으로 로그인됩니다. 웹 애플리케이션은 추가한 새 인증을 사용하여 사용자를 인증하여 계정 이름을 결정했습니다.

    Account identified

    이제 채팅 앱이 GitHub로 인증을 수행하고 인증 정보를 쿠키로 저장하면 다음 단계에서는 Azure에 배포해야 합니다. 이 방법을 사용하면 다른 사용자가 해당 계정을 사용하여 인증하고 다양한 워크스테이션에서 통신할 수 있습니다.

Azure에 앱 배포

Azure CLI에 대한 환경을 준비합니다.

이 섹션에서는 Azure CLI를 사용하여 Azure 앱 Service에서 새 웹앱을 만들어 Azure에서 ASP.NET 애플리케이션을 호스트합니다. 웹앱은 로컬 Git 배포를 사용하도록 구성됩니다. 또한 웹앱은 SignalR 연결 문자열, GitHub OAuth 앱 비밀 및 배포 사용자로 구성됩니다.

다음 리소스를 만들 때 SignalR Service 리소스가 있는 것과 동일한 리소스 그룹을 사용해야 합니다. 이 방법을 사용하면 나중에 모든 리소스를 제거하려는 경우 클린 훨씬 더 쉬워집니다. 지정된 예제에서는 이전 자습서인 SignalRTestResources에서 권장되는 그룹 이름을 사용했다고 가정합니다.

웹앱 만들기 및 계획

아래 명령의 텍스트를 복사하고 매개 변수를 업데이트합니다. 업데이트된 스크립트를 Azure Cloud Shell에 붙여넣고 Enter 키를 눌러 새 App Service 계획 및 웹앱을 만듭니다.

#========================================================================
#=== 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 이 매개 변수는 새 웹앱의 이름 및 URL의 일부입니다. 고유하게 만드십시오. 예를 들어 signalrtestwebapp22665120입니다.

웹앱에 앱 설정 추가

이 섹션에서는 다음 구성 요소에 대한 앱 설정을 추가합니다.

  • 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 이 변수를 이전 섹션에서 만든 새 웹앱의 이름으로 업데이트합니다.

로컬 Git 배포를 위한 웹앱 구성

Azure Cloud Shell에서 다음 스크립트를 붙여넣습니다. 이 스크립트는 Git을 사용하여 웹앱에 코드를 배포할 때 사용하는 새 배포 사용자 이름 및 암호를 만듭니다. 또한 스크립트는 로컬 Git 리포지토리를 사용하여 배포할 웹앱을 구성하고 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 이 매개 변수는 이전에 만든 새 웹앱의 이름입니다.

이 명령에서 반환된 Git 배포 URL을 기록해 둡다. 나중에 이 URL을 사용합니다.

Azure 웹앱에 코드 배포

코드를 배포하려면 Git 셸에서 다음 명령을 실행합니다.

  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에서 웹앱에 코드를 배포합니다.

    git push Azure main
    

    Azure에 코드를 배포하기 위해 인증하라는 메시지가 표시됩니다. 위에서 만든 배포 사용자의 사용자 이름과 암호를 입력합니다.

GitHub OAuth 앱 업데이트

마지막으로 해야 할 일은 GitHub OAuth 앱의 홈페이지 URL권한 부여 콜백 URL을 업데이트하여 호스트된 새 앱을 가리키는 것입니다.

  1. 브라우저에서 열고 https://github.com 계정의 설정>Developer 설정>Oauth Apps로 이동합니다.

  2. 인증 앱을 선택하고 아래와 같이 홈페이지 URL권한 부여 콜백 URL을 업데이트합니다.

    설정 예시
    홈페이지 URL https://signalrtestwebapp22665120.azurewebsites.net
    권한 부여 콜백 URL https://signalrtestwebapp22665120.azurewebsites.net/signin-github
  3. 웹앱 URL로 이동하여 애플리케이션을 테스트합니다.

    OAuth Complete hosted in Azure

리소스 정리

다음 자습서를 계속 진행하는 경우 이 빠른 시작에서 만든 리소스를 유지하고 다음 자습서에서 다시 사용할 수 있습니다.

그렇지 않고, 빠른 시작 샘플 애플리케이션 사용이 끝나면 이 빠른 시작에서 만든 Azure 리소스를 삭제하여 요금이 청구되는 것을 방지할 수 있습니다.

Important

리소스 그룹 삭제는 취소할 수 없으며 해당 리소스 그룹 및 해당 그룹 안에 있는 모든 리소스는 영구적으로 삭제됩니다. 잘못된 리소스 그룹 또는 리소스를 자동으로 삭제하지 않도록 해야 합니다. 유지하려는 리소스가 포함된 기존 리소스 그룹 내에 이 샘플을 호스트하기 위한 리소스를 만든 경우 리소스 그룹을 삭제하는 대신, 해당 블레이드에서 각 리소스를 개별적으로 삭제할 수 있습니다.

Azure Portal에 로그인하고 리소스 그룹을 선택합니다.

이름을 기준으로 필터링... 텍스트 상자에 리소스 그룹의 이름을 입력합니다. 이 문서의 지침에서는 SignalRTestResources라는 리소스 그룹을 사용했습니다. 결과 목록의 리소스 그룹에서 ...를 클릭한 다음 리소스 그룹을 삭제합니다.

Delete

리소스 그룹 삭제를 확인하는 메시지가 표시됩니다. 확인할 리소스 그룹의 이름을 입력하고 삭제를 선택합니다.

잠시 후, 리소스 그룹 및 해당 그룹에 포함된 모든 리소스가 삭제됩니다.

다음 단계

이 자습서에서는 Azure SignalR Service를 사용하여 더 나은 인증 방법을 제공하기 위해 OAuth 인증을 추가했습니다. Azure SignalR Server 사용에 대해 자세히 알아보려면 SignalR Service용 Azure CLI 샘플을 계속 진행하세요.