首页 > 解决方案 > 身份服务器4,本地登录/天蓝色的不同行为

问题描述

环境:localhost / azure,.netcore 3.1 mvc identityserver4 + mvc api 客户端。

当我在本地运行我的应用程序时,登录/注销工作正常,有: - identityserver4 mvc .netcore 3.1 - client mvc api .netcore 3.1

我可以随心所欲地登录/注销,登录始终重定向到 identityserver4 登录并且登录有效。

当与 Azure 上托管的 identityserver4 相同的应用程序第一次登录正确地重定向到 azure identityserver4,并且登录工作正常。然后在注销后(似乎已删除 cockies),当我再次尝试登录时,重定向到登录页面不起作用,并且存在“隐式”登录并直接重定向到网站主页。

客户端 mvc api 配置如下:

{
  "ClientId": "IdentityServer.WebApi",
  "ClientSecret": "IdentityServer.WebApi",
  "AllowedGrantTypes": "GrantTypes.CodeAndClientCredentials",
  "RedirectUris": [
    "https://localhost:44372/signin-oidc",
    "https://localhost:5001/signin-oidc",
    "https://192.168.1.7:44372/signin-oidc",
    "https://mogui:44372/signin-oidc"
  ],
  "PostLogoutRedirectUris": [
    "https://localhost:44372/signout-callback-oidc",
    "https://localhost:5001/signout-callback-oidc",
    "https://192.168.1.7:44372/signout-callback-oidc",
    "https://mogui:44372/signout-callback-oidc"
  ],
  "AllowedScopes": [
    "openid",
    "profile"
  ],
  "RequireConsent": true,
  "RequirePkce": true,
  "AllowOfflineAccess": true
},

本地/天蓝色的 identityserver4 在其 Startup 类上有这种代码:

public void ConfigureServices(IServiceCollection services)
{
    try
    {
        telemetryClient.TrackTrace("============== Startup ConfigureServices ============== ");

        // uncomment, if you wan to add an MVC-based UI
        services.AddControllersWithViews();
        //services.AddMvc();

        string connectionString = Configuration.GetConnectionString("IdentityDbContextConnection");
        //const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.Quickstart.EntityFramework-3.0.102;trusted_connection=yes;";
        var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

        services.AddDbContext<IdentityServer.Models.IdentityDbContext>(options =>
            options.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly))
        );

        services.AddIdentity<ApplicationUser, IdentityRole>(options =>
        {
            options.SignIn.RequireConfirmedEmail = true;
        })
        .AddEntityFrameworkStores<IdentityServer.Models.IdentityDbContext>()
        .AddDefaultTokenProviders();

        services.AddMvc(options =>
        {
            options.EnableEndpointRouting = false;
        })
        .SetCompatibilityVersion(CompatibilityVersion.Latest);

        var builder = services.AddIdentityServer(options =>
        {
            options.Events.RaiseErrorEvents = true;
            options.Events.RaiseInformationEvents = true;
            options.Events.RaiseFailureEvents = true;
            options.Events.RaiseSuccessEvents = true;
            options.UserInteraction.LoginUrl = "/Account/Login";
            options.UserInteraction.LogoutUrl = "/Account/Logout";
            options.Authentication = new AuthenticationOptions()
            {
                CookieLifetime = TimeSpan.FromHours(10), // ID server cookie timeout set to 10 hours
                CookieSlidingExpiration = true
            };
        })
        .AddSigningCredential(X509.GetCertificate("B22BBE7C991CEF13F470481A4042D1E091967FCC"))   // signing.crt thumbprint
        .AddValidationKey(X509.GetCertificate("321ABA505F6FCDDD00AA5EC2BD307F0C9002F9A8"))       // validation.crt thumbprint
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
        })
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
            options.EnableTokenCleanup = true;
        })
        .AddAspNetIdentity<ApplicationUser>();

        services.AddAuthentication()
        .AddGoogle("Google", options =>
        {
            options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

            options.ClientId = "174637674775-7bgu471gtme25sr5iagq5agq6riottek.apps.googleusercontent.com";
            options.ClientSecret = "V_UsR825ZWxCB9i2xrN-u1Kj";
        });

        services.AddTransient<IEmailSender, IdentityEmailSender>();

        services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader()));

        services.Configure<CookiePolicyOptions>(options =>
        {
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.Strict;
        });

        services.AddScoped<IProfileService, ProfileService>();

        telemetryClient.TrackTrace("============== Startup ConfigureServices finish OK ============== ");

    }
    catch (Exception e)
    {
        telemetryClient.TrackTrace("Exception general in ConfigureServices");
        telemetryClient.TrackException(e);
        throw;
    }
}

和这个 :

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    try
    {
        telemetryClient.TrackTrace("============== Startup Configure ============== ");

        InitializeDatabase(app);

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseStaticFiles();
        app.UseCors("AllowAll");
        app.UseRouting();

        app.UseIdentityServer();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
        });

        app.UseMvcWithDefaultRoute();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                   name: "default",
                   template: "{controller=Home}/{action=Index}/{id?}");
        });

        telemetryClient.TrackTrace("============== Startup Configure finish OK============== ");
    }
    catch (Exception e)
    {
        telemetryClient.TrackTrace("Exception general in Configure");
        telemetryClient.TrackException(e);
        throw;
    }
}

所以问题在于

identityserver4 localhost 登录/注销工作找到

托管在 azure 上的 idnetityserver4 登录被跳过并直接进入主页(用户通过以前的登录进行身份验证)。

抱歉有点长,我在stackoverflow或其他地方没有看到这个确切的问题。

提前谢谢!

标签: azureidentityserver4

解决方案


你说得对,根据我看到的不同帖子,我们可以做这样的事情:

- first of all, we have to put FrontChannelLogoutUri parameter 

(它应该是一个由 identityserver4 调用的 mvc 客户端控制器/动作,在我们的例子中应该类似于https://localhost:999/Account/FrontChannelLogout)用于客户端 mvc 应用程序,通常它放在 Config.cs 中并添加这个客户端 Mvc 的参数(使用 RedirectUris、PostLogoutRedirectUris、...)

- on the client mvc, in an account controller (for instance) where is managed the login , 

我们可以添加/修改注销管理:

[Authorize]
public async Task<IActionResult> Logout()
{
    var client = new HttpClient();

    var disco = await client.GetDiscoveryDocumentAsync($"https://{Startup.Configuration["Auth0:Domain"]}");

    return Redirect(disco.EndSessionEndpoint);
}

public async Task<IActionResult> FrontChannelLogout(string sid)
{
    if (User.Identity.IsAuthenticated)
    {
        var currentSid = User.FindFirst("sid")?.Value ?? "";
        if (string.Equals(currentSid, sid, StringComparison.Ordinal))
        {
            await HttpContext.SignOutAsync("oidc");
            await HttpContext.SignOutAsync("Identity.Application");
            await _signInManager.Context.SignOutAsync("_af");
            await _signInManager.Context.SignOutAsync("idsrv.session");
            await _signInManager.Context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }

    return NoContent();
}

在 identityserver4 方面:

在 QuickStart Account Controller 中,我们必须更新 BuildLoggedOutViewModelAsync 方法:

    private async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
    {
        // get context information (client name, post logout redirect URI and iframe for federated signout)
        var logout = await _interaction.GetLogoutContextAsync(logoutId);

        var client = await _clientStore.FindEnabledClientByIdAsync(logout.ClientIds.First());

        if (!string.IsNullOrEmpty(client.FrontChannelLogoutUri))
        {
            //var pos = GetNthIndex(client.FrontChannelLogoutUri, '/', 3);
            //logout.PostLogoutRedirectUri = client.FrontChannelLogoutUri.Substring(0, Math.Min(client.FrontChannelLogoutUri.Length, pos));
            // Here TODO =====> get the real PostLogoutRedirectUri, it should be a controller/action url on the client mvc side and put it in **logout.PostLogoutRedirectUri**
        }

        var vm = new LoggedOutViewModel
        {
            AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut,
            PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
            ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName,
            SignOutIframeUrl = logout?.SignOutIFrameUrl,
            LogoutId = logoutId
        };

        if (User?.Identity.IsAuthenticated == true)
        {
            var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
            if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider)
            {
                var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp);
                if (providerSupportsSignout)
                {
                    if (vm.LogoutId == null)
                    {
                        // if there's no current logout context, we need to create one
                        // this captures necessary info from the current logged in user
                        // before we signout and redirect away to the external IdP for signout
                        vm.LogoutId = await _interaction.CreateLogoutContextAsync();
                    }

                    vm.ExternalAuthenticationScheme = idp;
                }
            }
        }

        return vm;
    }

====> 显然 _interaction.GetLogoutContextAsync(logoutId) 永远不会返回 PostLogoutRedirectUri,即使它已为 mvc 客户端(在 Config.cs 中)设置。

====> 通过在 identityServer4 端填写此参数 logout.PostLogoutRedirectUri ,它将注销重定向到客户端应用程序。

这就是我能说的,我不知道注销重定向到客户端应用程序是否是“标准”行为,不知道它是否是在identityserver4中计划的。

一些链接:

https://andersonnjen.com/2019/03/22/identityserver4-global-logout/ 从身份服务器注销后如何将用户重定向到客户端应用程序?

谢谢!


推荐阅读