✨ ASP.NET Core IdentityOptions 개요
ASP.NET Core의 IdentityOptions는 시스템의 설정을 한 곳에 몰아서 관리하는 클래스입니다. (중앙 집중식 구성)
이를 통해 패스워드 정책, 사용자 설정, 로그인 정책, 잠금 정책 등을 세밀하게 제어할 수 있습니다.
🔥 주요 구성 요소

1️⃣ Password 옵션 (PasswordOptions)
패스워드 복잡성 요구사항을 설정합니다.
// Program.cs 또는 Startup.cs
// 개발 환경용 - 간단한 패스워드
public void ConfigureDevelopmentPassword(IdentityOptions options)
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 4;
}
// 프로덕션 환경용 - 강력한 패스워드
public void ConfigureProductionPassword(IdentityOptions options)
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequiredLength = 12;
options.Password.RequiredUniqueChars = 4;
}
2️⃣ Lockout 옵션 (LockoutOptions)
계정 잠금 정책을 설정합니다.
public void ConfigureProgressiveLockout(IdentityOptions options)
{
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1); // 1분부터 시작
options.Lockout.MaxFailedAccessAttempts = 3; // 3회 실패시 잠금
options.Lockout.AllowedForNewUsers = true;
}
// 커스텀 잠금 시간 증가 로직 (서비스에서 구현)
public class CustomLockoutService
{
public TimeSpan CalculateLockoutDuration(int failedAttempts)
{
return failedAttempts switch
{
<= 3 => TimeSpan.FromMinutes(5),
<= 6 => TimeSpan.FromMinutes(15),
<= 10 => TimeSpan.FromHours(1),
_ => TimeSpan.FromHours(24)
};
}
}
public void ConfigureProgressiveLockout(IdentityOptions options)
{
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1); // 1분부터 시작
options.Lockout.MaxFailedAccessAttempts = 3; // 3회 실패시 잠금
options.Lockout.AllowedForNewUsers = true;
}
// 커스텀 잠금 시간 증가 로직 (서비스에서 구현)
public class CustomLockoutService
{
public TimeSpan CalculateLockoutDuration(int failedAttempts)
{
return failedAttempts switch
{
<= 3 => TimeSpan.FromMinutes(5),
<= 6 => TimeSpan.FromMinutes(15),
<= 10 => TimeSpan.FromHours(1),
_ => TimeSpan.FromHours(24)
};
}
}
3️⃣ SignIn 옵션 (SignInOptions)
로그인 관련 설정을 구성합니다.
// 이메일 인증 필수 설정
public void ConfigureEmailConfirmation(IdentityOptions options)
{
options.SignIn.RequireConfirmedEmail = true;
}
// 회원가입 컨트롤러
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// 이메일 인증 토큰 생성
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var confirmationLink = Url.Action("ConfirmEmail", "Account",
new { userId = user.Id, token = token }, Request.Scheme);
// 이메일 발송
await _emailSender.SendEmailAsync(model.Email, "이메일 인증",
$"다음 링크를 클릭하여 계정을 활성화하세요: {confirmationLink}");
return View("EmailConfirmationSent");
}
return View(model);
}
// 이메일 인증 필수 설정
public void ConfigureEmailConfirmation(IdentityOptions options)
{
options.SignIn.RequireConfirmedEmail = true;
}
// 회원가입 컨트롤러
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// 이메일 인증 토큰 생성
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var confirmationLink = Url.Action("ConfirmEmail", "Account",
new { userId = user.Id, token = token }, Request.Scheme);
// 이메일 발송
await _emailSender.SendEmailAsync(model.Email, "이메일 인증",
$"다음 링크를 클릭하여 계정을 활성화하세요: {confirmationLink}");
return View("EmailConfirmationSent");
}
return View(model);
}
4️⃣ User 옵션 (UserOptions)
사용자 계정 관련 설정을 구성합니다.
builder.Services.Configure<IdentityOptions>(options =>
{
// 사용자 설정
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = true; // 중복 이메일 방지
});
사용자명 정책 예제:
public void ConfigureUserPolicies(IdentityOptions options)
{
// 한국어 사용자명 허용
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+가-힣";
// 이메일을 고유하게 설정
options.User.RequireUniqueEmail = true;
}
// 커스텀 사용자명 검증
public class CustomUserValidator<TUser> : IUserValidator<TUser>
where TUser : class
{
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
{
var userName = manager.GetUserNameAsync(user).Result;
// 사용자명이 숫자로만 구성되면 안됨
if (userName.All(char.IsDigit))
{
return Task.FromResult(
IdentityResult.Failed(new IdentityError
{
Code = "NumericUserName",
Description = "사용자명은 숫자로만 구성될 수 없습니다."
}));
}
return Task.FromResult(IdentityResult.Success);
}
}
5️⃣ Stores 옵션 (StoreOptions)
사용자 계정 관련 설정을 구성합니다.
builder.Services.Configure<IdentityOptions>(options =>
{
// 저장소 설정
options.Stores.MaxLengthForKeys = 128; // 키 최대 길이
options.Stores.ProtectPersonalData = false; // 개인정보 암호화 여부
});
🐱 실제 활용 시나리오
1️⃣ 다단계 인증 설정
public void ConfigureTwoFactorAuthentication(IdentityOptions options)
{
options.SignIn.RequireConfirmedPhoneNumber = true; // 전화번호 인증 필수
options.Tokens.AuthenticatorTokenProvider = TokenOptions.DefaultAuthenticatorProvider;
}
// 2FA 활성화 코드
[HttpPost]
public async Task<IActionResult> EnableTwoFactorAuthentication()
{
var user = await _userManager.GetUserAsync(User);
await _userManager.SetTwoFactorEnabledAsync(user, true);
return RedirectToAction("ShowRecoveryCodes");
}
2️⃣ 소셜 로그인과 연계
public void ConfigureExternalLogins(IdentityOptions options)
{
// 외부 로그인 시에는 이메일 인증 건너뛰기
options.SignIn.RequireConfirmedEmail = false;
// 하지만 고유 이메일은 필수
options.User.RequireUniqueEmail = true;
}
// 외부 로그인 처리
[HttpPost]
public IActionResult ExternalLogin(string provider)
{
var redirectUrl = Url.Action("ExternalLoginCallback");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
3️⃣ 커스텀 검증 규칙 적용
// 커스텀 패스워드 검증기
public class CustomPasswordValidator : IPasswordValidator<ApplicationUser>
{
public Task<IdentityResult> ValidateAsync(UserManager<ApplicationUser> manager,
ApplicationUser user, string password)
{
// 사용자명과 패스워드가 너무 유사하면 안됨
if (password.Contains(user.UserName, StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(IdentityResult.Failed(new IdentityError
{
Code = "PasswordContainsUserName",
Description = "패스워드에 사용자명이 포함될 수 없습니다."
}));
}
return Task.FromResult(IdentityResult.Success);
}
}
// 서비스 등록
builder.Services.AddTransient<IPasswordValidator<ApplicationUser>, CustomPasswordValidator>();
❇️ 참고 사항
1️⃣ 모니터링 및 로깅
보안 이벤트 로깅
public class SecurityEventLogger
{
private readonly ILogger<SecurityEventLogger> _logger;
public SecurityEventLogger(ILogger<SecurityEventLogger> logger)
{
_logger = logger;
}
public void LogFailedLogin(string userName, string ipAddress)
{
_logger.LogWarning("Failed login attempt for user {UserName} from IP {IpAddress}",
userName, ipAddress);
}
public void LogAccountLocked(string userName)
{
_logger.LogWarning("Account locked for user {UserName}", userName);
}
}
2️⃣ 환경별 설정 관리
appsettings.json을 활용한 설정
{
"Identity": {
"Password": {
"RequiredLength": 8,
"RequireDigit": true,
"RequireUppercase": true,
"RequireNonAlphanumeric": false
},
"Lockout": {
"MaxFailedAccessAttempts": 5,
"DefaultLockoutTimeSpanMinutes": 15
},
"SignIn": {
"RequireConfirmedEmail": true
}
}
}
// 설정 바인딩
public class IdentityConfig
{
public PasswordConfig Password { get; set; }
public LockoutConfig Lockout { get; set; }
public SignInConfig SignIn { get; set; }
}
// Program.cs에서 적용
var identityConfig = builder.Configuration.GetSection("Identity").Get<IdentityConfig>();
builder.Services.Configure<IdentityOptions>(options =>
{
options.Password.RequiredLength = identityConfig.Password.RequiredLength;
options.Password.RequireDigit = identityConfig.Password.RequireDigit;
// ... 기타 설정
});


