【问题标题】:Identity Server 4 API Authentication FailureIdentity Server 4 API 身份验证失败
【发布时间】:2019-04-18 18:27:09
【问题描述】:

使用 .Net Core 2.1 和 Identity Server 4

我有 3 家初创公司,分别是 IS4、MVC App 和 API App。我能够很好地登录到我的 MVC 应用程序,并检索我的 access_token。我尝试使用此令牌访问 API,但无法授权该请求。

这是我的 IS4 Startup.cs localhost:5000

services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<IdentityContext>()
            .AddDefaultTokenProviders();

        services.AddMvc();

        services.Configure<IISOptions>(iis =>
        {
            iis.AuthenticationDisplayName = "Windows";
            iis.AutomaticAuthentication = false;
        });

        services.AddIdentityServer()
            .AddAspNetIdentity<ApplicationUser>()
            .AddConfigurationStore(configDb =>
            {
                configDb.ConfigureDbContext = db => db.UseNpgsql(Configuration.GetConnectionString("IdentityServer4ConfigurationContext"),
                    sql => sql.MigrationsAssembly(typeof(IdentityServer4Startup).GetTypeInfo().Assembly.GetName().Name));
            })
            .AddOperationalStore(operationDb =>
            {
                operationDb.ConfigureDbContext = db => db.UseNpgsql(Configuration.GetConnectionString("IdentityServer4PersistedGrantContext"),
                     sql => sql.MigrationsAssembly(typeof(IdentityServer4Startup).GetTypeInfo().Assembly.GetName().Name));
            })
            .AddDeveloperSigningCredential();

我的 MVC Startup.cs localhost:61000

services.AddMvc();

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<IdentityContext>()
            .AddDefaultTokenProviders();

        services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
                {
                    options.SignInScheme = "Cookies";
                    options.Authority = "http://localhost:5000";
                    options.RequireHttpsMetadata = false;
                    options.ClientId = "mvc";
                    options.ClientSecret = "secret";
                    options.ResponseType = "code id_token";
                    options.SaveTokens = true;
                    options.GetClaimsFromUserInfoEndpoint = true;
                    options.Scope.Add("api1");
                    options.Scope.Add("offline_access");
                    options.Scope.Add("openid");
                    options.Scope.Add("email");
                });

还有我的 API Startup.cs localhost:62000

services.AddMvc();


        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
            .AddIdentityServerAuthentication("oidc", options =>
            {
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;
                options.ApiName = "api1";
            });

我的客户信息是

new Client {
                ClientId = "mvc",
                ClientName = "Example Mvc",
                AllowedGrantTypes = GrantTypes.Hybrid,
                AllowedScopes = new List<string>
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.Email,
                    "role",
                    "api1"
                },
                RedirectUris = new List<string> {"http://localhost:61000/signin-oidc"},
                PostLogoutRedirectUris = new List<string> { "http://localhost:61000/signout-callback-oidc" }
            }

和 API 资源

new ApiResource {
                Name = "api1",
                DisplayName = "Custom API",
                Description = "Custom API Access",
                UserClaims = new List<string> {"role"},
                ApiSecrets = new List<Secret> {new Secret("scopeSecret".Sha256())},
                Scopes = new List<Scope> {
                    new Scope("api1"),
                }
            }

当我以客户端身份登录时尝试调用 API 时,我无法访问它。它会给出错误

[10:40:11 ERR] Invalid redirect_uri: http://localhost:62000/signin-oidc
{
  "ClientId": "mvc",
  "ClientName": "MVC Client",
  "AllowedRedirectUris": [
    "http://localhost:61000/signin-oidc"
  ],
  "SubjectId": "anonymous",
  "RequestedScopes": "",
  "Raw": {
    "client_id": "mvc",
    "redirect_uri": "http://localhost:62000/signin-oidc",
    "response_type": "code id_token",
    "scope": "openid profile api1 offline_access email",
    "response_mode": "form_post",
    "nonce": "636778932114224044.N2M1YzZhMjktMThjMS00ZGEyLWI1OGQtZjM1YmZiMzViNDVkNDQ2NDhjZWMtNzk4Mi00NTc2LWI2YzctNjAwMzkwNjI5NGE5",
    "state": "CfDJ8EzT-5PsjT5Htj2FYGOkFvxTH4w-8q3uXGtc1wXYyvWMHyarfbV0cQz8cFh4ESIW33n4pwtIEmLhrTpX-0Y0-_HS24cWs05nR3npx1ZIAJsYr9hIqB9hj9Ic_QYkU2Z_bcjjnaynqfbF5KIyyQhYGNlDxDfknwZUTXpPzTFEhLTcnam7O-b-xV9a3e9iWARoFZRBLjGV5Hs5i-8jS5EGM9DpqwcvrUjSUlRVDP-TzBBhYPhswn2hKsjkhL26_Gp9lluoKNLOvUVM-yq1zHbbI9uTcZzS_2GtbQoPuuGTmCuOoV-c-K2IztzXP88W-osYaY9wQP2qi8aXD951hrAhYbo",
    "x-client-SKU": "ID_NETSTANDARD1_4",
    "x-client-ver": "5.2.0.0"
  }
}

我认为我不需要注册 redirect_uri。但是我还是尝试了,当我做同样的请求时,它只会返回登录屏幕。因为它无法验证我的请求。

这是我从 MVC 发送到 API 的 HttpClient 请求

var apiUrl = "http://localhost:62000/test/api";
        var authenticate = await HttpContext.AuthenticateAsync("Cookies");
        var accessToken = authenticate.Ticket.Properties.GetTokenValue("access_token");
        var client = new HttpClient();
        client.SetBearerToken(accessToken);
        var response = await client.GetAsync(apiUrl);
        return Json(response.Content.ReadAsStringAsync());

从所有示例和文档中,我看不出为什么这不起作用。任何帮助将不胜感激。

这是我尝试使用不记名令牌调用 API 时 IS4 的输出。

[12:38:58 INF] Request starting HTTP/1.1 GET http://localhost:5000/connect/authorize?client_id=mvc&redirect_uri=http%3A%2F%2Flocalhost%3A62000%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20api1%20offline_access%20email&response_mode=form_post&nonce=636779003387253889.MThjYjQ5MzItOTY3NS00NmM1LWJhZDItMzkxMzYyMWQzZTUwZThjNThmMjUtMmQyZC00M2YyLTk3OTktN2E3OTc1ZDcyNmM0&state=CfDJ8EzT-5PsjT5Htj2FYGOkFvzm1YFOhTLHlbLS3LpJd_xUBgDz8hWjGkXxSzfnWKqoVU5d4L7ESHCSDGxqmxqnMh1-j-IUGBLzt5uEYFi2M4QV6WmGN1Lv5bMkUDRbrE9pdCNc7vLDUDZY1OWlp4HfQ0vEMr8-OfUH4Q00fOn1v9zpcE6QlHI5Aye9xiFOshHRyMCNwEEcVrLz4Y06Hsa-40OKom3xb8sie09JtiSMZJL9WyKgKYGRgqUIpD6wqryBafzMl_t1tvegYWESq59TafnIpvrFd3ifdOzJx6cXul4ZCcFv0wfvsqpUgCF7t7xyMSwtheDSiE2AySkbnrpr_Dg&x-client-SKU=ID_NETSTANDARD1_4&x-client-ver=5.2.0.0
[12:38:58 INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
[12:38:58 INF] ValidatedAuthorizeRequest
{
  "ClientId": "mvc",
  "ClientName": "MVC Client",
  "RedirectUri": "http://localhost:62000/signin-oidc",
  "AllowedRedirectUris": [
    "http://localhost:62000/signin-oidc",
    "http://localhost:61000/signin-oidc"
  ],
  "SubjectId": "anonymous",
  "ResponseType": "code id_token",
  "ResponseMode": "form_post",
  "GrantType": "hybrid",
  "RequestedScopes": "openid profile api1 offline_access email",
  "State": "CfDJ8EzT-5PsjT5Htj2FYGOkFvzm1YFOhTLHlbLS3LpJd_xUBgDz8hWjGkXxSzfnWKqoVU5d4L7ESHCSDGxqmxqnMh1-j-IUGBLzt5uEYFi2M4QV6WmGN1Lv5bMkUDRbrE9pdCNc7vLDUDZY1OWlp4HfQ0vEMr8-OfUH4Q00fOn1v9zpcE6QlHI5Aye9xiFOshHRyMCNwEEcVrLz4Y06Hsa-40OKom3xb8sie09JtiSMZJL9WyKgKYGRgqUIpD6wqryBafzMl_t1tvegYWESq59TafnIpvrFd3ifdOzJx6cXul4ZCcFv0wfvsqpUgCF7t7xyMSwtheDSiE2AySkbnrpr_Dg",
  "Nonce": "636779003387253889.MThjYjQ5MzItOTY3NS00NmM1LWJhZDItMzkxMzYyMWQzZTUwZThjNThmMjUtMmQyZC00M2YyLTk3OTktN2E3OTc1ZDcyNmM0",
  "Raw": {
    "client_id": "mvc",
    "redirect_uri": "http://localhost:62000/signin-oidc",
    "response_type": "code id_token",
    "scope": "openid profile api1 offline_access email",
    "response_mode": "form_post",
    "nonce": "636779003387253889.MThjYjQ5MzItOTY3NS00NmM1LWJhZDItMzkxMzYyMWQzZTUwZThjNThmMjUtMmQyZC00M2YyLTk3OTktN2E3OTc1ZDcyNmM0",
    "state": "CfDJ8EzT-5PsjT5Htj2FYGOkFvzm1YFOhTLHlbLS3LpJd_xUBgDz8hWjGkXxSzfnWKqoVU5d4L7ESHCSDGxqmxqnMh1-j-IUGBLzt5uEYFi2M4QV6WmGN1Lv5bMkUDRbrE9pdCNc7vLDUDZY1OWlp4HfQ0vEMr8-OfUH4Q00fOn1v9zpcE6QlHI5Aye9xiFOshHRyMCNwEEcVrLz4Y06Hsa-40OKom3xb8sie09JtiSMZJL9WyKgKYGRgqUIpD6wqryBafzMl_t1tvegYWESq59TafnIpvrFd3ifdOzJx6cXul4ZCcFv0wfvsqpUgCF7t7xyMSwtheDSiE2AySkbnrpr_Dg",
    "x-client-SKU": "ID_NETSTANDARD1_4",
    "x-client-ver": "5.2.0.0"
  }
}
[12:38:58 INF] Showing login: User is not authenticated
[12:38:58 INF] Request finished in 138.5606ms 302
[12:38:58 INF] Request starting HTTP/1.1 GET http://localhost:5000/account/login?returnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Dmvc%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A62000%252Fsignin-oidc%26response_type%3Dcode%2520id_token%26scope%3Dopenid%2520profile%2520api1%2520offline_access%2520email%26response_mode%3Dform_post%26nonce%3D636779003387253889.MThjYjQ5MzItOTY3NS00NmM1LWJhZDItMzkxMzYyMWQzZTUwZThjNThmMjUtMmQyZC00M2YyLTk3OTktN2E3OTc1ZDcyNmM0%26state%3DCfDJ8EzT-5PsjT5Htj2FYGOkFvzm1YFOhTLHlbLS3LpJd_xUBgDz8hWjGkXxSzfnWKqoVU5d4L7ESHCSDGxqmxqnMh1-j-IUGBLzt5uEYFi2M4QV6WmGN1Lv5bMkUDRbrE9pdCNc7vLDUDZY1OWlp4HfQ0vEMr8-OfUH4Q00fOn1v9zpcE6QlHI5Aye9xiFOshHRyMCNwEEcVrLz4Y06Hsa-40OKom3xb8sie09JtiSMZJL9WyKgKYGRgqUIpD6wqryBafzMl_t1tvegYWESq59TafnIpvrFd3ifdOzJx6cXul4ZCcFv0wfvsqpUgCF7t7xyMSwtheDSiE2AySkbnrpr_Dg%26x-client-SKU%3DID_NETSTANDARD1_4%26x-client-ver%3D5.2.0.0
[12:38:58 INF] Route matched with {action = "Login", controller = "Account"}. Executing action ASC.ADEX.Runtime.Controllers.AccountController.Login (ASC.ADEX.Runtime)
[12:38:58 INF] Executing action method ASC.ADEX.Runtime.Controllers.AccountController.Login (ASC.ADEX.Runtime) with arguments (["/connect/authorize/callback?client_id=mvc&redirect_uri=http%3A%2F%2Flocalhost%3A62000%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20api1%20offline_access%20email&response_mode=form_post&nonce=636779003387253889.MThjYjQ5MzItOTY3NS00NmM1LWJhZDItMzkxMzYyMWQzZTUwZThjNThmMjUtMmQyZC00M2YyLTk3OTktN2E3OTc1ZDcyNmM0&state=CfDJ8EzT-5PsjT5Htj2FYGOkFvzm1YFOhTLHlbLS3LpJd_xUBgDz8hWjGkXxSzfnWKqoVU5d4L7ESHCSDGxqmxqnMh1-j-IUGBLzt5uEYFi2M4QV6WmGN1Lv5bMkUDRbrE9pdCNc7vLDUDZY1OWlp4HfQ0vEMr8-OfUH4Q00fOn1v9zpcE6QlHI5Aye9xiFOshHRyMCNwEEcVrLz4Y06Hsa-40OKom3xb8sie09JtiSMZJL9WyKgKYGRgqUIpD6wqryBafzMl_t1tvegYWESq59TafnIpvrFd3ifdOzJx6cXul4ZCcFv0wfvsqpUgCF7t7xyMSwtheDSiE2AySkbnrpr_Dg&x-client-SKU=ID_NETSTANDARD1_4&x-client-ver=5.2.0.0"]) - Validation state: Valid
[12:38:58 INF] Executed action method ASC.ADEX.Runtime.Controllers.AccountController.Login (ASC.ADEX.Runtime), returned result Microsoft.AspNetCore.Mvc.ViewResult in 41.3848ms.
[12:38:58 INF] Executing ViewResult, running view Login.
[12:38:58 INF] Executed ViewResult - view Login executed in 1.8018ms.
[12:38:58 INF] Executed action ASC.ADEX.Runtime.Controllers.AccountController.Login (ASC.ADEX.Runtime) in 78.939ms
[12:38:58 INF] Request finished in 83.6512ms 200 text/html; charset=utf-8

【问题讨论】:

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


    【解决方案1】:

    我使用AddJwtBearer 设置了我的API 身份验证,如下所示。我看到你使用AddIdentityServerAuthentication。可能是这个原因?

    services.AddJwtBearer(o =>
                   {
                       o.Authority = "<id-server>";
                       o.Audience = "<api-audience>";
                       o.RequireHttpsMetadata = true;
                   });
    

    API 控制器指定了AuthenticationSchemes,如下所示:

    [Route("api/rates")]
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    public class RatesController : Controller
    {
    }
    

    【讨论】:

    • 当我更新到该代码时,我收到错误An unhandled exception has occurred while executing the request. System.InvalidOperationException: No authentication handler is registered for the scheme 'Bearer'. The registered schemes are: Identity.Application, Identity.External, Identity.TwoFactorRememberMe, Identity.TwoFactorUserId, Cookies, oidc. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("Bearer",...)?
    • 就我而言,这些项目不仅仅是纯粹的 API 项目。所以我执行以下操作 - services.AddAuthentication(...) .AddCookie() .AddOpenIdConnect(...) .AddJwtBearer(...);我猜您可能删除了 AddAuthentication?我建议将其添加回来并遵循上面的模式。
    • k25 你是说将这些添加到 API 启动中?或者 MVC
    • 对于 API 项目,如果它是纯 API 项目,则不需要 AddOpenIdConnect(..) 部分,但您仍然需要 AddAuthentication(...)。 stackoverflow 链接注释也许会提供一些见解。
    • 该项目包含所有部分,IS4、API 和 MVC。我有一个指向启动作业的命令行,因此我可以从同一个项目运行 3 个不同的应用程序。这是为了简化 Docket 部署。
    【解决方案2】:

    您通常只会在 Client(即您的 MVC 应用程序)重定向到 Identity Server 以便用户可以进行身份​​验证时获得Invalid redirect_uri,而不是在调用 API 时,也不是由 API 引起的本身,因此您应该从 API 的配置中删除挑战,并将其仅留给 UI。您的 API 还需要其秘密来对 Identity Server 进行一些反向通道调用,因此请将您的 API 的 Startup.cs 更改为:

            services.AddAuthorization();
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(
                    options =>
                    {
                        options.Authority = "http://localhost:5000";
                        options.ApiName = "api1";
                        options.ApiSecret = "scopeSecret";
                        options.RequireHttpsMetadata = false;
    
                        // You will almost certainly want these at some point too,
                        // to prevent the API talking to the IS for every
                        // API call.  Adjust the duration as desired.
                        options.EnableCaching = true;
                        options.CacheDuration = TimeSpan.FromMinutes(10);
                    });
    

    您没有显示 API 的 Startup.cs 的 Configure() 部分,但要确保它有

    .UseAuthentication();
    

    调用之前

    .AddMvc()
    

    现在您应该只需要使用[Authorize] 装饰受保护的 API 方法,并确保您通过用户的不记名令牌来调用这些 API 方法。

    【讨论】:

      猜你喜欢
      • 2017-06-26
      • 2019-08-17
      • 2018-07-23
      • 2019-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-31
      相关资源
      最近更新 更多