首页 > 解决方案 > 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;
    }
}

这是数据库模式的示例 在此处输入图像描述

我究竟做错了什么

标签: c#asp.net-coreoauth-2.0identityserver4

解决方案


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");
            ...
         }

推荐阅读