3

J'ai un Identity Server (ASP.NET Core 2 avec Identity Server 4 2.0.0) configuré pour utiliser Kestrel et IISIntegration, avec l'authentification anonyme et Windows activée sur launchSettings.json. J'ai également configuré IISOptions comme ceci:Windows L'authentification n'accepte pas les informations d'identification

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(); 

Et je ce client (aussi ASP.NET Core 2 avec Windows et l'authentification anonyme est activée, en cours d'exécution sur Kestrel avec IISIntegration)

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(); 

Le serveur d'identité fonctionne sur http://localhost:5000 et le client sur http://localhost:2040. Lorsque je démarre le client, il présente correctement l'écran de connexion d'Identity Server, mais en cliquant sur l'authentification Windows, je ne demande que des informations d'identification. J'ai regardé la fenêtre de sortie pour les deux applications et il n'y a aucune exception soulevant de chaque côté. J'ai essayé de déployer Identity Server sur un serveur IIS (avec l'authentification Windows activée et son pool s'exécutant sous NETWORK SERVICE) et le même comportement est reproduit.

+0

@CodeCaster vous avez essayé de placer 'services.AddDeveloperSigningCredential();' pour le dépannage? – Kostya

+0

@KostyaK Si vous faites référence à 'services.AddIdentityServer(). AddDeveloperSigningCredential()', oui, c'est déjà là –

+0

@CodeCaster Essayez d'exécuter l'exemple officiel à partir d'ici https://github.com/IdentityServer/IdentityServer4.Samples/tree/ release/Quickstarts/6_AspNetIdentity/src pour voir si cela fonctionne dans votre environnement. De cette façon, vous pouvez affiner la recherche d'un problème. – Kostya

Répondre

3

J'ai finalement compris. J'ai suivi le démarrage rapide Combined_AspNetIdentity_and_EntityFrameworkStorage qui ne prend pas en charge l'authentification Windows. La copie sur le code approprié à partir du quickstart 4_ImplicitFlowAuthenticationWithExternal a résolu le problème.

Pour simplifier, voici le code de démarrage rapide qui permet l'authentification Windows, modifié pour utiliser Identity comme un magasin d'utilisateur:

[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("~/"); 
}