SPA용 Web API 백 엔드를 보호하는 데 사용하는 Identity 방법

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

Important

이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.

현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

ASP.NET Core Identity 는 인증, 권한 부여 및 ID 관리를 처리하는 API를 제공합니다. API를 사용하면 기반 인증을 사용하여 Web API 백 엔드의 엔드포인트를 cookie보호할 수 있습니다. 사용할 cookie수 없는 클라이언트에 대한 토큰 기반 옵션이 있습니다.

이 문서에서는 Angular, React 및 Vue 앱과 같은 SPA용 Web API 백 엔드를 보호하는 방법을 Identity 보여 줍니다. 동일한 백 엔드 API를 사용하여 앱을 보호할 Blazor WebAssembly 수 있습니다.

필수 조건

이 문서에 표시된 단계는 다음을 수행하는 ASP.NET Core Web API 앱에 인증 및 권한 부여를 추가합니다.

  • 인증을 위해 아직 구성되지 않았습니다.
  • net8.0 대상 이상.
  • 최소 API 또는 컨트롤러 기반 API일 수 있습니다.

이 문서의 테스트 지침 중 일부는 프로젝트 템플릿에 포함된 Swagger UI 를 사용합니다. Swagger UI는 Web API 백 엔드와 함께 사용할 Identity 필요가 없습니다.

NuGet 패키지 설치

다음 NuGet 패키지를 설치합니다.

가장 빠른 시작 방법은 메모리 내 데이터베이스를 사용합니다.

나중에 데이터베이스를 SQLite 또는 SQL Server로 변경하여 테스트할 때 또는 프로덕션 사용을 위해 세션 간에 사용자 데이터를 저장합니다. 이는 시작 자습서와 같이 EF Core 마이그레이션을 통해 데이터베이스를 만들어야 하므로 메모리 내와 비교하여 약간 복잡합니다.

Visual Studio에서 NuGet 패키지 관리자를 사용하거나 dotnet add package CLI 명령을 사용하여 이러한 패키지를 설치합니다.

IdentityDbContext 만들기

다음에서 IdentityDbContext<TUser>상속되는 명명 ApplicationDbContext 된 클래스를 추가합니다.

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) :
        base(options)
    { }
}

표시된 코드는 다양한 환경에 대해 데이터베이스를 구성할 수 있도록 하는 특수 생성자를 제공합니다.

이러한 단계에 표시된 코드를 추가할 때 필요에 따라 다음 using 지시문 중 하나 이상을 추가합니다.

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

EF Core 컨텍스트 구성

앞에서 설명한 것처럼 가장 간단한 시작 방법은 메모리 내 데이터베이스를 사용하는 것입니다. 메모리 내의 각 실행은 새 데이터베이스로 시작되며 마이그레이션을 사용할 필요가 없습니다. 호출 WebApplication.CreateBuilder(args)후 다음 코드를 추가하여 메모리 내 데이터베이스를 사용하도록 구성 Identity 합니다.

builder.Services.AddDbContext<ApplicationDbContext>(
    options => options.UseInMemoryDatabase("AppDb"));

테스트할 때 또는 프로덕션 사용을 위해 세션 간에 사용자 데이터를 저장하려면 나중에 데이터베이스를 SQLite 또는 SQL Server로 변경합니다.

컨테이너에 서비스 추가 Identity

호출 WebApplication.CreateBuilder(args)후 DI(종속성 주입) 컨테이너에 서비스를 추가하도록 호출 AddAuthorization 합니다.

builder.Services.AddAuthorization();

API 활성화 Identity

호출 WebApplication.CreateBuilder(args)후 , 호출 AddIdentityApiEndpoints<TUser>(IServiceCollection)AddEntityFrameworkStores<TContext>(IdentityBuilder).

builder.Services.AddIdentityApiEndpoints<IdentityUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

기본적으로 s 및 독점 토큰은 모두 cookie활성화됩니다. Cookie로그인 엔드포인트의 쿼리 문자열 매개 변수가 있는 경우 useCookies 로그인 시 s 및 토큰이 발급됩니다 true.

경로 매핑 Identity

호출 builder.Build()후 엔드포인트를 매핑 Identity 하기 위한 호출MapIdentityApi<TUser>(IEndpointRouteBuilder):

app.MapIdentityApi<IdentityUser>();

선택한 엔드포인트 보안

엔드포인트를 보호하려면 경로를 정의하는 호출에서 Map{Method} 확장 메서드를 사용합니다RequireAuthorization. 예시:

app.MapGet("/weatherforecast", (HttpContext httpContext) =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = summaries[Random.Shared.Next(summaries.Length)]
        })
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi()
.RequireAuthorization();

이 메서드를 RequireAuthorization 사용하여 다음을 수행할 수도 있습니다.

  • 다음 예제와 같이 Swagger UI 엔드포인트를 보호합니다.

    app.MapSwagger().RequireAuthorization();
    
  • 다음 예제와 같이 특정 클레임 또는 권한으로 보호합니다.

    .RequireAuthorization("Admin");
    

컨트롤러 기반 웹 API 프로젝트에서 [] 특성을 컨트롤러 또는 작업에 적용하여 Authorize엔드포인트를 보호합니다.

API 테스트

인증을 테스트하는 빠른 방법은 프로젝트 템플릿에 포함된 메모리 내 데이터베이스 및 Swagger UI를 사용하는 것입니다. 다음 단계에서는 Swagger UI를 사용하여 API를 테스트하는 방법을 보여줍니다. Swagger UI 엔드포인트가 보호되지 않는지 확인합니다.

보안 엔드포인트에 액세스 시도

  • 앱을 실행하고 Swagger UI로 이동합니다.
  • 웹 API 템플릿에서 만든 프로젝트와 같이 /weatherforecast 보안 엔드포인트를 확장합니다.
  • 사용해 보기를 선택합니다.
  • 실행을 선택합니다. 응답은 .입니다 401 - not authorized.

테스트 등록

  • 확장 /register 하여 사용해 보세요.

  • UI의 매개 변수 섹션에서 샘플 요청 본문이 표시됩니다.

    {
      "email": "string",
      "password": "string"
    }
    
  • "string"을 유효한 전자 메일 주소 및 암호로 바꾼 다음 실행을 선택합니다.

    기본 암호 유효성 검사 규칙을 준수하려면 암호 길이가 6자 이상이어야 하며 다음 문자 중 하나 이상을 포함해야 합니다.

    • 대문자
    • 소문자
    • 숫자 숫자
    • 무수 문자

    잘못된 전자 메일 주소 또는 잘못된 암호를 입력하면 결과에 유효성 검사 오류가 포함됩니다. 유효성 검사 오류가 있는 응답 본문의 예는 다음과 같습니다.

    {
      "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
      "title": "One or more validation errors occurred.",
      "status": 400,
      "errors": {
        "PasswordTooShort": [
          "Passwords must be at least 6 characters."
        ],
        "PasswordRequiresNonAlphanumeric": [
          "Passwords must have at least one non alphanumeric character."
        ],
        "PasswordRequiresDigit": [
          "Passwords must have at least one digit ('0'-'9')."
        ],
        "PasswordRequiresLower": [
          "Passwords must have at least one lowercase ('a'-'z')."
        ]
      }
    }
    

    오류는 클라이언트가 구문 분석하고 필요에 따라 유효성 검사 오류를 표시할 수 있도록 ProblemDetails 형식으로 반환됩니다.

    등록에 성공하면 응답이 발생합니다 200 - OK .

로그인 테스트

  • 확장 /login 하여 사용해 보세요. 요청 본문 예제에서는 두 개의 추가 매개 변수를 보여 줍니다.

    {
      "email": "string",
      "password": "string",
      "twoFactorCode": "string",
      "twoFactorRecoveryCode": "string"
    }
    

    이 예제에는 추가 JSON 속성이 필요하지 않으며 삭제할 수 있습니다. useCookiestrue로 설정합니다.

  • "string"을 등록하는 데 사용한 전자 메일 주소 및 암호로 바꾼 다음 실행을 선택합니다.

    로그인에 성공하면 응답 헤더에 200 - OK 응답이 cookie 표시됩니다.

보안 엔드포인트 다시 테스트

성공적으로 로그인한 후 보안 엔드포인트를 다시 실행합니다. 인증 cookie 은 요청과 함께 자동으로 전송되고 엔드포인트는 권한이 부여됩니다. Cookie-based authentication은 브라우저에 안전하게 기본 제공되고 "작동"합니다.

비브로우저 클라이언트를 사용하여 테스트

일부 웹 클라이언트는 기본적으로 헤더에 s를 포함하지 cookie않을 수 있습니다.

  • API를 테스트하기 위해 도구를 사용하는 경우 설정에서 S를 사용하도록 설정 cookie해야 할 수 있습니다.

  • JavaScript fetch API는 기본적으로 포함되지 cookie않습니다. 옵션의 값 include 으로 설정하여 사용하도록 설정합니다credentials.

  • HttpClient 앱에서 실행하려면 Blazor WebAssembly 다음 예제와 같이 자격 증명을 포함해야 합니다HttpRequestMessage.

    request.SetBrowserRequestCredential(BrowserRequestCredentials.Include);
    

토큰 기반 인증 사용

지원하지 cookie않는 클라이언트의 경우 로그인 API는 토큰을 요청하는 매개 변수를 제공합니다. 후속 요청을 인증하는 데 사용할 수 있는 사용자 지정 토큰(ASP.NET Core ID 플랫폼에 소유하는 토큰)이 발급됩니다. 토큰은 헤더에 Authorization 전달자 토큰으로 전달됩니다. 새로 고침 토큰도 제공됩니다. 이 토큰을 사용하면 사용자가 다시 로그인하도록 강제하지 않고 이전 토큰이 만료되면 애플리케이션에서 새 토큰을 요청할 수 있습니다.

토큰은 표준 JSON 웹 토큰(JWT)이 아닙니다. 기본 제공 Identity API는 주로 간단한 시나리오를 위한 것이므로 사용자 지정 토큰의 사용은 의도적입니다. 토큰 옵션은 완전한 기능을 갖춘 ID 서비스 공급자 또는 토큰 서버가 아니라 사용할 cookie수 없는 클라이언트에 대한 옵션의 대안 cookie 입니다.

토큰 기반 인증을 사용하려면 엔드포인트를 useCookies 호출할 때 쿼리 문자열 매개 변수를 /loginfalse 설정합니다. 토큰은 전달자 인증 체계를 사용합니다. 호출에서 반환된 토큰을 /login사용하여 보호된 엔드포인트에 대한 후속 호출은 액세스 토큰이 있는 <token> 헤더 Authorization: Bearer <token> 를 추가해야 합니다. 자세한 내용은 이 문서의 뒷부분에 있는 POST /login 엔드포인트 사용을 참조하세요.

로그아웃

사용자가 로그아웃할 수 있는 방법을 제공하려면 다음 예제와 /logout 같이 엔드포인트를 정의합니다.

app.MapPost("/logout", async (SignInManager<IdentityUser> signInManager,
    [FromBody] object empty) =>
{
    if (empty != null)
    {
        await signInManager.SignOutAsync();
        return Results.Ok();
    }
    return Results.Unauthorized();
})
.WithOpenApi()
.RequireAuthorization();

이 엔드포인트를 호출할 때 요청 본문에 빈 JSON 개체({})를 제공합니다. 다음 코드는 로그아웃 엔드포인트에 대한 호출의 예입니다.

public signOut() {
  return this.http.post('/logout', {}, {
    withCredentials: true,
    observe: 'response',
    responseType: 'text'

MapIdentityApi<TUser> 엔드포인트

앱에 MapIdentityApi<TUser> 다음 엔드포인트를 추가하는 호출입니다.

POST /register 엔드포인트 사용

요청 본문에는 다음과 같은 속성이 EmailPassword 있어야 합니다.

{
  "email": "string",
  "password": "string",
}

자세한 내용은 다음을 참조하세요.

POST /login 엔드포인트 사용

요청 본문 EmailPassword 에서 필수입니다. 2FA(2단계 인증)를 사용하도록 설정한 경우 또는 TwoFactorCodeTwoFactorRecoveryCode 필수입니다. 2FA를 사용하도록 설정하지 않으면 둘 다 twoFactorCode 생략하고 twoFactorRecoveryCode. 자세한 내용은 이 문서의 뒷부분에 있는 POST /manage/2fa 엔드포인트 사용을 참조하세요.

다음은 2FA를 사용할 수 없는 요청 본문 예제입니다.

{
  "email": "string",
  "password": "string"
}

다음은 2FA를 사용하도록 설정된 요청 본문 예제입니다.

  • {
      "email": "string",
      "password": "string",
      "twoFactorCode": "string",
    }
    
  • {
      "email": "string",
      "password": "string",
      "twoFactorRecoveryCode": "string"
    }
    

엔드포인트에는 쿼리 문자열 매개 변수가 필요합니다.

  • useCookies - -based authentication에 true 대해 cookie설정됩니다. 토큰 기반 인증에 false 대해 설정하거나 생략합니다.

-based authentication에 대한 cookie자세한 내용은 이 문서의 앞부분에 있는 테스트 로그인을 참조하세요.

토큰 기반 인증

false 지정되거나 생략된 경우 useCookies 토큰 기반 인증을 사용하도록 설정됩니다. 응답 본문에는 다음 속성이 포함됩니다.

{
  "tokenType": "string",
  "accessToken": "string",
  "expiresIn": 0,
  "refreshToken": "string"
}

이러한 속성에 대한 자세한 내용은 다음을 참조하세요 AccessTokenResponse.

다음 예제와 같이 헤더에 액세스 토큰을 넣어 인증된 요청을 수행합니다.

Authorization: Bearer {access token}

액세스 토큰이 만료되려고 하면 /refresh 엔드포인트를 호출합니다.

POST /refresh 엔드포인트 사용

토큰 기반 인증에만 사용합니다. 사용자가 다시 로그인하도록 강제하지 않고 새 액세스 토큰을 가져옵니다. 액세스 토큰이 만료되려고 할 때 이 엔드포인트를 호출합니다.

요청 본문에는 RefreshToken. 요청 본문 예제는 다음과 같습니다.

{
  "refreshToken": "string"
}

호출에 성공하면 다음 예제와 같이 응답 본문은 새 AccessTokenResponse본문입니다.

{
  "tokenType": "string",
  "accessToken": "string",
  "expiresIn": 0,
  "refreshToken": "string"
}

GET /confirmEmail 엔드포인트 사용

전자 메일 확인을 위해 설정된 경우 Identity 엔드포인트에 /register 대한 성공적인 호출은 엔드포인트에 대한 링크가 포함된 전자 메일을 /confirmEmail 보냅니다. 링크에는 다음 쿼리 문자열 매개 변수가 포함됩니다.

  • userId
  • code
  • changedEmail - 사용자가 등록하는 동안 전자 메일 주소를 변경한 경우에만 포함됩니다.

기본적으로 전자 메일 제목은 "전자 메일 확인"이며 전자 메일 본문은 다음 예제와 같습니다.

 Please confirm your account by <a href='https://contoso.com/confirmEmail?userId={user ID}&code={generated code}&changedEmail={new email address}'>clicking here</a>.

속성이 RequireConfirmedEmail 설정된 true경우 전자 메일의 링크를 클릭하여 전자 메일 주소가 확인될 때까지 사용자가 로그인할 수 없습니다. /confirmEmail 엔드포인트:

  • 전자 메일 주소를 확인하고 사용자가 로그인할 수 있도록 합니다.
  • 응답 본문에 "이메일을 확인해 주셔서 감사합니다."라는 텍스트를 반환합니다.

전자 메일 확인을 위해 설정 Identity 하려면 설정할 RequireConfirmedEmail 코드를 Program.cs 추가하고 DI 컨테이너에 true 구현하는 클래스를 추가합니다IEmailSender. 예시:

builder.Services.Configure<IdentityOptions>(options =>
{
    options.SignIn.RequireConfirmedEmail = true;
});

builder.Services.AddTransient<IEmailSender, EmailSender>();

앞의 예제 EmailSender 에서 구현하는 클래스입니다 IEmailSender. 구현IEmailSender하는 클래스의 예제를 비롯한 자세한 내용은 ASP.NET Core의 계정 확인 및 암호 복구를 참조하세요.

POST /resendConfirmationEmail 엔드포인트 사용

등록된 사용자에 대해 주소가 유효한 경우에만 전자 메일을 보냅니다.

요청 본문에는 Email. 요청 본문 예제는 다음과 같습니다.

{
  "email": "string"
}

자세한 내용은 이 문서의 앞부분에 있는 GET /confirmEmail 엔드포인트 사용을 참조하세요.

POST /forgotPassword 엔드포인트 사용

암호 재설정 코드가 포함된 이메일을 생성합니다. 새 암호를 사용하여 /resetPassword 해당 코드를 보냅니다.

요청 본문에는 Email. 예를 들면 다음과 같습니다.

{
  "email": "string"
}

전자 메일을 보낼 수 있도록 설정하는 Identity 방법에 대한 자세한 내용은 엔드포인트 사용을 GET /confirmEmail 참조하세요.

POST /resetPassword 엔드포인트 사용

엔드포인트를 호출하여 다시 설정 코드를 얻은 후 이 엔드포인트를 호출합니다 /forgotPassword .

요청 본문에는 다음이 ResetCodeNewPassword필요합니다.Email 예를 들면 다음과 같습니다.

{
  "email": "string",
  "resetCode": "string",
  "newPassword": "string"
}

POST /manage/2fa 엔드포인트 사용

사용자에 대한 2FA(2단계 인증)를 구성합니다. 2FA를 사용하도록 설정하면 성공적으로 로그인하려면 이메일 주소 및 암호 외에도 인증자 앱에서 생성한 코드가 필요합니다.

2FA 사용

현재 인증된 사용자에 대해 2FA를 사용하도록 설정하려면 다음을 수행합니다.

  • 엔드포인트를 호출하여 /manage/2fa 요청 본문에 빈 JSON 개체({})를 보냅니다.

  • 응답 본문은 이 시점에서 필요하지 않은 몇 가지 다른 속성과 함께 제공합니다 SharedKey . 공유 키는 인증자 앱을 설정하는 데 사용됩니다. 응답 본문 예제:

    {
      "sharedKey": "string",
      "recoveryCodesLeft": 0,
      "recoveryCodes": null,
      "isTwoFactorEnabled": false,
      "isMachineRemembered": false
    }
    
  • 공유 키를 사용하여 시간 기반 TOTP(일회성 암호)를 가져옵니다. 자세한 내용은 ASP.NET Core에서 TOTP 인증자 앱에 대한 QR 코드 생성 사용을 참조하세요.

  • 엔드포인트를 /manage/2fa 호출하여 TOTP 및 "enable": true 요청 본문을 보냅니다. 예시:

    {
      "enable": true,
      "twoFactorCode": "string"
    }
    
  • 응답 본문은 trueRecoveryCodes임을 IsTwoFactorEnabled 확인하고 . 복구 코드 인증자 앱을 사용할 수 없는 경우 로그인하는 데 사용됩니다. 2FA를 사용하도록 설정한 후의 응답 본문 예제:

    {
      "sharedKey": "string",
      "recoveryCodesLeft": 10,
      "recoveryCodes": [
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string",
        "string"
      ],
      "isTwoFactorEnabled": true,
      "isMachineRemembered": false
    }
    

2FA로 로그인

엔드포인트를 /login 호출하여 요청 본문에 전자 메일 주소, 암호 및 TOTP를 보냅니다. 예시:

{
  "email": "string",
  "password": "string",
  "twoFactorCode": "string"
}

사용자가 인증자 앱에 액세스할 수 없는 경우 2FA를 사용하도록 설정할 때 제공된 복구 코드 중 하나를 사용하여 엔드포인트를 호출 /login 하여 로그인합니다. 요청 본문은 다음 예제와 같습니다.

{
  "email": "string",
  "password": "string",
  "twoFactorRecoveryCode": "string"
}

복구 코드 다시 설정

새 복구 코드 컬렉션을 얻으려면 이 엔드포인트를 로 ResetRecoveryCodes 설정하여 호출합니다true. 요청 본문 예제는 다음과 같습니다.

{
  "resetRecoveryCodes": true
}

공유 키 다시 설정

새 임의 공유 키를 얻으려면 다음으로 설정된 이 엔드포인트를 ResetSharedKey 호출합니다 true. 요청 본문 예제는 다음과 같습니다.

{
  "resetSharedKey": true
}

키를 다시 설정하면 이후 요청에 의해 다시 사용하도록 설정될 때까지 인증된 사용자에 대한 2단계 로그인 요구 사항이 자동으로 비활성화됩니다.

컴퓨터 잊어버리기

있는 경우 "내 플래그 기억"을 cookie 지우려면 true로 ForgetMachine 설정된 이 엔드포인트를 호출합니다. 요청 본문 예제는 다음과 같습니다.

{
  "forgetMachine": true
}

이 엔드포인트는 토큰 기반 인증에 영향을 주지 않습니다.

GET /manage/info 엔드포인트 사용

로그인한 사용자의 전자 메일 주소 및 전자 메일 확인 상태 가져옵니다. 보안상의 이유로 이 엔드포인트에서 클레임을 생략했습니다. 클레임이 필요한 경우 서버 쪽 API를 사용하여 클레임에 대한 엔드포인트를 설정합니다. 또는 모든 사용자의 클레임을 공유하는 대신 클레임을 수락하고 사용자에게 클레임이 있는지 여부를 응답하는 유효성 검사 엔드포인트를 제공합니다.

요청에는 매개 변수가 필요하지 않습니다. 응답 본문에는 다음 예제와 같이 및 IsEmailConfirmed 속성이 포함됩니다Email.

{
  "email": "string",
  "isEmailConfirmed": true
}

POST /manage/info 엔드포인트 사용

로그인한 사용자의 전자 메일 주소 및 암호를 업데이트. NewPassword다음 예제와 OldPassword 같이 요청 본문에서 < a0/>를 보냅니NewEmail다.

{
  "newEmail": "string",
  "newPassword": "string",
  "oldPassword": "string"
}

응답 본문의 예는 다음과 같습니다.

{
  "email": "string",
  "isEmailConfirmed": false
}

참고 항목

자세한 내용은 다음 리소스를 참조하세요.

ASP.NET Core 템플릿은 API 권한 부여에 대한 지원을 사용하여 SPA(단일 페이지 앱)에서 인증을 제공합니다. 사용자를 인증하고 저장하기 위한 Identity는 OpenID Connect 구현을 위해 Duende IdentityServer와 통합됩니다.

Important

Duende Software에서 Duende Identity 서버의 프로덕션 사용에 대한 라이선스 요금 지불을 요구할 수 있습니다. 자세한 내용은 ASP.NET Core 5.0에서 6.0으로 마이그레이션을 참조하세요.

AngularReact 프로젝트 템플릿에 웹 애플리케이션(Model-View-Controller)(MVC) 및 웹 애플리케이션(Razor Pages) 프로젝트 템플릿의 인증 매개 변수와 비슷한 인증 매개 변수가 추가되었습니다. 허용되는 매개 변수 값은 NoneIndividual입니다. React.js and Redux 프로젝트 템플릿은 현재 인증 매개 변수를 지원하지 않습니다.

API 권한 부여를 지원하는 앱 만들기

Angular SPA와 React SPA 양쪽에서 모두 사용자 인증 및 권한 부여를 사용할 수 있습니다. 명령 셸을 열고 다음 명령을 실행합니다.

Angular:

dotnet new angular -au Individual

React:

dotnet new react -au Individual

위 명령은 SPA를 포함하는 ClientApp 디렉터리를 갖는 ASP.NET Core 앱을 만듭니다.

앱의 ASP.NET Core 구성 요소에 대한 일반적인 설명

이어지는 섹션에서는 인증 지원이 포함된 경우 프로젝트에 추가되는 항목에 대해 설명합니다.

Program.cs

다음 코드 예제는 Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet 패키지를 사용합니다. 예제에서는 AddApiAuthorizationAddIdentityServerJwt 확장 메서드를 사용하여 API 인증 및 권한 부여를 구성합니다. 인증을 갖는 React 또는 Angular SPA 프로젝트 템플릿을 사용하는 프로젝트는 이 패키지에 참조를 포함합니다.

dotnet new angular -au Individual는 다음 Program.cs 파일을 생성합니다.

using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using output_directory_name.Data;
using output_directory_name.Models;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

builder.Services.AddAuthentication()
    .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

위의 코드는 다음을 구성합니다.

  • 기본 UI를 갖는 Identity

    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlite(connectionString));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    
    builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
  • IdentityServer 위에 기본 ASP.NET Core 규칙을 설정하는 추가 AddApiAuthorization 도우미 메서드를 갖는 IdentityServer:

    builder.Services.AddIdentityServer()
        .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
    
  • IdentityServer에 의해 생성된 JWT 토큰의 유효성을 검사하도록 앱을 구성하는 추가 AddIdentityServerJwt 도우미 메서드를 사용한 인증:

    builder.Services.AddAuthentication()
    .AddIdentityServerJwt();
    
  • 요청 자격 증명의 유효성을 검사하고 요청 컨텍스트에서 사용자를 설정하는 작업을 담당하는 인증 미들웨어:

    app.UseAuthentication();
    
  • OpenID Connect 엔드포인트를 노출하는 IdentityServer 미들웨어:

    app.UseIdentityServer();
    

Linux의 Azure App Service

Linux의 Azure App Service 배포의 경우, 명시적으로 발급자가 지정됩니다.

builder.Services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme, 
    options =>
    {
        options.Authority = "{AUTHORITY}";
    });

위 코드에서 {AUTHORITY} 자리 표시자는 OpenID Connect 호출을 할 때 사용할 Authority입니다.

예시:

options.Authority = "https://contoso-service.azurewebsites.net";

AddApiAuthorization

이 도우미 메서드는 IdentityServer가 지원되는 구성을 사용하도록 구성합니다. IdentityServer는 앱 보안 문제를 처리하는 강력하고 확장성 있는 프레임워크입니다. 이와 동시에 대부분의 일반적인 시나리오에서 불필요한 복잡성을 노출합니다. 이에 따라, 시작점으로 삼기에 좋은 규칙 및 구성 세트가 제공됩니다. 인증에 변경이 필요한 경우 IdentityServer의 모든 기능을 사용하여 요구 사항에 맞게 인증을 사용자 지정할 수 있습니다.

AddIdentityServerJwt

이 도우미 메서드는 앱의 정책 체계를 기본 인증 처리기로서 구성합니다. 정책은 Identity가 Identity URL 공간 “/Identity”에 있는 모든 하위 경로로 라우팅된 모든 요청을 처리할 수 있도록 구성됩니다. JwtBearerHandler는 다른 모든 요청을 처리합니다. 이 메서드는 또한 <<ApplicationName>>API의 기본 범위를 사용하여 IdentityServer에 <<ApplicationName>>API API 리소스를 등록하고 JWT 전달자 토큰 미들웨어가 앱에 대해 IdentityServer가 발급한 토큰의 유효성을 검사하도록 구성합니다.

WeatherForecastController

이 파일에서 클래스에 적용된 [Authorize] 특성은 사용자가 기본 정책에 따라 인증되어야 리소스에 접근할 수 있음을 나타냅니다. 기본 권한 부여 정책은 기본 인증 체계를 사용하도록 구성되어 있는데, 기본 인증 체계는 AddIdentityServerJwt에 의해 위에서 언급한 정책 체계에 설정되어 있기 때문에 이러한 도우미 메서드가 구성한 JwtBearerHandler가 앱에 대한 요청의 기본 처리기가 됩니다.

ApplicationDbContext

이 파일에서 DbContext는 Identity에서 사용되어, Identity 서버 체계를 포함하기 위하여 ApiAuthorizationDbContext 확장이 되는 예외(IdentityDbContext보다 더 파생된 클래스)가 사용되었음에 주목하세요.

데이터베이스 스키마에 대한 모든 권한을 얻으려면 사용 가능한 IdentityDbContext 클래스 중 하나에서 상속하고 OnModelCreating 메서드에서 builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)를 호출하여 Identity 스키마를 포함하도록 컨텍스트를 구성합니다.

OidcConfigurationController

이 파일에서 클라이언트가 사용하여야 하는 OIDC 매개 변수에 제공하도록 설정된 엔드포인트에 주목하세요.

appsettings.json

프로젝트 루트의 appsettings.json 파일에는 구성된 클라이언트 목록을 설명하는 새로운 IdentityServer 섹션이 있습니다. 다음 예제에는 단일 클라이언트가 있습니다. 클라이언트 이름은 앱 이름에 대응되며 규칙에 따라 OAuth ClientId 매개 변수에 매핑됩니다. 프로필은 구성되는 앱 유형을 나타냅니다. 프로필은 서버에 대한 구성 프로세스를 간소화하는 규칙을 구현하기 위해 내부적으로 사용됩니다. 애플리케이션 프로필 섹션에서 설명하는 것처럼 여러 가지 프로필이 있습니다.

"IdentityServer": {
  "Clients": {
    "angularindividualpreview3final": {
      "Profile": "IdentityServerSPA"
    }
  }
}

appsettings.Development.json

appsettings.Development.json 프로젝트 루트 IdentityServer 의 파일에는 토큰 서명에 사용되는 키를 설명하는 섹션이 있습니다. 프로덕션에 배포할 때는 프로덕션에 배포 섹션에서 설명하는 것처럼 앱과 함께 키를 프로비저닝 및 배포해야 합니다.

"IdentityServer": {
  "Key": {
    "Type": "Development"
  }
}

Angular 앱에 대한 일반적인 설명

Angular 템플릿의 인증 및 API 권한 부여 지원은 자체 Angular 모듈의 ClientApp\src\api-authorization 디렉터리에 있습니다. 모듈은 다음과 같은 요소로 구성됩니다.

  • 3개 구성 요소:
    • login.component.ts: 앱의 로그인 흐름을 처리합니다.
    • logout.component.ts: 앱의 로그아웃 흐름을 처리합니다.
    • login-menu.component.ts: 다음 링크 집합 중 하나를 표시하는 위젯입니다.
      • 사용자가 인증된 경우: 사용자 프로필 관리 링크와 로그아웃 링크.
      • 사용자가 인증되지 않은 경우: 등록 링크와 로그인 링크.
  • 경로 가드 AuthorizeGuard: 경로를 방문하기 전에 사용자에게 인증을 요구합니다. 경로에 추가할 수 있습니다.
  • HTTP 인터셉터 AuthorizeInterceptor: 사용자가 인증된 경우 API를 대상으로 하는 발신 HTTP 요청에 첨부되는 액세스 토큰.
  • 서비스 AuthorizeService: 인증 프로세스의 하위 수준 세부 정보를 처리하고, 앱의 나머지 부분이 사용할 수 있도록 인증된 사용자에 관한 정보를 노출합니다.
  • 앱의 인증 부분과 관련된 경로를 정의하는 Angular 모듈. 앱의 나머지 부분이 사용할 수 있도록 로그인 메뉴 구성 요소, 인터셉터, 가드 및 서비스를 노출합니다.

React 앱에 대한 일반적인 설명

React 템플릿의 인증 및 API 권한 부여 지원은 ClientApp\src\components\api-authorization 디렉터리에 있습니다. 다음과 같은 요소로 구성됩니다.

  • 4개 구성 요소:
    • Login.js: 앱의 로그인 흐름을 처리합니다.
    • Logout.js: 앱의 로그아웃 흐름을 처리합니다.
    • LoginMenu.js: 다음 링크 집합 중 하나를 표시하는 위젯입니다.
      • 사용자가 인증된 경우: 사용자 프로필 관리 링크와 로그아웃 링크.
      • 사용자가 인증되지 않은 경우: 등록 링크와 로그인 링크.
    • AuthorizeRoute.js: 매개 변수에 표시된 Component 구성 요소를 렌더링하기 전에 사용자를 인증해야 하는 경로 구성 요소입니다.
  • AuthorizeService 클래스의 내보낸 authService 인스턴스: 인증 프로세스의 하위 수준 세부 정보를 처리하고, 앱의 나머지 부분이 사용할 수 있도록 인증된 사용자에 관한 정보를 노출합니다.

솔루션의 기본 구성 요소를 살펴보았으니 이번에는 앱의 개별 시나리오를 자세히 살펴보겠습니다.

새 API에서 권한 부여 요구

기본적으로 시스템은 새 API에서 권한 부여를 쉽게 요구할 수 있도록 구성됩니다. 이렇게 하려면 새 컨트롤러를 만들고 컨트롤러 클래스 또는 컨트롤러 내부의 원하는 작업에 [Authorize] 특성을 추가합니다.

API 인증 처리기 사용자 지정

API의 JWT 처리기의 구성을 사용자 지정하려면 JwtBearerOptions 인스턴스를 구성합니다.

builder.Services.AddAuthentication()
    .AddIdentityServerJwt();

builder.Services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        ...
    });

API의 JWT 처리기는 JwtBearerEvents를 사용하여 인증 프로세스를 관리할 수 있도록 지원하는 이벤트를 발생시킵니다. AddIdentityServerJwt는 API 권한 부여에 대한 지원을 제공하기 위해 자체 이벤트 처리기를 등록합니다.

이벤트의 처리를 사용자 지정하려면 필요에 따라 추가 로직을 사용하여 기존 이벤트 처리기를 래핑합니다. 예시:

builder.Services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        var onTokenValidated = options.Events.OnTokenValidated;       

        options.Events.OnTokenValidated = async context =>
        {
            await onTokenValidated(context);
            ...
        }
    });

위 코드에서 OnTokenValidated 이벤트 처리기는 사용자 지정 구현으로 대체됩니다. 이 구현은:

  1. API 권한 부여 지원에서 제공하는 원본 구현을 호출합니다.
  2. 자체 사용자 지정 로직을 실행합니다.

클라이언트 쪽 경로 보호(Angular)

클라이언트 쪽 경로를 보호하는 작업은 경로를 구성할 때 실행할 가드 목록에 권한 부여 가드를 추가하여 수행합니다. 예를 들어, 기본 앱 Angular 모듈 내부에서 fetch-data 경로가 구성되는 방식을 살펴보세요.

RouterModule.forRoot([
  // ...
  { path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])

경로를 보호해도 실제 엔드포인트가 보호되지는 않습니다. (경로를 보호해도 실제 엔드포인트에는 [Authorize] 특성을 적용해야 합니다.) 경로를 보호하면 사용자가 권한이 부여되지 않은 클라이언트 쪽 경로로 이동하는 것이 방지될 뿐입니다.

API 요청 인증(Angular)

앱과 함께 호스트된 API에 대한 요청을 인증하는 작업은 앱에 의에 정의된 HTTP 클라이언트 인터셉터를 사용하여 수행됩니다.

클라이언트 쪽 경로 보호(React)

일반 Route 구성 요소 대신 AuthorizeRoute 구성 요소를 사용하여 클라이언트 쪽 경로를 보호합니다. 예를 들어, App 구성 요소 내부에서 fetch-data 경로가 어떻게 구성되는지 살펴보세요.

<AuthorizeRoute path='/fetch-data' component={FetchData} />

경로 보호는:

  • 실제 엔드포인트를 보호하지 않습니다. (경로를 보호해도 실제 엔드포인트에는 [Authorize] 특성을 적용해야 합니다.)
  • 사용자가 권한이 부여되지 않은 클라이언트 쪽 경로로 이동하는 것만 방지합니다.

API 요청 인증(React)

React를 사용한 요청 인증은 먼저 AuthorizeService에서 authService 인스턴스를 가져오는 것으로 수행됩니다. authService에서 가져온 액세스 토큰이 아래와 같이 요청에 첨부됩니다. React 구성 요소에서 이 작업은 주로 componentDidMount 수명 주기 메서드에서 또는 사용자 상호 작용의 결과로 수행됩니다.

authService를 구성 요소로 가져오기

import authService from './api-authorization/AuthorizeService'

액세스 토큰을 가져와서 응답에 첨부

async populateWeatherData() {
  const token = await authService.getAccessToken();
  const response = await fetch('api/SampleData/WeatherForecasts', {
    headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
  });
  const data = await response.json();
  this.setState({ forecasts: data, loading: false });
}

프로덕션에 배포

앱을 프로덕션에 배포하려면 다음과 같은 리소스를 프로비저닝해야 합니다.

  • Identity 사용자 계정과 IdentityServer 권한을 저장할 데이터베이스.
  • 토큰 서명에 사용할 프로덕션 인증서.
    • 이 인증서에는 별다른 요구 사항이 없습니다. 자체 서명된 인증서일 수도 있고, CA 기관을 통해 프로비저닝된 인증서일 수도 있습니다.
    • PowerShell 또는 OpenSSL과 같은 표준 도구를 통해 생성할 수 있습니다.
    • 대상 머신의 인증서 저장소에 설치할 수도 있고, 강력한 암호를 갖는 .pfx 파일로 배포할 수도 있습니다.

예제: 비Azure 웹 호스팅 공급자에 배포

웹 호스팅 패널에서 인증서를 만들거나 로드합니다. 그런 다음 앱의 appsettings.json 파일에서 키 세부 정보를 포함하도록 IdentityServer 섹션을 수정합니다. 예시:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "WebHosting",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}

앞의 예에서:

  • StoreName은 인증서가 저장되는 인증서 저장소의 이름을 나타냅니다. 여기서는 웹 호스팅 저장소를 가리킵니다.
  • StoreLocation은 인증서를 로드할 위치를 나타냅니다(여기서는 CurrentUser).
  • Name은 인증서의 고유 주체에 해당합니다.

예제: Azure App Service에 배포

이 섹션에서는 인증서 저장소에 저장된 인증서를 사용하여 앱을 Azure App Service에 배포하는 방법을 설명합니다. 앱이 인증서 저장소에서 인증서를 로드하도록 수정하려면, 나중 단계에서 Azure Portal에서 앱을 구성할 때 표준 계층 서비스 플랜 이상이 필요합니다.

앱의 appsettings.json 파일에서 키 세부 정보를 포함하도록 IdentityServer 섹션을 수정합니다.

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "My",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}
  • 저장소 이름은 인증서가 저장된 인증서 저장소의 이름을 나타냅니다. 여기서는 개인 사용자 저장소를 가리킵니다.
  • 저장소 위치는 인증서를 로드할 위치를 나타냅니다(CurrentUser 또는 LocalMachine).
  • 인증서의 이름 속성은 인증서의 고유 주체에 해당합니다.

Azure App Service에 배포하려면 필요한 Azure 리소스를 만들고 앱을 프로덕션에 배포하는 방법을 설명하는 Azure에 앱 배포의 단계를 따릅니다.

위 지침에 따라 배포하면 앱이 Azure에 배포되나 아직 작동하지는 않습니다. 앱이 사용할 인증서를 Azure Portal에서 구성해야 합니다. 인증서의 지문을 확인한 다음 인증서 로드에서 설명하는 단계를 따릅니다.

위 단계에서는 SSL이 언급되어 있으나, Azure Portal에는 앱에서 사용할 프로비저닝된 인증서를 업로드할 수 있는 프라이빗 인증서 섹션이 있습니다.

Azure Portal에서 앱과 앱의 설정을 구성한 후에 포털에서 앱을 다시 시작합니다.

그 밖의 구성 옵션

API 권한 부여에 대한 지원은 SPA 환경을 간소화하는 일련의 규칙, 기본값 및 개선 사항을 갖는 IdentityServer를 기반으로 합니다. ASP.NET Core 통합으로 시나리오를 구현할 수 없는 경우 뒷단에서 IdentityServer의 모든 기능을 사용할 수 있습니다. ASP.NET Core 지원은 모든 앱이 Microsoft에 의해 만들어지고 지원되는 “자사” 앱에 집중합니다. 따라서 동의나 페더레이션과 관련된 기능은 지원되지 않습니다. 이러한 시나리오에서는 IdentityServer를 사용하고 해당 설명서를 따르세요.

애플리케이션 프로필

애플리케이션 프로필은 앱의 매개 변수를 추가로 정의하기 위해 미리 정의된 구성입니다. 현재 지원되는 프로필은 다음과 같습니다.

  • IdentityServerSPA: IdentityServer와 함께 단일 단위로 호스트된 SPA를 나타냅니다.
    • redirect_uri의 기본값은 /authentication/login-callback입니다.
    • post_logout_redirect_uri의 기본값은 /authentication/logout-callback입니다.
    • 범위 세트는 openid, profile 및 앱에서 API에 대해 정의된 모든 범위를 포함합니다.
    • 허용되는 OIDC 응답 유형 세트는 id_token token 또는 개별적인 응답 유형(id_token, token)입니다.
    • 허용되는 응답 모드는 fragment입니다.
  • SPA: IdentityServer와 함께 호스트되지 않은 SPA를 나타냅니다.
    • 범위 세트는 openid, profile 및 앱에서 API에 대해 정의된 모든 범위를 포함합니다.
    • 허용되는 OIDC 응답 유형 세트는 id_token token 또는 개별적인 응답 유형(id_token, token)입니다.
    • 허용되는 응답 모드는 fragment입니다.
  • IdentityServerJwt: IdentityServer와 함께 호스트된 API를 나타냅니다.
    • 앱은 기본값이 앱 이름인 단일 범위를 갖도록 구성됩니다.
  • API: IdentityServer와 함께 호스트되지 않은 API를 나타냅니다.
    • 앱은 기본값이 앱 이름인 단일 범위를 갖도록 구성됩니다.

AppSettings를 통한 구성

Clients 또는 Resources 목록에 앱을 추가하여 구성 시스템을 통해 앱을 구성합니다.

아래 예제에서 볼 수 있는 것처럼 각 클라이언트의 redirect_uripost_logout_redirect_uri 속성을 구성합니다.

"IdentityServer": {
  "Clients": {
    "MySPA": {
      "Profile": "SPA",
      "RedirectUri": "https://www.example.com/authentication/login-callback",
      "LogoutUri": "https://www.example.com/authentication/logout-callback"
    }
  }
}

리소스를 구성할 때는 아래와 같이 리소스의 범위를 구성할 수 있습니다.

"IdentityServer": {
  "Resources": {
    "MyExternalApi": {
      "Profile": "API",
      "Scopes": "a b c"
    }
  }
}

코드를 통한 구성

옵션을 구성하는 작업을 수행하는 AddApiAuthorization의 오버로드를 사용하여 코드를 통해 클라이언트와 리소스를 구성할 수도 있습니다.

AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
    options.Clients.AddSPA(
        "My SPA", spa =>
        spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
           .WithLogoutRedirectUri(
               "http://www.example.com/authentication/logout-callback"));

    options.ApiResources.AddApiResource("MyExternalApi", resource =>
        resource.WithScopes("a", "b", "c"));
});

추가 리소스

ASP.NET Core 3.1 이상 템플릿은 API 권한 부여에 대한 지원을 사용하여 SPA(단일 페이지 앱)에서 인증을 제공합니다. 사용자를 인증하고 저장하기 위한 ASP.NET Core Identity는 OpenID Connect 구현을 위해 IdentityServer와 통합됩니다.

AngularReact 프로젝트 템플릿에 웹 애플리케이션(Model-View-Controller)(MVC) 및 웹 애플리케이션(Razor Pages) 프로젝트 템플릿의 인증 매개 변수와 비슷한 인증 매개 변수가 추가되었습니다. 허용되는 매개 변수 값은 NoneIndividual입니다. React.js and Redux 프로젝트 템플릿은 현재 인증 매개 변수를 지원하지 않습니다.

API 권한 부여를 지원하는 앱 만들기

Angular SPA와 React SPA 양쪽에서 모두 사용자 인증 및 권한 부여를 사용할 수 있습니다. 명령 셸을 열고 다음 명령을 실행합니다.

Angular:

dotnet new angular -o <output_directory_name> 

React:

dotnet new react -o <output_directory_name> -au Individual

위 명령은 SPA를 포함하는 ClientApp 디렉터리를 갖는 ASP.NET Core 앱을 만듭니다.

앱의 ASP.NET Core 구성 요소에 대한 일반적인 설명

이어지는 섹션에서는 인증 지원이 포함된 경우 프로젝트에 추가되는 항목에 대해 설명합니다.

Startup 클래스

다음 코드 예제는 Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet 패키지를 사용합니다. 예제에서는 AddApiAuthorizationAddIdentityServerJwt 확장 메서드를 사용하여 API 인증 및 권한 부여를 구성합니다. 인증을 갖는 React 또는 Angular SPA 프로젝트 템플릿을 사용하는 프로젝트는 이 패키지에 참조를 포함합니다.

Startup 클래스에는 다음과 같은 항목이 추가됩니다.

  • Startup.ConfigureServices 메서드 내부:

    • 기본 UI를 갖는 Identity

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>()
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • IdentityServer 위에 기본 ASP.NET Core 규칙을 설정하는 추가 AddApiAuthorization 도우미 메서드를 갖는 IdentityServer:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • IdentityServer에 의해 생성된 JWT 토큰의 유효성을 검사하도록 앱을 구성하는 추가 AddIdentityServerJwt 도우미 메서드를 사용한 인증:

      services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • Startup.Configure 메서드 내부:

    • 요청 자격 증명의 유효성을 검사하고 요청 컨텍스트에서 사용자를 설정하는 작업을 담당하는 인증 미들웨어:

      app.UseAuthentication();
      
    • OpenID Connect 엔드포인트를 노출하는 IdentityServer 미들웨어:

      app.UseIdentityServer();
      

Linux의 Azure App Service

Linux의 Azure App Service 배포의 경우, Startup.ConfigureServices에서 발급자를 명시적으로 지정합니다.

services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme, 
    options =>
    {
        options.Authority = "{AUTHORITY}";
    });

위 코드에서 {AUTHORITY} 자리 표시자는 OpenID Connect 호출을 할 때 사용할 Authority입니다.

예시:

options.Authority = "https://contoso-service.azurewebsites.net";

AddApiAuthorization

이 도우미 메서드는 IdentityServer가 지원되는 구성을 사용하도록 구성합니다. IdentityServer는 앱 보안 문제를 처리하는 강력하고 확장성 있는 프레임워크입니다. 이와 동시에 대부분의 일반적인 시나리오에서 불필요한 복잡성을 노출합니다. 이에 따라, 시작점으로 삼기에 좋은 규칙 및 구성 세트가 제공됩니다. 인증에 변경이 필요한 경우 IdentityServer의 모든 기능을 사용하여 요구 사항에 맞게 인증을 사용자 지정할 수 있습니다.

AddIdentityServerJwt

이 도우미 메서드는 앱의 정책 체계를 기본 인증 처리기로서 구성합니다. 정책은 Identity가 Identity URL 공간 “/Identity”에 있는 모든 하위 경로로 라우팅된 모든 요청을 처리할 수 있도록 구성됩니다. JwtBearerHandler는 다른 모든 요청을 처리합니다. 이 메서드는 또한 <<ApplicationName>>API의 기본 범위를 사용하여 IdentityServer에 <<ApplicationName>>API API 리소스를 등록하고 JWT 전달자 토큰 미들웨어가 앱에 대해 IdentityServer가 발급한 토큰의 유효성을 검사하도록 구성합니다.

WeatherForecastController

이 파일에서 클래스에 적용된 [Authorize] 특성은 사용자가 기본 정책에 따라 인증되어야 리소스에 접근할 수 있음을 나타냅니다. 기본 권한 부여 정책은 기본 인증 체계를 사용하도록 구성되어 있는데, 기본 인증 체계는 AddIdentityServerJwt에 의해 위에서 언급한 정책 체계에 설정되어 있기 때문에 이러한 도우미 메서드가 구성한 JwtBearerHandler가 앱에 대한 요청의 기본 처리기가 됩니다.

ApplicationDbContext

이 파일에서 DbContext는 Identity에서 사용되어, Identity 서버 체계를 포함하기 위하여 ApiAuthorizationDbContext 확장이 되는 예외(IdentityDbContext보다 더 파생된 클래스)가 사용되었음에 주목하세요.

데이터베이스 스키마에 대한 모든 권한을 얻으려면 사용 가능한 IdentityDbContext 클래스 중 하나에서 상속하고 OnModelCreating 메서드에서 builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)를 호출하여 Identity 스키마를 포함하도록 컨텍스트를 구성합니다.

OidcConfigurationController

이 파일에서 클라이언트가 사용하여야 하는 OIDC 매개 변수에 제공하도록 설정된 엔드포인트에 주목하세요.

appsettings.json

프로젝트 루트의 appsettings.json 파일에는 구성된 클라이언트 목록을 설명하는 새로운 IdentityServer 섹션이 있습니다. 다음 예제에는 단일 클라이언트가 있습니다. 클라이언트 이름은 앱 이름에 대응되며 규칙에 따라 OAuth ClientId 매개 변수에 매핑됩니다. 프로필은 구성되는 앱 유형을 나타냅니다. 프로필은 서버에 대한 구성 프로세스를 간소화하는 규칙을 구현하기 위해 내부적으로 사용됩니다. 애플리케이션 프로필 섹션에서 설명하는 것처럼 여러 가지 프로필이 있습니다.

"IdentityServer": {
  "Clients": {
    "angularindividualpreview3final": {
      "Profile": "IdentityServerSPA"
    }
  }
}

appsettings.Development.json

appsettings.Development.json 프로젝트 루트 IdentityServer 의 파일에는 토큰 서명에 사용되는 키를 설명하는 섹션이 있습니다. 프로덕션에 배포할 때는 프로덕션에 배포 섹션에서 설명하는 것처럼 앱과 함께 키를 프로비저닝 및 배포해야 합니다.

"IdentityServer": {
  "Key": {
    "Type": "Development"
  }
}

Angular 앱에 대한 일반적인 설명

Angular 템플릿의 인증 및 API 권한 부여 지원은 자체 Angular 모듈의 ClientApp\src\api-authorization 디렉터리에 있습니다. 모듈은 다음과 같은 요소로 구성됩니다.

  • 3개 구성 요소:
    • login.component.ts: 앱의 로그인 흐름을 처리합니다.
    • logout.component.ts: 앱의 로그아웃 흐름을 처리합니다.
    • login-menu.component.ts: 다음 링크 집합 중 하나를 표시하는 위젯입니다.
      • 사용자가 인증된 경우: 사용자 프로필 관리 링크와 로그아웃 링크.
      • 사용자가 인증되지 않은 경우: 등록 링크와 로그인 링크.
  • 경로 가드 AuthorizeGuard: 경로를 방문하기 전에 사용자에게 인증을 요구합니다. 경로에 추가할 수 있습니다.
  • HTTP 인터셉터 AuthorizeInterceptor: 사용자가 인증된 경우 API를 대상으로 하는 발신 HTTP 요청에 첨부되는 액세스 토큰.
  • 서비스 AuthorizeService: 인증 프로세스의 하위 수준 세부 정보를 처리하고, 앱의 나머지 부분이 사용할 수 있도록 인증된 사용자에 관한 정보를 노출합니다.
  • 앱의 인증 부분과 관련된 경로를 정의하는 Angular 모듈. 앱의 나머지 부분이 사용할 수 있도록 로그인 메뉴 구성 요소, 인터셉터, 가드 및 서비스를 노출합니다.

React 앱에 대한 일반적인 설명

React 템플릿의 인증 및 API 권한 부여 지원은 ClientApp\src\components\api-authorization 디렉터리에 있습니다. 다음과 같은 요소로 구성됩니다.

  • 4개 구성 요소:
    • Login.js: 앱의 로그인 흐름을 처리합니다.
    • Logout.js: 앱의 로그아웃 흐름을 처리합니다.
    • LoginMenu.js: 다음 링크 집합 중 하나를 표시하는 위젯입니다.
      • 사용자가 인증된 경우: 사용자 프로필 관리 링크와 로그아웃 링크.
      • 사용자가 인증되지 않은 경우: 등록 링크와 로그인 링크.
    • AuthorizeRoute.js: 매개 변수에 표시된 Component 구성 요소를 렌더링하기 전에 사용자를 인증해야 하는 경로 구성 요소입니다.
  • AuthorizeService 클래스의 내보낸 authService 인스턴스: 인증 프로세스의 하위 수준 세부 정보를 처리하고, 앱의 나머지 부분이 사용할 수 있도록 인증된 사용자에 관한 정보를 노출합니다.

솔루션의 기본 구성 요소를 살펴보았으니 이번에는 앱의 개별 시나리오를 자세히 살펴보겠습니다.

새 API에서 권한 부여 요구

기본적으로 시스템은 새 API에서 권한 부여를 쉽게 요구할 수 있도록 구성됩니다. 이렇게 하려면 새 컨트롤러를 만들고 컨트롤러 클래스 또는 컨트롤러 내부의 원하는 작업에 [Authorize] 특성을 추가합니다.

API 인증 처리기 사용자 지정

API의 JWT 처리기의 구성을 사용자 지정하려면 JwtBearerOptions 인스턴스를 구성합니다.

services.AddAuthentication()
    .AddIdentityServerJwt();

services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        ...
    });

API의 JWT 처리기는 JwtBearerEvents를 사용하여 인증 프로세스를 관리할 수 있도록 지원하는 이벤트를 발생시킵니다. AddIdentityServerJwt는 API 권한 부여에 대한 지원을 제공하기 위해 자체 이벤트 처리기를 등록합니다.

이벤트의 처리를 사용자 지정하려면 필요에 따라 추가 로직을 사용하여 기존 이벤트 처리기를 래핑합니다. 예시:

services.Configure<JwtBearerOptions>(
    IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
    options =>
    {
        var onTokenValidated = options.Events.OnTokenValidated;       

        options.Events.OnTokenValidated = async context =>
        {
            await onTokenValidated(context);
            ...
        }
    });

위 코드에서 OnTokenValidated 이벤트 처리기는 사용자 지정 구현으로 대체됩니다. 이 구현은:

  1. API 권한 부여 지원에서 제공하는 원본 구현을 호출합니다.
  2. 자체 사용자 지정 로직을 실행합니다.

클라이언트 쪽 경로 보호(Angular)

클라이언트 쪽 경로를 보호하는 작업은 경로를 구성할 때 실행할 가드 목록에 권한 부여 가드를 추가하여 수행합니다. 예를 들어, 기본 앱 Angular 모듈 내부에서 fetch-data 경로가 구성되는 방식을 살펴보세요.

RouterModule.forRoot([
  // ...
  { path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])

경로를 보호해도 실제 엔드포인트가 보호되지는 않습니다. (경로를 보호해도 실제 엔드포인트에는 [Authorize] 특성을 적용해야 합니다.) 경로를 보호하면 사용자가 권한이 부여되지 않은 클라이언트 쪽 경로로 이동하는 것이 방지될 뿐입니다.

API 요청 인증(Angular)

앱과 함께 호스트된 API에 대한 요청을 인증하는 작업은 앱에 의에 정의된 HTTP 클라이언트 인터셉터를 사용하여 수행됩니다.

클라이언트 쪽 경로 보호(React)

일반 Route 구성 요소 대신 AuthorizeRoute 구성 요소를 사용하여 클라이언트 쪽 경로를 보호합니다. 예를 들어, App 구성 요소 내부에서 fetch-data 경로가 어떻게 구성되는지 살펴보세요.

<AuthorizeRoute path='/fetch-data' component={FetchData} />

경로 보호는:

  • 실제 엔드포인트를 보호하지 않습니다. (경로를 보호해도 실제 엔드포인트에는 [Authorize] 특성을 적용해야 합니다.)
  • 사용자가 권한이 부여되지 않은 클라이언트 쪽 경로로 이동하는 것만 방지합니다.

API 요청 인증(React)

React를 사용한 요청 인증은 먼저 AuthorizeService에서 authService 인스턴스를 가져오는 것으로 수행됩니다. authService에서 가져온 액세스 토큰이 아래와 같이 요청에 첨부됩니다. React 구성 요소에서 이 작업은 주로 componentDidMount 수명 주기 메서드에서 또는 사용자 상호 작용의 결과로 수행됩니다.

authService를 구성 요소로 가져오기

import authService from './api-authorization/AuthorizeService'

액세스 토큰을 가져와서 응답에 첨부

async populateWeatherData() {
  const token = await authService.getAccessToken();
  const response = await fetch('api/SampleData/WeatherForecasts', {
    headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
  });
  const data = await response.json();
  this.setState({ forecasts: data, loading: false });
}

프로덕션에 배포

앱을 프로덕션에 배포하려면 다음과 같은 리소스를 프로비저닝해야 합니다.

  • Identity 사용자 계정과 IdentityServer 권한을 저장할 데이터베이스.
  • 토큰 서명에 사용할 프로덕션 인증서.
    • 이 인증서에는 별다른 요구 사항이 없습니다. 자체 서명된 인증서일 수도 있고, CA 기관을 통해 프로비저닝된 인증서일 수도 있습니다.
    • PowerShell 또는 OpenSSL과 같은 표준 도구를 통해 생성할 수 있습니다.
    • 대상 머신의 인증서 저장소에 설치할 수도 있고, 강력한 암호를 갖는 .pfx 파일로 배포할 수도 있습니다.

예제: 비Azure 웹 호스팅 공급자에 배포

웹 호스팅 패널에서 인증서를 만들거나 로드합니다. 그런 다음 앱의 appsettings.json 파일에서 키 세부 정보를 포함하도록 IdentityServer 섹션을 수정합니다. 예시:

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "WebHosting",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}

앞의 예에서:

  • StoreName은 인증서가 저장되는 인증서 저장소의 이름을 나타냅니다. 여기서는 웹 호스팅 저장소를 가리킵니다.
  • StoreLocation은 인증서를 로드할 위치를 나타냅니다(여기서는 CurrentUser).
  • Name은 인증서의 고유 주체에 해당합니다.

예제: Azure App Service에 배포

이 섹션에서는 인증서 저장소에 저장된 인증서를 사용하여 앱을 Azure App Service에 배포하는 방법을 설명합니다. 앱이 인증서 저장소에서 인증서를 로드하도록 수정하려면, 나중 단계에서 Azure Portal에서 앱을 구성할 때 표준 계층 서비스 플랜 이상이 필요합니다.

앱의 appsettings.json 파일에서 키 세부 정보를 포함하도록 IdentityServer 섹션을 수정합니다.

"IdentityServer": {
  "Key": {
    "Type": "Store",
    "StoreName": "My",
    "StoreLocation": "CurrentUser",
    "Name": "CN=MyApplication"
  }
}
  • 저장소 이름은 인증서가 저장된 인증서 저장소의 이름을 나타냅니다. 여기서는 개인 사용자 저장소를 가리킵니다.
  • 저장소 위치는 인증서를 로드할 위치를 나타냅니다(CurrentUser 또는 LocalMachine).
  • 인증서의 이름 속성은 인증서의 고유 주체에 해당합니다.

Azure App Service에 배포하려면 필요한 Azure 리소스를 만들고 앱을 프로덕션에 배포하는 방법을 설명하는 Azure에 앱 배포의 단계를 따릅니다.

위 지침에 따라 배포하면 앱이 Azure에 배포되나 아직 작동하지는 않습니다. 앱이 사용할 인증서를 Azure Portal에서 구성해야 합니다. 인증서의 지문을 확인한 다음 인증서 로드에서 설명하는 단계를 따릅니다.

위 단계에서는 SSL이 언급되어 있으나, Azure Portal에는 앱에서 사용할 프로비저닝된 인증서를 업로드할 수 있는 프라이빗 인증서 섹션이 있습니다.

Azure Portal에서 앱과 앱의 설정을 구성한 후에 포털에서 앱을 다시 시작합니다.

그 밖의 구성 옵션

API 권한 부여에 대한 지원은 SPA 환경을 간소화하는 일련의 규칙, 기본값 및 개선 사항을 갖는 IdentityServer를 기반으로 합니다. ASP.NET Core 통합으로 시나리오를 구현할 수 없는 경우 뒷단에서 IdentityServer의 모든 기능을 사용할 수 있습니다. ASP.NET Core 지원은 모든 앱이 Microsoft에 의해 만들어지고 지원되는 “자사” 앱에 집중합니다. 따라서 동의나 페더레이션과 관련된 기능은 지원되지 않습니다. 이러한 시나리오에서는 IdentityServer를 사용하고 해당 설명서를 따르세요.

애플리케이션 프로필

애플리케이션 프로필은 앱의 매개 변수를 추가로 정의하기 위해 미리 정의된 구성입니다. 현재 지원되는 프로필은 다음과 같습니다.

  • IdentityServerSPA: IdentityServer와 함께 단일 단위로 호스트된 SPA를 나타냅니다.
    • redirect_uri의 기본값은 /authentication/login-callback입니다.
    • post_logout_redirect_uri의 기본값은 /authentication/logout-callback입니다.
    • 범위 세트는 openid, profile 및 앱에서 API에 대해 정의된 모든 범위를 포함합니다.
    • 허용되는 OIDC 응답 유형 세트는 id_token token 또는 개별적인 응답 유형(id_token, token)입니다.
    • 허용되는 응답 모드는 fragment입니다.
  • SPA: IdentityServer와 함께 호스트되지 않은 SPA를 나타냅니다.
    • 범위 세트는 openid, profile 및 앱에서 API에 대해 정의된 모든 범위를 포함합니다.
    • 허용되는 OIDC 응답 유형 세트는 id_token token 또는 개별적인 응답 유형(id_token, token)입니다.
    • 허용되는 응답 모드는 fragment입니다.
  • IdentityServerJwt: IdentityServer와 함께 호스트된 API를 나타냅니다.
    • 앱은 기본값이 앱 이름인 단일 범위를 갖도록 구성됩니다.
  • API: IdentityServer와 함께 호스트되지 않은 API를 나타냅니다.
    • 앱은 기본값이 앱 이름인 단일 범위를 갖도록 구성됩니다.

AppSettings를 통한 구성

Clients 또는 Resources 목록에 앱을 추가하여 구성 시스템을 통해 앱을 구성합니다.

아래 예제에서 볼 수 있는 것처럼 각 클라이언트의 redirect_uripost_logout_redirect_uri 속성을 구성합니다.

"IdentityServer": {
  "Clients": {
    "MySPA": {
      "Profile": "SPA",
      "RedirectUri": "https://www.example.com/authentication/login-callback",
      "LogoutUri": "https://www.example.com/authentication/logout-callback"
    }
  }
}

리소스를 구성할 때는 아래와 같이 리소스의 범위를 구성할 수 있습니다.

"IdentityServer": {
  "Resources": {
    "MyExternalApi": {
      "Profile": "API",
      "Scopes": "a b c"
    }
  }
}

코드를 통한 구성

옵션을 구성하는 작업을 수행하는 AddApiAuthorization의 오버로드를 사용하여 코드를 통해 클라이언트와 리소스를 구성할 수도 있습니다.

AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
    options.Clients.AddSPA(
        "My SPA", spa =>
        spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
           .WithLogoutRedirectUri(
               "http://www.example.com/authentication/logout-callback"));

    options.ApiResources.AddApiResource("MyExternalApi", resource =>
        resource.WithScopes("a", "b", "c"));
});

추가 리소스