ASP.NET Core Identity 없이 cookie 인증 사용

작성자: Rick Anderson

ASP.NET Core Identity는 로그인을 만들고 유지 관리하기 위한 완전한 기능을 갖춘 인증 공급자입니다. 그러나 ASP.NET Core Identity 없는 cookie 기반 인증 공급자를 사용할 수 있습니다. 자세한 내용은 ASP.NET Core의 Identity 소개를 참조하세요.

샘플 코드 보기 및 다운로드(다운로드 방법)

샘플 앱의 데모용으로, 가상 사용자인 Maria Rodriguez의 사용자 계정이 앱에 하드 코딩됩니다. 이메일 주소 maria.rodriguez@contoso.com 및 암호를 사용하여 사용자를 로그인합니다. 사용자가 파일의 AuthenticateUser 메서드에서 Pages/Account/Login.cshtml.cs 인증됩니다. 실제 예제에서는 사용자가 데이터 저장소에 대해 인증됩니다.

using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie();

builder.Services.AddHttpContextAccessor();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

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

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

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

AddAuthentication에 전달된 AuthenticationScheme는 앱의 기본 인증 체계를 설정합니다. AuthenticationScheme는 cookie 인증의 인스턴스가 여러 개이고 앱이 특정 스키마로 권한을 부여해야 하는 경우에 유용합니다. AuthenticationSchemeCookieAuthenticationDefaults.AuthenticationScheme에 설정하면 스키마에 대한 "Cookies" 값이 제공됩니다. 스키마를 구분하는 모든 문자열 값을 사용할 수 있습니다.

앱의 인증 체계가 앱의 cookie 인증 체계와 다릅니다. cookie 인증 스키마가 AddCookie에 제공되지 않으면 CookieAuthenticationDefaults.AuthenticationScheme를 사용합니다. CookieAuthenticationDefaults.AuthenticationScheme GitHub 원본"Cookies"에 설정되었음을 표시합니다.

기본적으로 인증 cookie의 IsEssential 속성은 true로 설정됩니다. 사이트 방문자가 데이터 수집에 동의하지 않은 경우에는 인증 cookie를 사용할 수 있습니다. 자세한 내용은 ASP.NET Core의 GDPR(일반 데이터 보호 규정) 지원을 참조하세요.

CookieAuthenticationOptions 클래스는 인증 공급자 옵션을 구성하는 데 사용됩니다.

AddCookie 메서드에서 CookieAuthenticationOptions를 구성합니다.

using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
        options.SlidingExpiration = true;
        options.AccessDeniedPath = "/Forbidden/";
    });

builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

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

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

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

Cookie 정책 미들웨어(GitHub 원본)UseCookiePolicy는 cookie 정책 기능을 사용하도록 설정합니다. 미들웨어는 추가된 순서대로 처리됩니다.

app.UseCookiePolicy(cookiePolicyOptions);

Cookie 정책 미들웨어에 제공된 CookiePolicyOptions를 사용하여 cookie 처리의 글로벌 특성을 제어하고 cookie가 추가되거나 삭제될 때 cookie 처리 처리기에 연결됩니다.

기본 MinimumSameSitePolicy 값은 OAuth2 인증을 허용하는 SameSiteMode.Lax입니다. SameSiteMode.Strict의 동일한 사이트 정책을 엄격하게 적용하려면 MinimumSameSitePolicy를 설정합니다. 이 설정은 OAuth2 및 다른 원본 간 인증 스키마를 중단하지만 원본 간 요청 처리에 의존하지 않는 다른 유형의 앱에 대한 cookie 보안 수준을 강화합니다.

var cookiePolicyOptions = new CookiePolicyOptions
{
    MinimumSameSitePolicy = SameSiteMode.Strict,
};

MinimumSameSitePolicy에 대한 Cookie 정책 미들웨어 설정은 아래 행렬에 따라 CookieAuthenticationOptions 설정의 Cookie.SameSite 설정에 영향을 줄 수 있습니다.

MinimumSameSitePolicy Cookie.SameSite 결과 Cookie.SameSite 설정
SameSiteMode.None SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict

사용자 정보를 보유하는 cookie를 만들려면 ClaimsPrincipal을 구성합니다. 사용자 정보는 직렬화되고 cookie에 저장됩니다.

필요한 Claim을 사용하여 ClaimsIdentity를 만들고 SignInAsync를 호출하여 사용자를 로그인합니다.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    ReturnUrl = returnUrl;

    if (ModelState.IsValid)
    {
        // Use Input.Email and Input.Password to authenticate the user
        // with your custom authentication logic.
        //
        // For demonstration purposes, the sample validates the user
        // on the email address maria.rodriguez@contoso.com with 
        // any password that passes model validation.

        var user = await AuthenticateUser(Input.Email, Input.Password);

        if (user == null)
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }

        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.Email),
            new Claim("FullName", user.FullName),
            new Claim(ClaimTypes.Role, "Administrator"),
        };

        var claimsIdentity = new ClaimsIdentity(
            claims, CookieAuthenticationDefaults.AuthenticationScheme);

        var authProperties = new AuthenticationProperties
        {
            //AllowRefresh = <bool>,
            // Refreshing the authentication session should be allowed.

            //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
            // The time at which the authentication ticket expires. A 
            // value set here overrides the ExpireTimeSpan option of 
            // CookieAuthenticationOptions set with AddCookie.

            //IsPersistent = true,
            // Whether the authentication session is persisted across 
            // multiple requests. When used with cookies, controls
            // whether the cookie's lifetime is absolute (matching the
            // lifetime of the authentication ticket) or session-based.

            //IssuedUtc = <DateTimeOffset>,
            // The time at which the authentication ticket was issued.

            //RedirectUri = <string>
            // The full path or absolute URI to be used as an http 
            // redirect response value.
        };

        await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme, 
            new ClaimsPrincipal(claimsIdentity), 
            authProperties);

        _logger.LogInformation("User {Email} logged in at {Time}.", 
            user.Email, DateTime.UtcNow);

        return LocalRedirect(Url.GetLocalUrl(returnUrl));
    }

    // Something failed. Redisplay the form.
    return Page();
}

영어 이외의 언어로 번역된 코드 주석을 보려면 이 GitHub 토론 이슈에서 알려주세요.

SignInAsync는 암호화된 cookie를 만들어 현재 응답에 추가합니다. AuthenticationScheme를 지정하지 않은 경우 기본 스키마가 사용됩니다.

RedirectUri는 기본적으로 몇 가지 특정 경로(예: 로그인 경로 및 로그아웃 경로)에서만 사용됩니다. 자세한 내용은 CookieAuthenticationHandler 원본을 참조하세요.

ASP.NET Core의 데이터 보호 시스템은 암호화에 사용됩니다. 여러 머신에서 호스트되는 앱, 앱 간에 부하를 분산하거나 웹 팜을 사용하는 경우 동일한 키 링 및 앱 식별자를 사용하도록 데이터 보호를 구성합니다.

로그아웃

현재 사용자를 로그아웃하고 해당 cookie를 삭제하려면 SignOutAsync를 호출합니다.

public async Task OnGetAsync(string returnUrl = null)
{
    if (!string.IsNullOrEmpty(ErrorMessage))
    {
        ModelState.AddModelError(string.Empty, ErrorMessage);
    }

    // Clear the existing external cookie
    await HttpContext.SignOutAsync(
        CookieAuthenticationDefaults.AuthenticationScheme);

    ReturnUrl = returnUrl;
}

CookieAuthenticationDefaults.AuthenticationScheme 또는 "Cookie"는 스키마로 사용되지 않는 경우 인증 공급자를 구성할 때 사용되는 체계를 제공합니다. 그렇지 않으면 기본 스키마가 사용됩니다. 예를 들어 "ContosoCookie"가 스키마로 사용되는 경우 인증 공급자를 구성할 때 사용되는 체계를 제공합니다.

브라우저가 닫히면 세션 기반 cookie(비영구 cookie)가 자동으로 삭제되지만 개별 탭이 닫힐 때 cookie는 제거되지 않습니다. 탭 또는 브라우저 닫기 이벤트에 대한 알림이 서버에 표시되지 않습니다.

백 엔드 변경 내용에 대응

cookie가 생성되면 cookie는 ID의 단일 원본입니다. 백 엔드 시스템에서 사용자 계정을 사용할 수 없는 경우:

  • 앱의 cookie 인증 시스템은 인증 cookie에 따라 요청을 계속 처리합니다.
  • 인증 cookie가 유효한 한 사용자는 앱에 로그인된 상태를 유지합니다.

ValidatePrincipal 이벤트를 사용하여 cookie ID의 유효성 검사를 가로채고 재정의할 수 있습니다. 모든 요청에서 cookie의 유효성을 검사하면 해지된 사용자가 앱에 액세스하는 위험을 완화할 수 있습니다.

cookie 유효성 검사에 대한 한 가지 접근 방식은 사용자 데이터베이스가 변경되는 시기를 추적하는 것입니다. 사용자의 cookie가 발급된 후 데이터베이스가 변경되지 않은 경우에 해당 cookie가 여전히 유효하면 사용자를 다시 인증할 필요가 없습니다. 샘플 앱에서 데이터베이스는 IUserRepository로 구현되고 LastChanged 값을 저장합니다. 데이터베이스에서 사용자를 업데이트하는 경우 LastChanged 값은 현재 시간으로 설정됩니다.

LastChanged 값에 따라 데이터베이스가 변경될 때 cookie를 무효화하려면 데이터베이스의 현재 LastChanged 값이 포함된 LastChanged 클레임을 사용하여 cookie를 만듭니다.

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.Email),
    new Claim("LastChanged", {Database Value})
};

var claimsIdentity = new ClaimsIdentity(
    claims,
    CookieAuthenticationDefaults.AuthenticationScheme);

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme, 
    new ClaimsPrincipal(claimsIdentity));

ValidatePrincipal 이벤트에 대한 재정의를 구현하려면 CookieAuthenticationEvents에서 파생되는 클래스에서 다음 시그니처를 사용하여 메서드를 작성합니다.

ValidatePrincipal(CookieValidatePrincipalContext)

다음은 CookieAuthenticationEvents의 구현 예제입니다.

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
    private readonly IUserRepository _userRepository;

    public CustomCookieAuthenticationEvents(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
    {
        var userPrincipal = context.Principal;

        // Look for the LastChanged claim.
        var lastChanged = (from c in userPrincipal.Claims
                           where c.Type == "LastChanged"
                           select c.Value).FirstOrDefault();

        if (string.IsNullOrEmpty(lastChanged) ||
            !_userRepository.ValidateLastChanged(lastChanged))
        {
            context.RejectPrincipal();

            await context.HttpContext.SignOutAsync(
                CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

cookie 서비스 등록 중에 이벤트 인스턴스를 등록합니다. CustomCookieAuthenticationEvents 클래스에 대해 범위가 지정된 서비스 등록을 제공합니다.

using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

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

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.EventsType = typeof(CustomCookieAuthenticationEvents);
    });

builder.Services.AddScoped<CustomCookieAuthenticationEvents>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

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

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

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

사용자의 이름이 보안에 영향을 주지 않는 결정으로 업데이트되는 상황을 살펴봅시다. 사용자 보안 주체를 비파괴적으로 업데이트하려면 context.ReplacePrincipal를 호출하고 context.ShouldRenew 속성을 true로 설정합니다.

Warning

여기에 설명된 방식은 모든 요청에서 트리거됩니다. 모든 요청에서 모든 사용자에 대한 인증 cookie의 유효성을 검사하면 앱의 성능이 크게 저하될 수 있습니다.

지속성 cookie

cookie가 브라우저 세션에서 지속되도록 할 수 있습니다. 이 지속성은 로그인 시 또는 유사한 메커니즘에서 "Remember Me" 확인란을 통해 명시적 사용자 동의가 있는 경우에만 활성화되어야 합니다.

다음 코드 조각에서는 브라우저 클로저를 통해 유지되는 ID와 해당 cookie를 만듭니다. 이전에 구성된 슬라이딩 만료 설정은 모두 적용됩니다. 브라우저가 닫혀 있는 동안 cookie가 만료된 경우 브라우저가 다시 시작되면 cookie를 지웁니다.

AuthenticationProperties에서 IsPersistenttrue로 설정합니다.

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true
    });

절대 만료 시간은 ExpiresUtc로 설정할 수 있습니다. 영구 cookie를 만들려면 IsPersistent도 설정해야 합니다. 그렇지 않으면 cookie는 세션 기반 수명을 통해 만들어지고 해당 수명이 있는 인증 티켓 전후에 만료될 수 있습니다. ExpiresUtc가 설정되면 CookieAuthenticationOptionsExpireTimeSpan 옵션 값이 재정의됩니다(설정된 경우).

다음 코드 조각에서는 20분 동안 지속되는 ID와 해당 cookie를 만듭니다. 이렇게 하면 이전에 구성된 슬라이딩 만료 설정이 무시됩니다.

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true,
        ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
    });

ASP.NET Core Identity는 로그인을 만들고 유지 관리하기 위한 완전한 기능을 갖춘 인증 공급자입니다. 그러나 ASP.NET Core Identity 없는 cookie 기반 인증 공급자를 사용할 수 있습니다. 자세한 내용은 ASP.NET Core의 Identity 소개를 참조하세요.

샘플 코드 보기 및 다운로드(다운로드 방법)

샘플 앱의 데모용으로, 가상 사용자인 Maria Rodriguez의 사용자 계정이 앱에 하드 코딩됩니다. 이메일 주소 maria.rodriguez@contoso.com 및 암호를 사용하여 사용자를 로그인합니다. 사용자가 파일의 AuthenticateUser 메서드에서 Pages/Account/Login.cshtml.cs 인증됩니다. 실제 예제에서는 사용자가 데이터베이스에 대해 인증됩니다.

구성

Startup.ConfigureServices 메서드에서 AddAuthenticationAddCookie 메서드를 사용하여 인증 미들웨어 서비스를 만듭니다.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie();

AddAuthentication에 전달된 AuthenticationScheme는 앱의 기본 인증 체계를 설정합니다. AuthenticationScheme는 cookie 인증 인스턴스가 여러 개 있고 특정 스키마를 사용하여 권한을 부여하려는 경우에 유용합니다. AuthenticationSchemeCookieAuthenticationDefaults.AuthenticationScheme으로 설정하면 스키마에 "Cookie" 값이 제공됩니다. 체계를 구별하는 모든 문자열 값을 제공할 수 있습니다.

앱의 인증 체계가 앱의 cookie 인증 체계와 다릅니다. cookie 인증 체계가 AddCookie에 제공되지 않으면 CookieAuthenticationDefaults.AuthenticationScheme("Cookie")를 사용합니다.

기본적으로 인증 cookie의 IsEssential 속성은 true로 설정됩니다. 사이트 방문자가 데이터 수집에 동의하지 않은 경우에는 인증 cookie를 사용할 수 있습니다. 자세한 내용은 ASP.NET Core의 GDPR(일반 데이터 보호 규정) 지원을 참조하세요.

Startup.Configure에서 UseAuthenticationUseAuthorization을 호출하여 HttpContext.User 속성을 설정하고 요청에 대한 권한 부여 미들웨어를 실행합니다. UseEndpoints를 호출하기 전에 UseAuthenticationUseAuthorization 메서드를 호출합니다.

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

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapRazorPages();
});

CookieAuthenticationOptions 클래스는 인증 공급자 옵션을 구성하는 데 사용됩니다.

Startup.ConfigureServices 메서드의 인증에 대한 서비스 구성에서 CookieAuthenticationOptions를 설정합니다.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        ...
    });

Cookie 정책 미들웨어는 cookie 정책 기능을 사용하도록 설정합니다. 앱 처리 파이프라인에 미들웨어를 추가하는 것은 순서가 중요하며, 파이프라인에 등록된 다운스트림 구성 요소에만 영향을 줍니다.

app.UseCookiePolicy(cookiePolicyOptions);

Cookie 정책 미들웨어에 제공된 CookiePolicyOptions를 사용하여 cookie 처리의 글로벌 특성을 제어하고 cookie가 추가되거나 삭제될 때 cookie 처리 처리기에 연결됩니다.

기본 MinimumSameSitePolicy 값은 OAuth2 인증을 허용하는 SameSiteMode.Lax입니다. SameSiteMode.Strict의 동일한 사이트 정책을 엄격하게 적용하려면 MinimumSameSitePolicy를 설정합니다. 이 설정은 OAuth2 및 다른 원본 간 인증 스키마를 중단하지만 원본 간 요청 처리에 의존하지 않는 다른 유형의 앱에 대한 cookie 보안 수준을 강화합니다.

var cookiePolicyOptions = new CookiePolicyOptions
{
    MinimumSameSitePolicy = SameSiteMode.Strict,
};

MinimumSameSitePolicy에 대한 Cookie 정책 미들웨어 설정은 아래 행렬에 따라 CookieAuthenticationOptions 설정의 Cookie.SameSite 설정에 영향을 줄 수 있습니다.

MinimumSameSitePolicy Cookie.SameSite 결과 Cookie.SameSite 설정
SameSiteMode.None SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Lax
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict SameSiteMode.None
SameSiteMode.Lax
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict
SameSiteMode.Strict

사용자 정보를 보유하는 cookie를 만들려면 ClaimsPrincipal을 구성합니다. 사용자 정보는 직렬화되고 cookie에 저장됩니다.

필요한 Claim을 사용하여 ClaimsIdentity를 만들고 SignInAsync를 호출하여 사용자를 로그인합니다.

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.Email),
    new Claim("FullName", user.FullName),
    new Claim(ClaimTypes.Role, "Administrator"),
};

var claimsIdentity = new ClaimsIdentity(
    claims, CookieAuthenticationDefaults.AuthenticationScheme);

var authProperties = new AuthenticationProperties
{
    //AllowRefresh = <bool>,
    // Refreshing the authentication session should be allowed.

    //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
    // The time at which the authentication ticket expires. A 
    // value set here overrides the ExpireTimeSpan option of 
    // CookieAuthenticationOptions set with AddCookie.

    //IsPersistent = true,
    // Whether the authentication session is persisted across 
    // multiple requests. When used with cookies, controls
    // whether the cookie's lifetime is absolute (matching the
    // lifetime of the authentication ticket) or session-based.

    //IssuedUtc = <DateTimeOffset>,
    // The time at which the authentication ticket was issued.

    //RedirectUri = <string>
    // The full path or absolute URI to be used as an http 
    // redirect response value.
};

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme, 
    new ClaimsPrincipal(claimsIdentity), 
    authProperties);

영어 이외의 언어로 번역된 코드 주석을 보려면 이 GitHub 토론 이슈에서 알려주세요.

SignInAsync는 암호화된 cookie를 만들어 현재 응답에 추가합니다. AuthenticationScheme를 지정하지 않은 경우 기본 스키마가 사용됩니다.

RedirectUri는 기본적으로 몇 가지 특정 경로(예: 로그인 경로 및 로그아웃 경로)에서만 사용됩니다. 자세한 내용은 CookieAuthenticationHandler 원본을 참조하세요.

ASP.NET Core의 데이터 보호 시스템은 암호화에 사용됩니다. 여러 머신에서 호스트되는 앱, 앱 간에 부하를 분산하거나 웹 팜을 사용하는 경우 동일한 키 링 및 앱 식별자를 사용하도록 데이터 보호를 구성합니다.

로그아웃

현재 사용자를 로그아웃하고 해당 cookie를 삭제하려면 SignOutAsync를 호출합니다.

await HttpContext.SignOutAsync(
    CookieAuthenticationDefaults.AuthenticationScheme);

CookieAuthenticationDefaults.AuthenticationScheme(또는 "Cookie")가 스키마(예: "ContosoCookie")로 사용되지 않는 경우 인증 공급자를 구성할 때 사용되는 스키마를 제공합니다. 그렇지 않으면 기본 스키마가 사용됩니다.

브라우저가 닫히면 세션 기반 cookie(비영구 cookie)가 자동으로 삭제되지만 개별 탭이 닫힐 때 cookie는 제거되지 않습니다. 탭 또는 브라우저 닫기 이벤트에 대한 알림이 서버에 표시되지 않습니다.

백 엔드 변경 내용에 대응

cookie가 생성되면 cookie는 ID의 단일 원본입니다. 백 엔드 시스템에서 사용자 계정을 사용할 수 없는 경우:

  • 앱의 cookie 인증 시스템은 인증 cookie에 따라 요청을 계속 처리합니다.
  • 인증 cookie가 유효한 한 사용자는 앱에 로그인된 상태를 유지합니다.

ValidatePrincipal 이벤트를 사용하여 cookie ID의 유효성 검사를 가로채고 재정의할 수 있습니다. 모든 요청에서 cookie의 유효성을 검사하면 해지된 사용자가 앱에 액세스하는 위험을 완화할 수 있습니다.

cookie 유효성 검사에 대한 한 가지 접근 방식은 사용자 데이터베이스가 변경되는 시기를 추적하는 것입니다. 사용자의 cookie가 발급된 후 데이터베이스가 변경되지 않은 경우에 해당 cookie가 여전히 유효하면 사용자를 다시 인증할 필요가 없습니다. 샘플 앱에서 데이터베이스는 IUserRepository로 구현되고 LastChanged 값을 저장합니다. 데이터베이스에서 사용자를 업데이트하는 경우 LastChanged 값은 현재 시간으로 설정됩니다.

LastChanged 값에 따라 데이터베이스가 변경될 때 cookie를 무효화하려면 데이터베이스의 현재 LastChanged 값이 포함된 LastChanged 클레임을 사용하여 cookie를 만듭니다.

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, user.Email),
    new Claim("LastChanged", {Database Value})
};

var claimsIdentity = new ClaimsIdentity(
    claims, 
    CookieAuthenticationDefaults.AuthenticationScheme);

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme, 
    new ClaimsPrincipal(claimsIdentity));

ValidatePrincipal 이벤트에 대한 재정의를 구현하려면 CookieAuthenticationEvents에서 파생되는 클래스에서 다음 시그니처를 사용하여 메서드를 작성합니다.

ValidatePrincipal(CookieValidatePrincipalContext)

다음은 CookieAuthenticationEvents의 구현 예제입니다.

using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
    private readonly IUserRepository _userRepository;

    public CustomCookieAuthenticationEvents(IUserRepository userRepository)
    {
        // Get the database from registered DI services.
        _userRepository = userRepository;
    }

    public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
    {
        var userPrincipal = context.Principal;

        // Look for the LastChanged claim.
        var lastChanged = (from c in userPrincipal.Claims
                           where c.Type == "LastChanged"
                           select c.Value).FirstOrDefault();

        if (string.IsNullOrEmpty(lastChanged) ||
            !_userRepository.ValidateLastChanged(lastChanged))
        {
            context.RejectPrincipal();

            await context.HttpContext.SignOutAsync(
                CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

Startup.ConfigureServices 메서드에서 cookie 서비스를 등록하는 동안 이벤트 인스턴스를 등록합니다. CustomCookieAuthenticationEvents 클래스에 대해 범위가 지정된 서비스 등록을 제공합니다.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.EventsType = typeof(CustomCookieAuthenticationEvents);
    });

services.AddScoped<CustomCookieAuthenticationEvents>();

사용자의 이름이 보안에 영향을 주지 않는 결정으로 업데이트되는 상황을 살펴봅시다. 사용자 보안 주체를 비파괴적으로 업데이트하려면 context.ReplacePrincipal를 호출하고 context.ShouldRenew 속성을 true로 설정합니다.

Warning

여기에 설명된 방식은 모든 요청에서 트리거됩니다. 모든 요청에서 모든 사용자에 대한 인증 cookie의 유효성을 검사하면 앱의 성능이 크게 저하될 수 있습니다.

지속성 cookie

cookie가 브라우저 세션에서 지속되도록 할 수 있습니다. 이 지속성은 로그인 시 또는 유사한 메커니즘에서 "Remember Me" 확인란을 통해 명시적 사용자 동의가 있는 경우에만 활성화되어야 합니다.

다음 코드 조각에서는 브라우저 클로저를 통해 유지되는 ID와 해당 cookie를 만듭니다. 이전에 구성된 슬라이딩 만료 설정은 모두 적용됩니다. 브라우저가 닫혀 있는 동안 cookie가 만료된 경우 브라우저가 다시 시작되면 cookie를 지웁니다.

AuthenticationProperties에서 IsPersistenttrue로 설정합니다.

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true
    });

절대 만료 시간은 ExpiresUtc로 설정할 수 있습니다. 영구 cookie를 만들려면 IsPersistent도 설정해야 합니다. 그렇지 않으면 cookie는 세션 기반 수명을 통해 만들어지고 해당 수명이 있는 인증 티켓 전후에 만료될 수 있습니다. ExpiresUtc가 설정되면 CookieAuthenticationOptionsExpireTimeSpan 옵션 값이 재정의됩니다(설정된 경우).

다음 코드 조각에서는 20분 동안 지속되는 ID와 해당 cookie를 만듭니다. 이렇게 하면 이전에 구성된 슬라이딩 만료 설정이 무시됩니다.

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true,
        ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
    });