重新授权持久登录(MVC客户端)而不触发登录UI

时间:2018-01-08 18:34:37

标签: asp.net-core-2.0 identityserver4

我试图找出服务器端客户端(MVC / ASP.NET Core 2)如何查询IdentityServer4以检索在某些先前会话中创建的持久登录的各种声明范围,而不提示登录如果持久登录无效(用户不活动,cookie过期等)。

我们正在使用隐式流程与第三方身份验证(Google,FB等),但我们已将Cookie上的会话持续时间更改为IdentityServer' s {{更加用户友好的30天到期时间1}}。

ExternalLoginCallback上访问声明(我们使用ASP.NET身份)在建立登录的会话期间运行良好。在稍后的会话中,导航到具有HttpContext.User属性的客户端资源也是有效的:如果用户先前已登录,则他们透明地获得对资源的访问权限,声明填充等等。如果不是,则他们'重新提示登录,这可以响应用户启动的操作。

但是,我们要求客户端登录页面根据用户是匿名还是经过身份验证来更改内容。一个简单的例子就是" Register"和"登录"匿名用户的链接,但"帐户"和"退出"经过身份验证的用户的链接

因此有理由检索声明并启动持久登录,如果它有效,但如果无效则不执行任何操作(无登录提示):我们不希望登录页面强制每个匿名用户到登录屏幕。

关于我们在管道两端的设置没什么好说的。客户端:

[Authorize]

IdentityServer客户端资源定义:

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")

.AddOpenIdConnect("oidc", options =>
{
    options.SignInScheme = "Cookies";
    options.Authority = "https://localhost:5000";
    options.RequireHttpsMetadata = true;
    options.ClientId = "example.com.webserver";
    options.ClientSecret = "examplesecret";
    options.ResponseType = "id_token";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.Scope.Add("example.com.identity");
});

客户端运行故意登录(用户点击"登录"链接),如下所示:

new Client
{
    ClientId = "example.com.webserver",
    ClientName = "example.com",
    ClientUri = "https://localhost:5002",
    AllowedGrantTypes = GrantTypes.Implicit,
    ClientSecrets = {new Secret("examplesecret".Sha256())},
    RequireConsent = false,
    AllowRememberConsent = true,
    AllowOfflineAccess = true,
    RedirectUris = { "https://localhost:5002/signin-oidc"},
    PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc"},
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityServerConstants.StandardScopes.Email,
        IdentityServerConstants.StandardScopes.Phone,
        IdentityServerConstants.StandardScopes.Address,
        "example.com.identity"
    }
}

2 个答案:

答案 0 :(得分:2)

解决方案是在安装程序中添加第二个OIDC身份验证流,拦截重定向以将Prompt选项更改为none,以便不显示登录提示,拦截生成的login_required错误消息,并在着陆页的PageModel OnGet处理程序中触发该流(客户端应用使用RazorPages)。

一个警告是处理程序必须设置标志,以便仅尝试一次,以便它可以检测页面是第一次被命中,还是作为登录尝试的返回行程。这是通过将一个值放入Razor TempData来实现的,这只是一个基于cookie的名称 - 值对的存储桶。

添加到Setup.cs

.AddOpenIdConnect("persistent", options =>
{
    options.CallbackPath = "/signin-persistent";
    options.Events = new OpenIdConnectEvents
    {
        OnRedirectToIdentityProvider = context =>
        {
            context.ProtocolMessage.Prompt = "none";
            return Task.FromResult<object>(null);
        },

        OnMessageReceived = context => {
            if(string.Equals(context.ProtocolMessage.Error, "login_required", StringComparison.Ordinal))
            {
                context.HandleResponse();
                context.Response.Redirect("/");
            }
            return Task.FromResult<object>(null);
        }
    };

    options.SignInScheme = "Cookies";
    options.Authority = "https://localhost:5000";
    options.RequireHttpsMetadata = true;
    options.ClientId = "example.com.webserver";
    options.ClientSecret = "examplesecret";
    options.ResponseType = "code";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.Scope.Add("example.com.identity");
})

登陆页面(Index.cshtml.cs)

public class IndexModel : PageModel
{
    private bool PersistentLoginAttempted = false;
    private const string PersistentLoginFlag = "persistent_login_attempt";

    public IActionResult OnGet()
    {
        // Always clean up an existing flag.
        bool FlagFound = false;
        if(!String.IsNullOrEmpty(TempData[PersistentLoginFlag] as string))
        {
            FlagFound = true;
            TempData.Remove(PersistentLoginFlag);
        }

        // Try to refresh a persistent login the first time an anonymous user hits the index page in this session
        if(!User.Identity.IsAuthenticated && !PersistentLoginAttempted)
        {
            PersistentLoginAttempted = true;
            // If there was a flag, this is the return-trip from a failed persistent login attempt.
            if(!FlagFound)
            {
                // No flag was found. Create it, then begin the OIDC challenge flow.
                TempData[PersistentLoginFlag] = PersistentLoginFlag;
                return Challenge("persistent");
            }
        }
        return Page();
    }
}

答案 1 :(得分:-1)

为什么不仅仅使身份验证cookie永久化?这是另一种实现方法...然后,您可以对照身份验证服务器检查服务器上的身份验证。

在客户端AddOpenIdConnect上:

                options.Events = new OpenIdConnectEvents {
                    OnRedirectToIdentityProvider = context => {
                        context.Properties.RedirectUri = context.Request.Path;
                        return Task.FromResult(0);
                    },
                    OnTicketReceived = context =>
                    {
                        context.Properties.IsPersistent = true;
                        context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(15);
                        context.Properties.AllowRefresh = true;

                        return Task.FromResult(0);
                    }
                };