c# - Identity Server OAuth 2.0 代码授权 - 如何在同意屏幕中请求自定义范围的权限
问题描述
我已经实现了 Identity Server,它也在工作。
我的一个客户端是 MVC 客户端,在身份验证期间,我想显示同意屏幕。为此,我在客户端配置中添加了“RequireConsent=true”
现在它显示同意屏幕,但问题是,它只显示“openid”和“profile”范围的权限。
我还有其他几个自定义范围,例如“Api1.read”、“Api1.write”,它们在 Identity Server 为 concent 屏幕构建视图模式时未获取授权请求。
我做错了什么。在客户端 AllowedScopes 包含 = { 'openid', 'profile', 'Api1.read', 'Api1.write' }
当它到达同意页面时,ApiResources 和 ApiScopes 为空,但在 IdentityResources 中可以使用 openid 和配置文件
这就是我在启动时配置 IdentityServer 的方式
services.AddIdentityServer(options =>
{
options.Authentication.CookieLifetime = TimeSpan.FromSeconds(config.IdentityServerCookieLifetime);
})
.AddDeveloperSigningCredential()
.AddCorsPolicyService<MyCORSPolicy>()
.AddResourceStore<MyResourceStore>()
.AddClientStore<MyClientStore>()
.AddProfileService<ProfileService>()
.AddDeveloperSigningCredential();
我正在使用 IClientStore 和 IResourceStore 来实现从数据库中获取详细信息,而不是 appsettings.json 中的静态配置
我也不想为此使用实体框架核心。我更喜欢使用我自己的自定义表模式和 Dapper。
这是 MVC 应用程序的启动配置
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
//Add Support for OAuth 2.0 Code-Grant With Identity Server 4
services.AddAuthentication(opt =>
{
opt.DefaultScheme = "Cookies";
opt.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", opt =>
{
opt.SignInScheme = "Cookies";
opt.Authority = "https://localhost:5005";
opt.ClientId = "mvc-client";
opt.ResponseType = "code";
opt.ClientSecret = "MVCSecret";
opt.UseTokenLifetime = true;
opt.SaveTokens = true;
});
}
这是我的 ResourceStore 实现
public class MyResourceStore : IResourceStore
{
private readonly IConfiguration config;
private readonly string connectionString;
public MyResourceStore(IConfiguration config)
{
this.config = config;
this.connectionString = config.GetConnectionString("AuthConfigDatabase");
}
public async Task<IEnumerable<IdentityServer4.Models.ApiResource>> FindApiResourcesByNameAsync(IEnumerable<string> apiResourceNames)
{
var apis = SqlHelper.Query<AuthApiResources>($"SELECT * FROM AuthApiResources WHERE Name='{apiResourceNames}' AND IsActive=1", connectionString);
if (apis != null)
{
var result = new List<IdentityServer4.Models.ApiResource>();
foreach (var api in apis)
{
var availableScopes = new List<string>() { "openid", "profile" };
availableScopes.AddRange(api.SupportedScopes.Split(",").ToList());
result.Add(new IdentityServer4.Models.ApiResource
{
Name = api.Name,
DisplayName = api.DisplayName,
Scopes = availableScopes
});
}
return result;
}
return null;
}
public async Task<IEnumerable<IdentityServer4.Models.ApiResource>> FindApiResourcesByScopeNameAsync(IEnumerable<string> scopesList)
{
var scopeNames = scopesList.ToList();
var likeStatements = "";
for (var i = 0; i < scopeNames.Count(); i++)
{
if (i == scopeNames.Count() - 1)
{
likeStatements += $"SupportedScopes LIKE '%{scopeNames[i]}%'";
}
else
{
likeStatements += $"SupportedScopes LIKE '%{scopeNames[i]}%' OR ";
}
}
var apis = SqlHelper.Query<AuthApiResources>($"SELECT * FROM AuthApiResources WHERE ({likeStatements}) AND IsActive=1", connectionString);
if (apis != null)
{
var result = new List<IdentityServer4.Models.ApiResource>();
foreach (var api in apis)
{
var availableScopes = new List<string>() { "openid", "profile" };
availableScopes.AddRange(api.SupportedScopes.Split(",").ToList());
result.Add(new IdentityServer4.Models.ApiResource
{
Name = api.Name,
DisplayName = api.DisplayName,
Scopes = availableScopes
});
}
return result;
}
return null;
}
public async Task<IEnumerable<ApiScope>> FindApiScopesByNameAsync(IEnumerable<string> scopesList)
{
var scopeNames = scopesList.ToList();
var likeStatements = "";
for (var i = 0; i < scopeNames.Count(); i++)
{
if (i == scopeNames.Count() - 1)
{
likeStatements += $"ScopeName='{scopeNames[i]}'";
}
else
{
likeStatements += $"ScopeName='{scopeNames[i]}' OR ";
}
}
var scopes = SqlHelper.Query<AuthScope>($"SELECT * FROM AuthScopes WHERE ({likeStatements})", connectionString);
if (scopes != null)
{
var result = new List<IdentityServer4.Models.ApiScope>();
foreach (var scope in scopes)
{
result.Add(new IdentityServer4.Models.ApiScope
{
Name = scope.ScopeName,
DisplayName = scope.ScopeDescription
});
}
return result;
}
return null;
}
public async Task<IEnumerable<IdentityResource>> FindIdentityResourcesByScopeNameAsync(IEnumerable<string> scopeNames)
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
public async Task<Resources> GetAllResourcesAsync()
{
var allResources = new Resources();
allResources.IdentityResources =
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
var apis = SqlHelper.Query<AuthApiResources>($"SELECT * FROM AuthApiResources WHERE IsActive=1", connectionString);
if (apis != null)
{
var result = new List<IdentityServer4.Models.ApiResource>();
foreach (var api in apis)
{
var availableScopes = new List<string>() { "openid", "profile" };
availableScopes.AddRange(api.SupportedScopes.Split(",").ToList());
result.Add(new IdentityServer4.Models.ApiResource
{
Name = api.Name,
DisplayName = api.DisplayName,
Scopes = availableScopes
});
}
allResources.ApiResources = result;
}
var scopes = SqlHelper.Query<AuthScope>($"SELECT * FROM AuthScopes", connectionString);
if (scopes != null)
{
var result = new List<IdentityServer4.Models.ApiScope>();
foreach (var scope in scopes)
{
result.Add(new IdentityServer4.Models.ApiScope
{
Name = scope.ScopeName,
DisplayName = scope.ScopeDescription
});
}
allResources.ApiScopes = result;
}
return allResources;
}
}
我究竟做错了什么
解决方案
In your client, inside the AddOpenIdConnect method, you need to also define what scopes you want to have access to, like:
.AddOpenIdConnect(options =>
{
...
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("employee_info");
...
}
推荐阅读
- ruby-on-rails - 如何在 RoR 中播放视频(流媒体)...???我什么时候上传视频?
- python - 合并具有不一致数据的数据框
- echarts - 是否可以将数据添加到条形图中鼠标悬停时出现的 echart 框中
- c - 如何在禁用安全性较低的 TLS 协议版本的情况下构建 OpenSSL 1.0 DLL?
- common-lisp - Common Lisp 阅读器:自定义实习生行为
- python - 是否可以在没有“渲染”对象的情况下创建物理模拟(使用子弹物理引擎和 Panda3D)
- c# - 如何获取目录中文件的大小?
- python - 我的 Django 模板中的“Else if”语句出现在 HTML 标头块之后
- ruby-on-rails - Ruby on rails 6:无法编辑我的 credentials.yml.enc
- python - asyncio - 从任务中重新引发异常