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

从IdentityServer4迁移到OpenIddict后刷新令牌遇scope无效错误

从IdentityServer4迁移到OpenIddict后刷新令牌报错'invalid_grant'

问题详情

正从IdentityServer4迁移到OpenIddict,authorization_code授权流程获取令牌正常,但刷新令牌时触发invalid_grant错误,错误描述为The specified 'scope' is invalid. while refreshing token.。

刷新令牌请求载荷

grant_type: refresh_token
scope: openid roles offline_access
refresh_token: *[redacted]
client_id: js

服务器配置

.AddServer(options =>
{
    options.DisableAccessTokenEncryption();

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

    options.AllowAuthorizationCodeFlow()
           .AllowRefreshTokenFlow();

    options.RegisterScopes(Scopes.Profile,
                            Scopes.Roles,
                            Scopes.OfflineAccess,
                            Scopes.OpenId,
                            Scopes.Email,
                            "api1");

    options.RegisterClaims(Claims.Role,
                            Claims.Profile,
                            Claims.Name,
                            Claims.Subject,
                            "user_type");

    options.AddEncryptionKey(new SymmetricSecurityKey(
        Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")));

    // Register the signing credentials.
    options.AddDevelopmentSigningCertificate();

    options.UseAspNetCore()
                           .EnableAuthorizationEndpointPassthrough()
                           .EnableLogoutEndpointPassthrough()
                           .EnableTokenEndpointPassthrough()
                           .EnableUserinfoEndpointPassthrough();

    options.SetAccessTokenLifetime(TimeSpan.FromSeconds(50));
})    
.AddValidation(options =>
{
    options.UseLocalServer();
    options.UseAspNetCore();
});

应用客户端配置

await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
    ClientId = "js",
    ConsentType = ConsentTypes.External,
    DisplayName = "angular client PKCE",
    DisplayNames =
            {
                [CultureInfo.GetCultureInfo("th-TH")] = "Application client"
            },
    PostLogoutRedirectUris =
            {
                new Uri("https://localhost.domain.com")
            },
    RedirectUris =
            {
                new Uri("https://localhost.domain.com/callback")
            },
    Permissions =
            {
                Permissions.Endpoints.Authorization,
                Permissions.Endpoints.Logout,
                Permissions.Endpoints.Token,
                Permissions.Endpoints.Revocation,
                Permissions.GrantTypes.AuthorizationCode,
                Permissions.GrantTypes.RefreshToken,
                Permissions.ResponseTypes.Code,
                Permissions.Scopes.Profile,
                Permissions.Scopes.Roles,
                Permissions.Prefixes.Scope + "api1"
            },
    Requirements =
            {
                Requirements.Features.ProofKeyForCodeExchange
            }
});

相关OpenID配置

"scopes_supported": [
   "openid",
   "offline_access",
   "profile",
   "roles",
   "email",
   "api1"
],

疑问:已禁用AccessToken加密(因为Angular客户端需要读取sub获取用户ID),这是否是问题原因?未发现缺失的scope。


问题原因及解决方法

  • 客户端权限缺失:客户端配置的Permissions集合中未添加Permissions.Scopes.OfflineAccess权限,而刷新令牌请求包含了offline_access scope,OpenIddict会校验客户端是否被允许请求该scope,这是触发错误的核心原因。需要在客户端权限中添加该条目。
  • 刷新令牌scope范围限制:刷新令牌时,请求的scope不能超出初始授权码流程中获取的scope集合,同时所有请求的scope必须都被客户端授权。需确认初始授权时是否包含openid、roles、offline_access这些scope。
  • AccessToken加密的影响:禁用AccessToken加密不是导致该错误的原因,该配置仅用于让客户端直接读取JWT中的声明,与刷新令牌的scope校验无关。

修改后的客户端权限配置应补充:

Permissions =
{
    // 保留原有所有权限...
    Permissions.Scopes.OfflineAccess,
    // 保留原有所有权限...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 06:25:53