Identity Server OAuth 2.0 代码授权 - 如何在同意屏幕中请求自定义范围的权限
Identity Server OAuth 2.0 Code Grant - How To Request Permission For Custom Scopes In Consent Screen
我已经实施了 Identity Server,它也能正常工作。
我的一个客户端是 MVC 客户端,在身份验证期间,我想显示同意屏幕。为此,我在客户端配置中添加了 'RequireConsent=true'
现在显示同意屏幕,但问题是,它只显示 'openid' 和 'profile' 范围的权限。
我还有其他几个自定义范围,如 'Api1.read'、'Api1.write',当 Identity Server 为集中屏幕构建视图模式时,它们不会根据授权请求获取。
我做错了什么。
在客户端 AllowedScopes 包含 =
{ 'openid', 'profile', 'Api1.read', 'Api1.write' }
当它到达同意页面时,ApiResources 和 ApiScopes 是空的,但 openid 和配置文件在 IdentityResources 中可用
这就是我在启动时配置 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
中的静态配置
我也不想为此使用 Entity Framework 核心。我更喜欢使用我自己的自定义 table 架构和 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;
}
}
这是一个数据库模式示例
我做错了什么
在您的客户端中,在 AddOpenIdConnect 方法中,您还需要定义您想要访问的范围,例如:
.AddOpenIdConnect(options =>
{
...
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("employee_info");
...
}
我已经实施了 Identity Server,它也能正常工作。
我的一个客户端是 MVC 客户端,在身份验证期间,我想显示同意屏幕。为此,我在客户端配置中添加了 'RequireConsent=true'
现在显示同意屏幕,但问题是,它只显示 'openid' 和 'profile' 范围的权限。
我还有其他几个自定义范围,如 'Api1.read'、'Api1.write',当 Identity Server 为集中屏幕构建视图模式时,它们不会根据授权请求获取。
我做错了什么。 在客户端 AllowedScopes 包含 = { 'openid', 'profile', 'Api1.read', 'Api1.write' }
当它到达同意页面时,ApiResources 和 ApiScopes 是空的,但 openid 和配置文件在 IdentityResources 中可用
这就是我在启动时配置 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
中的静态配置我也不想为此使用 Entity Framework 核心。我更喜欢使用我自己的自定义 table 架构和 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;
}
}
这是一个数据库模式示例
我做错了什么
在您的客户端中,在 AddOpenIdConnect 方法中,您还需要定义您想要访问的范围,例如:
.AddOpenIdConnect(options =>
{
...
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("employee_info");
...
}