OpenIddict 里 scope 和 claims 的区别在哪
scope 是客户端请求访问权限时声明的“能力范围”,比如
api.read或
profile;claims 是用户身份中实际携带的“属性数据”,比如
name、
role。OpenIddict 不自动把 scope 映射成 claims,也不自动发放 claims —— 它只按你配置的 scope 列表决定是否签发 token,而 claims 必须显式添加到
ClaimsPrincipal或通过
IAuthorizationService注入。
常见错误是以为只要在 client 配置了
scopes = ["profile"],token 就会自动包含
name和
ClaimsIdentity,或用
AddClaims扩展方法补全。 scope 控制“能访问什么”,用于授权决策(如
[Authorize(Policy = "ApiRead")]) claims 控制“是谁、有什么属性”,用于业务逻辑(如
User.FindFirst("role")?.Value)
OpenIddict 默认只注入 sub、
iat、
exp等基础 claims,其余全靠你填
如何注册 API 资源(Resource Server)
API 资源不是 OpenIddict 自动识别的,而是靠
AddApiScopes+
AddApiResources显式定义,并在 API 项目中用
AddAuthentication().AddJwtBearer()验证 token 中的
aud和
scope。
关键点:API 项目和授权服务器可以分离,但 scope 名称必须完全一致(大小写敏感),且 API 的
audience必须匹配授权服务器注册的
ApiResources名称。 在授权服务器调用
services.AddOpenIddict().AddServer().AddApiScopes("api.read", "api.write")
再调用 AddApiResources("my_api", "My API"),其中 "my_api"将成为 JWT 的
aud值 在 API 项目中,
JwtBearerOptions.Audience必须设为
"my_api",否则验证失败 scope 检查需配合策略:如
options.AddPolicy("ApiRead", p => p.RequireClaim("scope", "api.read"))
怎么配置 identity resources(如 profile、email)
identity resources 是 OIDC 标准中预定义的用户属性集合,OpenIddict 本身不内置实现,但允许你用
AddIdentityResources注册名称,并在 token endpoint 返回时手动注入对应 claims。
例如注册
profile后,客户端带
scope=profile请求 token,OpenIddict 不会自动返回
name或
picture—— 你需要监听
ProcessSignInContext事件,在签发前把用户数据塞进去。 注册:
options.AddIdentityResources("profile", "email", "address")
监听事件:options.AddEventHandler<openiddictserverevents.processsignincontext>(...)</openiddictserverevents.processsignincontext>在 handler 中判断
context.Request.GetScopes().Contains("profile"),再调用 context.Principal.AddIdentityTokenClaim(...)注意:这些 claims 只出现在 ID Token(登录响应)中,不出现在 Access Token,除非你额外往 access token 加
为什么 /connect/token 返回的 access_token 没有 scope 字段
OpenIddict 默认不把 scope 写进 access_token payload,它只在 token 的
scope响应参数里返回(HTTP body 中),这是 OAuth 2.0 规范行为。access_token 是 opaque 或 JWT,JWT 版本默认也不含
scopeclaim —— 因为 scope 是授权上下文信息,不是资源访问所需的凭证内容。
如果你需要在 API 中读取 scope,有两种方式:
解析 JWT 并检查scopeclaim(需手动添加,OpenIddict 不自动加):在
ProcessSignInContext中调用
context.Principal.AddAccessTokenClaim("scope", string.Join(" ", context.Request.GetScopes()))
更推荐的方式:用 policy + RequireScope("api.read"),它底层从 context.User.FindAll("scope") 读取,无需改 token 结构
切勿依赖 access_token 中是否存在 scope字段做鉴权,应统一走
IAuthorizationService或策略机制
最易被忽略的是:scope 名称拼写不一致、大小写错位、API 未正确设置 audience、以及忘记在
ProcessSignInContext中手动注入 identity claims —— 这些都会导致看似配置完整,实则 token 为空或鉴权失败。
