我正在开发一个Web API应用程序,该应用程序将使用身份服务器4进行保护。 用户使用 JavaScript 客户端使用隐式流进行身份验证。
我在 Web API 客户端中读取标识资源作用域时遇到问题。 我想当我调用应用程序时,配置中可能缺少某些内容。在我的 Startup.cs 中使用 IdentityServerAuthentication( (。 当我检查 User.Claims 集合时,我看不到任何这些 IdentityResource 范围内的 JSON 数据。
在 JavaScript 客户端中,我能够使用 oidc-client.js 库查看范围数据。 但是,我无法读取 Web API 应用程序中的范围数据。
在 javascript 客户端中,我可以通过在 TypeScript 中执行此操作来查看存储为 json blob 的范围数据:
class MyService {
private _userManager: Oidc.UserManager;
private _createUserManager(authority: string, origin: string) {
// https://github.com/IdentityModel/oidc-client-js/wiki#configuration
var config : Oidc.UserManagerSettings = {
authority: authority,
client_id: "myapi",
redirect_uri: `${origin}/callback.html`,
response_type: "id_token token",
scope: "openid profile user.profile user.organization ...",
post_logout_redirect_uri: `${origin}/index.html`
};
this._userManager = new Oidc.UserManager(config);
}
// snip
user() {
this._userManager.getUser().then(user => {
const userProfile = JSON.parse(user.profile["user.profile"]);
console.log(userProfile);
const userOrganization = JSON.parse(user.profile["user.organization"]);
console.log(userOrganization);
});
}
}
在我的 Web API 项目中,我有:
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
public IContainer Container { get; set; }
public Startup(IHostingEnvironment env)
{
var configurationBuilder = new ConfigurationBuilder();
if (env.IsDevelopment())
{
configurationBuilder
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json");
env.ConfigureNLog("nlog.development.config");
}
else
{
configurationBuilder.AddEnvironmentVariables();
env.ConfigureNLog("nlog.config");
}
Configuration = configurationBuilder.Build();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddJsonFormatters()
.AddAuthorization();
services.AddCors();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IRestService, RestService>();
var builder = AutoFacConfig.Create(Configuration);
builder.Populate(services);
Container = builder.Build();
return new AutofacServiceProvider(Container);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors(policy =>
{
policy
.WithOrigins(
"http://app.local:5000"
)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
ConfigureExceptionHandling(app, env);
ConfigureOidc(app);
app.UseMvc();
}
private void ConfigureExceptionHandling(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
}
private void ConfigureOidc(IApplicationBuilder app)
{
app.UseCors("default");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = Configuration["IdentityServerUrl"],
AllowedScopes = { "api1", "openid", "profile", "user.profile", "user.organization" ... },
ApiName = "myapi",
RequireHttpsMetadata = false
});
}
}
问题是当我尝试访问声明以读取其范围时。 对用户进行身份验证后,我将请求标头中的令牌传递给 Web API 终结点。 我有一个基本控制器,我想从中读取范围,但不能。
public class BaseController : Controller
{
private ApplicationUser _user;
public string UserId => CurrentUser?.UserInfo.Id;
public string OrganizationId => CurrentUser?.Organization.Id;
[CanBeNull]
public ApplicationUser CurrentUser
{
get
{
var claims = Request.HttpContext.User.Claims.ToList();
_user = new ApplicationUser
{
UserInfo = claims.Where(f => f.Type == "user.profile").Select(s => s.Value).FirstOrDefault(),
OrganizationInfo = claims.Where(f => f.Type == "user.organization").Select(s => s.Value).FirstOrDefault()
};
return _user;
}
}
}
如果我查看声明集合,则会看到以下类型和值:
注:1499441027
经验: 1499444627
伊斯:https://identity-service...
澳元:https://identity 服务..../资源
澳元:妙亚羚
client_id:妙药
子: the_user
auth_time:1499441027
境内流离失所者:本地
范围:开放ID
范围:配置文件
范围:用户配置文件
范围:用户组织
。
范围:妙药
抗微生物药物耐药性:残疾人
如何从 C# 读取范围数据?
对于user.organization范围,我希望读取如下数据结构:
{
"Id":"some id",
"BusinessCategory":"some category",
"Name":"some name"
}
作用域是类型,配置文件是其中一个作用域的值,这就是为什么它是这样的。您想看到配置文件的什么值?它没有任何价值,因为它本身就是一个值。你要求它:AllowedScopes = { "api1", "openid", "profile", "user.profile", ... },
这就是您在索赔列表中获得的范围。
如果要从声明列表中获取特定值,还可以使用JwtRegisterClaimNames
User.FindFirst(JwtRegisteredClaimNames.Email(.价值
以防这对任何人都有用。 我已经实施了一个黑客。 我在中间件中添加了额外的代码来调用身份服务器的用户信息端点。 用户信息端点将返回我需要的数据。 然后,我使用此数据手动添加声明。
我已将此代码添加到我的配置方法中:
app.Use(async (context, next) =>
{
if (context.User.Identity.IsAuthenticated)
{
using (var scope = Container.BeginLifetimeScope())
{
// fetch resource identity scopes from userinfo endpoint
var restService = scope.Resolve<IRestService>();
var token = context.Request.Headers["Authorization"]
.ToString().Replace("Bearer ", string.Empty);
restService.SetAuthorizationHeader("Bearer", token);
// fetch data from my custom RestService class and
// serialize into my custom ScopeData object
var data = await restService.Get<ScopeData>(
Configuration["IdentityServerUrl"], "connect/userinfo");
if (data.IsSuccessStatusCode)
{
// add identity resource scopes to claims
var claims = new List<Claim>
{
new Claim("user.organization", data.Result.Organization),
new Claim("user.profile", data.Result.UserInfo)
};
}
else
{
sLogger.Warn("Failed to retrieve identity scopes from profile service.");
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
}
}
}
await next();
});