web-dev-qa-db-fra.com

Pourquoi l'utilisateur final doit-il se déconnecter deux fois?

J'essaie de faire fonctionner IdentityServer4 dans une nouvelle application .NET Core 2.1 (cela fonctionne parfaitement dans une application .NET Core 2.0). J'ai essayé ce qui suit:

1) Téléchargez ce projet, qui est l'application IdentityServer4: https://github.com/ghstahl/IdentityServer4-Asp.Net-2.1-Identity-Examples/tree/e0aeeff7e078aa082c8e16029dd2c220acc77

2) Téléchargez ce projet, à savoir l'application MVC utilisant l'application Identity Server4: https://github.com/IdentityServer/IdentityServer4.Samples/tree/dev/Quickstarts/6_AspNetIdentity/src/MvcClient .

3) Ajoutez les deux projets à la même solution. Le projet MVC utilise le projet IdentityServer pour l'authentification. autorisation etc.

Je devais apporter les modifications suivantes:

1) Modifiez le démarrage figurant dans l'application IdentityServer (AddIdentityServer accepte désormais un argument):

services.AddIdentityServer(options =>
{
    options.UserInteraction.LoginUrl = "/Identity/Account/Login";
    options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
})

2) Configurez l'application IdentityServer pour qu'elle écoute sur le port 5000 et désactive SSL sur le serveur d'identité.

Tout fonctionne normalement, à l'exception de la fonction de déconnexion. Lorsque je clique sur me déconnecter dans l'application MVC; le code suivant est appelé dans l'application MVC:

public async Task Logout() 
{ 
    await HttpContext.SignOutAsync("Cookies"); 
    await HttpContext.SignOutAsync("oidc"); 
} 

L'utilisateur est ensuite redirigé vers Logout.cshtml dans l'application IdentityServer. Cependant, ils doivent cliquer de nouveau sur la déconnexion (sur l’application IdentityServer) pour se déconnecter, c’est-à-dire qu’ils cliquent sur la déconnexion dans l’application MVC (deuxième point), puis se déconnecter dans IdentityServer (premier point).

Pourquoi l'utilisateur final doit-il se déconnecter deux fois?

7
w0051977

Dans la page Account/Logout, qui se trouve sous Areas/Identity/Account/Logout.cshtml.cs dans votre code d'identité ASP.NET Core échafaudé, il existe un gestionnaire OnGet qui ressemble à ceci:

public void OnGet() { }

Dans la mesure où il utilise ASP.NET Core Razor Pages, il ne restitue que le rendu de la page Logout.cshtml correspondante. Dans votre exemple, lorsque vous appuyez sur Logout dans l'application MVC, celui-ci efface ses propres cookies et vous transmet ensuite à l'application IS4 (la OnGet, en particulier). Comme ce gestionnaire OnGet est vide, il ne fait rien et ne vous déconnecte certainement pas de l'application IS4.

Si vous regardez le gestionnaire OnPost à l'intérieur de Logout.cshtml.cs, vous verrez qu'il ressemble à ceci:

public async Task<IActionResult> OnPost(string returnUrl = null)
{
    await _signInManager.SignOutAsync();
    // ...
}

Cet appel à SignOutAsync fait exactement ce qu'il suggère: il vous déconnecte de IS4 lui-même. Toutefois, dans votre flux de travail actuel, ce gestionnaire OnPost n'est pas appelé. Le gestionnaire OnGet est appelé indirectement lorsque vous utilisez Logout dans l'application MVC, comme je l'ai déjà mentionné.

Maintenant, si vous regardez l'implémentation contrôleur/action de la déconnexion IS4 dans le projet Quickstart.UI , vous remarquerez qu'il transmet essentiellement la demande GET à la demande POST. Voici le code, avec les commentaires supprimés:

[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
    var vm = await BuildLogoutViewModelAsync(logoutId);

    if (vm.ShowLogoutPrompt == false)
        return await Logout(vm);

    return View(vm);
}

Lors de la déconnexion, un paramètre détermine si l'utilisateur doit d'abord ou non être invité à confirmer s'il souhaite ou non se déconnecter. C'est principalement ce dont s'occupe ce code: il le transmet directement au gestionnaire de demandes POST si l'invite n'est pas requise. Voici un extrait du code de la variable POST:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
    var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);

    if (User?.Identity.IsAuthenticated == true)
    {
        await HttpContext.SignOutAsync();

        // ...
    }

    // ...

    return View("LoggedOut", vm);
}

La ligne importante ici est l'appel à HttpContext.SignOutAsync - ceci finit par supprimer le cookie utilisé par IS4 pour vous garder connecté. Une fois que cela a été supprimé, vous êtes déconnecté de IS4. En fin de compte, c’est ce qui manque à votre implémentation actuelle.

Au niveau le plus simple, vous pouvez résoudre votre problème en mettant à jour votre OnGet pour qu’elle ressemble à ceci:

public async Task<IActionResult> OnGet()
{
    if (User?.Identity.IsAuthenticated == true)
    {
        await _signInManager.SignOutAsync();          
        return RedirectToPage(); // A redirect ensures that the cookies has gone.
    }

    return Page();
}

Cela ne prend pas en charge l'option ShowLogoutPrompt que j'ai détaillée ci-dessus, il suffit simplement de garder cette réponse un peu plus courte. En dehors de cela, c'est juste utiliser _signInManager pour faire la déconnexion, étant donné que vous êtes dans le monde de l'identité ASP.NET Core.

Je vous encourage à explorer le code source complet de Quickstart.UI implementation afin de prendre en charge ShowLogoutPrompt, returnUrl, etc. - Je ne pourrais peut-être pas le faire ici sans écrire un livre.

3
Kirk Larkin

La fonctionnalité de déconnexion simple est possible comme suit:

        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;
        private readonly IIdentityServerInteractionService _interaction;
        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger,
            IIdentityServerInteractionService interaction)
        {
            _signInManager = signInManager;
            _logger = logger;
            _interaction = interaction;
        }

        public async Task<IActionResult> OnGet(string logoutId)
        {
            return await OnPost(logoutId);
        }

        public async Task<IActionResult> OnPost(string logoutId)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            var r = await _interaction.GetLogoutContextAsync(logoutId);
            if (r.PostLogoutRedirectUri == null)
            {
                return Redirect("/");
            }
            return Redirect(r.PostLogoutRedirectUri);
        }
1
adem caglin