使用IdentityServer4从单个客户端接受Bearer身份验证和OpenIdConnect时的奇怪行为

时间:2019-03-07 17:16:14

标签: c# asp.net-core identityserver4 asp.net-core-identity

在对IdentityServer4 Quickstart sample solution(特别是8_AspNetIdentity示例)进行一些调整之后,我遇到了一些问题。

在开始我之前,我不确定我所尝试执行的操作是否受支持,或者我做错了。

此示例解决方案包含与我的问题有关的以下项​​目:

  • IdentityServer,
  • 使用OpenIdConnect对其用户进行身份验证的MVC客户端(名为MVCClient),
  • 一个对其用户使用承载身份验证的Web API客户端(命名为API)
  • 旨在成为API客户端的控制台应用程序(名为ResourceOwnerClient)

我要做的是将API项目合并到MVCClient中,以便MVCClient既可以使用OIDC对其MVC网站中的用户进行身份验证,也可以使用承载身份验证来对ResourceOwnerClient进行身份验证。

我对MVCClient的Startup.cs进行了以下更改:

  1. services.AddMvc();更改为:

    services.AddMvc(config =>
    {
        var policy = new AuthorizationPolicyBuilder(new[]
            {
                JwtBearerDefaults.AuthenticationScheme,
                CookieAuthenticationDefaults.AuthenticationScheme,
                "oidc"
            })
            .RequireAuthenticatedUser()
            .Build();
    
        config.Filters.Add(new AuthorizeFilter(policy));
    });
    
  2. services.AddAuthentication()中添加了JWT承载选项:

    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;
        options.Audience = "api1";
    })
    

现在从技术上讲这确实可行,因为ResourceOwnerClient和MVC用户都可以通过MVCClient成功进行身份验证。但是,我有一个警告:

当我从MVC端向用户进行身份验证时,我注意到当前用户中有两个身份。在声明等方面,两者都是相同的。只有当我在MVCClient中放置一个断点时,才会发生这种情况,在IdentityServer上只有一个标识。

enter image description here

在IdentityServer上,我注册了一个UserClaimsPrincipalFactory,它将我自己的自定义声明添加到ClaimsIdentity。在IdentityServer上的两个身份中,我可以看到重复的声明。因此,我看到两个具有四个自定义声明的身份,而不是具有两个自定义声明的身份。我的UserClaimsPrincipalFactory中的CreateAsync方法也因单次登录而被点击了5次。

尽管这种行为很奇怪,但似乎没有任何负面影响。但这仅是我正在构建的大型应用程序的概念证明,而且我担心将来会因此而遇到问题。

如果有人以前曾尝试过这种事情,或者知道为什么会发生这种情况,我们将不胜感激。

1 个答案:

答案 0 :(得分:3)

尽管这种设计应该不会发生任何不好的事情,但我会彻底重做。为什么?因为您正在混合ClientApiResource,所以它们在逻辑上应该分开。 Client一种应用,某些用户与之交互的东西,即使它是无头的(即自动化服务); ApiResource由提供给客户端的资源组成,因此没有用户可以直接与其交互。

您可以添加针对IdentityServer的两种身份验证,一种作为API(将其添加为JwtBearer),另一种作为客户端(并将其添加为Cookies)。然后,您可以根据操作/控制器的功能使用[Authorize(AuthenticationSchemes = "JwtBearer")]= "Cookies"

抛开这些,问题在于您的应用程序在MVC端获得了一个Identity,在API端获得了一个Identity,因为它无法告诉您想要哪个。

就这样,您便有了一个想法,这就是我的带有ASP.NET Core Identtiy的IdentityServer的外观,您可以在其中使用UI对其进行登录,还可以使用JwtToken访问REST端点:

services
    .AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
        options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
        options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
    })
    .AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.Authority = Configuration["IdentityServerUrl"];
        options.ApiName = Configuration["ApiName"];

        options.RequireHttpsMetadata = false;
    })
    .AddCookie(IdentityConstants.ApplicationScheme, o =>
    {
        o.LoginPath = new PathString("/Account/Login");
        o.Events = new CookieAuthenticationEvents()
        {
            OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
        };
    })
    .AddCookie(IdentityConstants.ExternalScheme, o =>
    {
        o.Cookie.Name = IdentityConstants.ExternalScheme;
        o.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);
    })
    .AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o =>
    {
        o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme;
    })
    .AddCookie(IdentityConstants.TwoFactorUserIdScheme, o =>
    {
        o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme;
        o.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);
    });