You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让OpenIddict Server认证方案在自定义API端点生效?

问题描述

基于Robin van der Knaap的指南搭建OpenIddict OAuth服务器(采用Identity而非Cookie实现),需在同一项目中运行Web API,使用Postman测试端点时遇到以下问题:

  • AuthorisationController中的userinfo端点使用[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]可正常工作
  • 自定义Web API控制器端点(/api/Test/)及新增的~/connect/hello端点使用相同授权注解时触发错误
  • 直接使用[Authorize]时始终返回未授权

正常工作的Userinfo端点代码

[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
[HttpGet("~/connect/userinfo")]
public async Task<IActionResult> Userinfo()
{
    var claimsPrincipal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;

    return Ok(new
    {
        Name = claimsPrincipal.GetClaim(OpenIddictConstants.Claims.Subject),
        Occupation = "Developer",
        Age = 43
    });
}

报错的自定义API端点代码

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    [HttpGet]
    [Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
    public IActionResult Index()
    {
        return Ok("hello");
    }
}

错误信息

System.InvalidOperationException: An identity cannot be extracted from
this request. This generally indicates that the OpenIddict server
stack was asked to validate a token for an endpoint it doesn't manage.
To validate tokens received by custom API endpoints, the OpenIddict
validation handler (e.g
OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme or
OpenIddictValidationOwinDefaults.AuthenticationType) must be used
instead. at
OpenIddict.Server.OpenIddictServerHandlers.ValidateAuthenticationDemand.HandleAsync(ProcessAuthenticationContext
context) at
OpenIddict.Server.OpenIddictServerDispatcher.DispatchAsync[TContext](TContext
context) at
OpenIddict.Server.OpenIddictServerDispatcher.DispatchAsync[TContext](TContext
context) at
OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandler.HandleAuthenticateAsync()
at
Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync()
at
Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext
context, String scheme) at
Microsoft.AspNetCore.Authorization.Policy.PolicyEvaluator.AuthenticateAsync(AuthorizationPolicy
policy, HttpContext context) at
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext
context) at
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext
context) at
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)

Program.cs核心配置

builder.Services.AddOpenIddict()
        .AddCore(options =>
        {
            options.UseEntityFrameworkCore()
                .UseDbContext<ApplicationDbContext>();
        })
        .AddServer(options =>
        {
            options
                .AllowClientCredentialsFlow()
                .AllowAuthorizationCodeFlow().RequireProofKeyForCodeExchange()
                .AllowRefreshTokenFlow();

            options
                .SetTokenEndpointUris("/connect/token")
                .SetAuthorizationEndpointUris("/connect/authorize")
                .SetTokenEndpointUris("/connect/token")
                .SetUserinfoEndpointUris("/connect/userinfo");

            options
                .AddEncryptionCertificate(CertificateHelper.LoadCertificateFromKeyVault(builder.Configuration["KeyVault:Name"], builder.Configuration["OAuth:EncryptionCertName"]))
                .AddSigningCertificate(CertificateHelper.LoadCertificateFromKeyVault(builder.Configuration["KeyVault:Name"], builder.Configuration["OAuth:EncryptionCertName"]))
                .DisableAccessTokenEncryption();

            options.RegisterScopes("api");

            options
                .UseAspNetCore()
                .EnableTokenEndpointPassthrough()
                .EnableAuthorizationEndpointPassthrough()
                .EnableUserinfoEndpointPassthrough();
        })
        .AddValidation();

builder.Services.AddAuthentication(options => options.DefaultScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
解决方案

核心原因

OpenIddict的Server组件仅负责处理它明确注册的端点(如/connect/userinfo、/connect/token等),自定义API端点不属于Server组件的管辖范围,必须使用OpenIddict Validation组件的认证方案来验证令牌。

具体修改

  1. 修改自定义端点的授权注解
    将自定义API端点的Authorize注解中的认证方案替换为Validation组件的默认值:
// 自定义API端点使用Validation方案
[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
  1. 确保直接使用[Authorize]生效
    虽然你已在Program.cs中设置默认认证方案为Validation,但需确保请求携带的令牌满足以下条件:
  • 令牌包含api scope(你已在Server配置中注册该scope,申请令牌时需指定此scope)
  • 请求中正确携带令牌(通常放在Authorization: Bearer <token>头中)

示例修改后的TestController

using OpenIddict.Validation.AspNetCore;

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    [HttpGet]
    [Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
    public IActionResult Index()
    {
        return Ok("hello");
    }
}

新增的connect/hello端点修改

using OpenIddict.Validation.AspNetCore;

[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
[HttpGet("~/connect/hello")]
public async Task<IActionResult> Hello()
{
    return Ok("hi");
}

补充说明

  • userinfo端点能正常工作是因为你在Server配置中通过SetUserinfoEndpointUris("/connect/userinfo")将其注册为Server管辖的端点,因此可以使用Server的认证方案
  • 若希望所有API端点默认使用Validation方案,除了设置options.DefaultScheme外,还可配置options.DefaultAuthenticateScheme和options.DefaultChallengeScheme:
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
    options.DefaultAuthenticateScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
});

这样无需在每个[Authorize]注解中指定认证方案,直接使用[Authorize]即可。

内容的提问来源于stack exchange,提问作者tappetyclick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 19:35:20