3

Kestrel과 IISIntegration을 사용하도록 구성된 Identity Server (Identity Server 4 2.0.0이 적용된 ASP.NET 코어 2)가 있으며, launchSettings.json에서 익명 인증과 Windows 인증을 모두 사용할 수 있습니다. 나는 또한이 같은 IISOptions 구성 :Windows 인증에서 자격 증명을 허용하지 않습니다.

services.Configure<IISOptions>(iis => 
{ 
    iis.AutomaticAuthentication = false; 
    iis.AuthenticationDisplayName = "Windows"; 
}); 

services.AddAuthentication(); 
services.AddCors() 
     .AddMvc(); 
services.AddIdentityServer(); // with AspNetIdentity configured 

app.UseAuthentication() 
    .UseIdentityServer() 
    .UseStaticFiles() 
    .UseCors(options => options.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()) 
    .UseMvcWithDefaultRoute(); 

그리고

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) 
    .AddOpenIdConnect(config => 
    { 
     config.Authority = "http://localhost:5000"; 
     config.RequireHttpsMetadata = false; 
     config.ClientId = "MyClientId"; 
     config.ClientSecret = "MyClientSecret"; 
     config.SaveTokens = true; 
     config.GetClaimsFromUserInfoEndpoint = true; 
    }); 

services.AddMvc(); 

신원 서버 (IISIntegration와 황조롱이에서 실행, Windows 및 익명 인증을 사용 모두 또한 ASP.NET 코어 2)이 클라이언트가 http://localhost:5000에서 실행 중이고 클라이언트가 http://localhost:2040에서 실행 중입니다.

클라이언트를 시작하면 Identity Server의 로그인 화면이 올바르게 표시되지만 Windows 인증을 클릭하면 자격 증명을 묻는 메시지 만 계속 표시됩니다. 두 응용 프로그램의 Output Window를 살펴 보았으므로 양쪽에서 예외가 발생하지 않습니다. Identity Server를 IIS 서버 (Windows 인증이 활성화되고 해당 풀이 네트워크 서비스에서 실행 됨)에 배포하려고 시도했지만 동일한 동작이 재현되었습니다.

+0

당신이'services.AddDeveloperSigningCredential()을 설정 봤어 @CodeCaster :

단순화하기 위해, 여기에 사용자 저장소로 Identity를 사용하도록 수정 Windows 인증을 허용 퀵 스타트 코드는 무엇입니까? – Kostya

+0

@KostyaK 만약'services.AddIdentityServer(). AddDeveloperSigningCredential()'을 참조한다면, 이미 그곳에 있습니다. –

+0

@CodeCaster 공식 샘플은 https://github.com/IdentityServer/IdentityServer4.Samples/tree/에서 실행 해보십시오. release/Quickstarts/6_AspNetIdentity/src를 실행하여 사용자 환경에서 작동하는지 확인하십시오. 이렇게하면 문제의 검색 범위를 좁힐 수 있습니다. – Kostya

답변

3

나는 그것을 마침내 발견했습니다. Windows 인증을 지원하지 않는 Combined_AspNetIdentity_and_EntityFrameworkStorage 빠른 시작을 따랐습니다. 4_ImplicitFlowAuthenticationWithExternal 빠른 코드에서 관련 코드를 복사하면 문제가 해결됩니다. ;`문제 해결

[HttpPost] 
[AllowAnonymous] 
[ValidateAntiForgeryToken] 
public async Task<IActionResult> ExternalLogin(string provider, string returnUrl = null) 
{ 
    var props = new AuthenticationProperties() 
    { 
     RedirectUri = Url.Action("ExternalLoginCallback"), 
     Items = 
     { 
      { "returnUrl", returnUrl }, 
      { "scheme", AccountOptions.WindowsAuthenticationSchemeName } 
     } 
    }; 

    // I only care about Windows as an external provider 
    var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName); 
    if (result?.Principal is WindowsPrincipal wp) 
    { 
     var id = new ClaimsIdentity(provider); 
     id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name)); 
     id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name)); 

     await HttpContext.SignInAsync(
      IdentityServerConstants.ExternalCookieAuthenticationScheme, 
      new ClaimsPrincipal(id), 
      props); 
     return Redirect(props.RedirectUri); 
    } 
    else 
    { 
     return Challenge(AccountOptions.WindowsAuthenticationSchemeName); 
    } 
} 

[HttpGet] 
[AllowAnonymous] 
public async Task<IActionResult> ExternalLoginCallback() 
{ 
    var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); 
    if (result?.Succeeded != true) 
    { 
     throw new Exception("External authentication error"); 
    } 

    var externalUser = result.Principal; 
    var claims = externalUser.Claims.ToList(); 

    var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject); 
    if (userIdClaim == null) 
    { 
     userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier); 
    } 

    if (userIdClaim == null) 
    { 
     throw new Exception("Unknown userid"); 
    } 

    claims.Remove(userIdClaim); 
    string provider = result.Properties.Items["scheme"]; 
    string userId = userIdClaim.Value; 

    var additionalClaims = new List<Claim>(); 

    // I changed this to use Identity as a user store 
    var user = await userManager.FindByNameAsync(userId); 
    if (user == null) 
    { 
     user = new ApplicationUser 
     { 
      UserName = userId 
     }; 

     var creationResult = await userManager.CreateAsync(user); 

     if (!creationResult.Succeeded) 
     { 
      throw new Exception($"Could not create new user: {creationResult.Errors.FirstOrDefault()?.Description}"); 
     } 
    } 
    else 
    { 
     additionalClaims.AddRange(await userManager.GetClaimsAsync(user)); 
    } 

    var sid = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); 
    if (sid != null) 
    { 
     additionalClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); 
    } 

    AuthenticationProperties props = null; 
    string id_token = result.Properties.GetTokenValue("id_token"); 
    if (id_token != null) 
    { 
     props = new AuthenticationProperties(); 
     props.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); 
    } 

    await HttpContext.SignInAsync(user.Id, user.UserName, provider, props, additionalClaims.ToArray()); 

    await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); 

    string returnUrl = result.Properties.Items["returnUrl"]; 
    if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl)) 
    { 
     return Redirect(returnUrl); 
    } 

    return Redirect("~/"); 
}