IdentityServer – 配置 Api 资源

huangapple go评论71阅读模式
英文:

IdentityServer - Configuring ApiResources

问题

以下是您要翻译的内容:

所以,我正在按照最新的IdentityServer/Duende官方资源中的快速入门进行操作。

从我的理解来看,Api资源实际上是api作用域和身份作用域的逻辑分组。您可以在Api资源"api"中包装3个Api作用域"apiscope1","apiscope2","apiscope3"。

在设置客户端时,您需要在范围中指定"api",这将自动为您提供apiscope1、apiscope2和apiscope3。

  1. 这是正确的吗?

我有两个Api:MyApi和IdentityServerApi。

MyApi的配置如下:

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    var idsvrConfig = builder.Configuration.GetSection("IdentityServer").Get<IdentityServerConfiguration>();

    options.Authority = idsvrConfig.Authority;
    options.ClientId = idsvrConfig.ClientId;
    options.ClientSecret = idsvrConfig.ClientSecret;
    options.ResponseType = idsvrConfig.ResponseType;
    options.Scope.Clear();
    //options.Scope.AddRange(idsvrConfig.Scopes);
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("verification");
    options.Scope.Add("apiResource"); //<-------当指定此范围时不起作用!!
    // 其他配置选项...
});

现在是Identity Server API的配置:

var apiResources = new List<ApiResource>()
{
    new ApiResource()
    {
        Name = "apiResource",
        DisplayName ="ApiResource",
        Scopes = new string[] { "test" }
    }
};

var apiScopes = new List<ApiScope>()
{
    new ApiScope()
    {
        Name = "test",
    }
};

var identityResources = new List<IdentityResource>
{
    new IdentityResources.OpenId(),
    new IdentityResources.Profile(),
    new IdentityResource()
    {
        Name = "verification",
        UserClaims = new List<string>
        {
            JwtClaimTypes.Email,
            JwtClaimTypes.EmailVerified
        }
    }
};

var clients = new List<Client>
{
    new Client
    {
        ClientId = "api",
        ClientSecrets = { new Secret(){
            Value = "supersecretpass"
        }},

        AllowedGrantTypes = GrantTypes.Code,

        // 其他客户端配置...
    }
};

// 添加Identity Server服务
builder.Services.AddIdentityServer()
    .AddInMemoryClients(clients)
    .AddInMemoryIdentityResources(identityResources)
    .AddInMemoryApiScopes(apiScopes)
    .AddInMemoryApiResources(apiResources)
    .AddTestUsers(TestUsers.Users);
  1. 当我在MyApi中添加"apiResource"范围时,为什么会出现无效范围屏幕错误,但当我从客户端请求的范围中删除它时,完整的登录/注销流程可以正常工作?

任何帮助/见解都将不胜感激。

请让我知道如果您需要更多的翻译。

英文:

So I was following the quick starts from the latest IdentityServer/Duende official resources.

From what I understood, An Api Resource is essentially logical grouping of apiscopes and identityscopes.
You can Have 3 ApiScopes "apiscope1", "apiscope2","apiscope3", wrapped in an ApiResource "api".

When setting the client, you need to specify "api" in scopes, which will automatically give you apiscope1, apiscope2, apiscope3.

1. Is this correct?

I have two Apis:
MyApi and IdentityServerApi.

MyApi is configured as follows:

builder.Services.AddAuthentication(options =&gt;
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =&gt;
    {
        var idsvrConfig = builder.Configuration.GetSection(&quot;IdentityServer&quot;).Get&lt;IdentityServerConfiguration&gt;();

        options.Authority = idsvrConfig.Authority;
        options.ClientId = idsvrConfig.ClientId;
        options.ClientSecret = idsvrConfig.ClientSecret;
        options.ResponseType = idsvrConfig.ResponseType;
        options.Scope.Clear();
        //options.Scope.AddRange(idsvrConfig.Scopes);
        options.Scope.Add(&quot;openid&quot;);
        options.Scope.Add(&quot;profile&quot;);
        options.Scope.Add(&quot;verification&quot;);
        options.Scope.Add(&quot;apiResource&quot;); //&lt;-------Doesn&#39;t work when this is specified!!

        options.GetClaimsFromUserInfoEndpoint = idsvrConfig.GetClaimsFromUserInfoEndpoint;
        options.SaveTokens = idsvrConfig.SaveTokens;
        foreach (var claims in idsvrConfig.ClaimActionsMapJsonKey)
        {
            options.ClaimActions.MapJsonKey(claims.Key, claims.Value);
        }
    });

Now for the Identity Server API Configuration:

var apiResources = new List&lt;ApiResource&gt;()
{
    new ApiResource()
    {
        Name = &quot;apiResource&quot;,
        DisplayName =&quot;ApiResource&quot;,
        Scopes = new string[] {&quot;test&quot; }
    }
};


var apiScopes = new List&lt;ApiScope&gt;()
{
    new ApiScope()
    {
            Name = &quot;test&quot;,
    }
};

var identityResources = new List&lt;IdentityResource&gt;
{
    new IdentityResources.OpenId(),
    new IdentityResources.Profile(),
    new IdentityResource()
    {
        Name = &quot;verification&quot;,
        UserClaims = new List&lt;string&gt;
        {
            JwtClaimTypes.Email,
            JwtClaimTypes.EmailVerified
        }
    }
};


var clients = new List&lt;Client&gt;
{
    // interactive ASP.NET Core Web App
    new Client
    {
        ClientId = &quot;api&quot;,
        ClientSecrets = { new Secret(){
            Value = &quot;supersecretpass&quot;
        }},

        AllowedGrantTypes = GrantTypes.Code,

        // where to redirect after login
        RedirectUris = { &quot;https://localhost:44330/signin-oidc&quot; },

        // where to redirect after logout
        PostLogoutRedirectUris = { &quot;https://localhost:44330/signout-callback-oidc&quot; },

        AllowedScopes = new List&lt;string&gt;
        {
            IdentityServerConstants.StandardScopes.OpenId,
            IdentityServerConstants.StandardScopes.Profile,
            &quot;verification&quot;,
            &quot;apiResource&quot;,
            &quot;test&quot;
        }
    }
};


// Add Identity Server services
builder.Services.AddIdentityServer()
    .AddInMemoryClients(clients)
    .AddInMemoryIdentityResources(identityResources)
    .AddInMemoryApiScopes(apiScopes)
    .AddInMemoryApiResources(apiResources)
    //.AddInMemoryClients(builder.Configuration.GetSection(&quot;IdentityServer:Clients&quot;))
    //.AddInMemoryIdentityResources(builder.Configuration.GetSection(&quot;IdentityServer:IdentityResources&quot;))
    //.AddInMemoryApiScopes(builder.Configuration.GetSection(&quot;IdentityServer:ApiScopes&quot;))
    //.AddInMemoryApiResources(builder.Configuration.GetSection(&quot;IdentityServer:ApiResources&quot;))
    .AddTestUsers(TestUsers.Users);

2. Why is it that when on MyApi I add the scope "apiResource", I get an invalid scope screen error but when I remove it from the scopes requested by the client the full login/logout flow works?

Any help/insight is appreciated.

Just in case the versions make any difference, I'm running the following packages:

Both Api are running .NET 7

For MyApi: Microsoft.AspNetCore.Authentication.OpenIdConnect 7.0.5

For Idsvr: Duende.IdentityServer 6.2.3

答案1

得分: 2

你在客户端中设置了包含apiResource字段的Scope,但没有在服务器中配置它,导致"Invalid scope"错误,因为Scope无法匹配。

你需要在IdentityServerApiClientAllowedScopes中指定apiResource

new Client
{
    ClientId = "api",
    ClientSecrets = { new Secret(){
        Value = "supersecretpass"
    }},

    AllowedGrantTypes = GrantTypes.Code,

    // where to redirect after login
    RedirectUris = { "https://localhost:44330/signin-oidc" },

    // where to redirect after logout
    PostLogoutRedirectUris = { "https://localhost:44330/signout-callback-oidc" },

    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "verification",
        "apiResource"
    }
}

然后,在MyApi中添加apiResource作用域,你应该能够成功运行:

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "<idsvr-url>";

    options.ClientId = "api";
    options.ClientSecret = "supersecretpass";
    options.ResponseType = "code";

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("verification");
    options.Scope.Add("apiResource");
    options.GetClaimsFromUserInfoEndpoint = true;

    options.SaveTokens = true;
});

有关ApiResourceApiScope的更多详细信息,你可以参考此链接

更新:

AllowedScopes中添加的作用域应该是已定义的。你只在apiScopes中定义了test,而没有定义apiResource

var apiScopes = new List<ApiScope>()
{
    new ApiScope()
    {
        Name = "test",
    },
    // 添加这个
    new ApiScope
    {
        Name = "apiResource"
    },
};

关于"Aud"声明,你不需要手动配置它。当你添加一个ApiResource并指定相应的Scope时,客户端使用这个Scope进行请求时,获得的Token将默认具有"Aud"声明。

例如,我添加了两个ApiResources并指定了相应的Scope

new List<ApiResource>
{
    new ApiResource()
    {
        Name = "apiResource",
        DisplayName ="ApiResource",
        Scopes = new string[] {"test" }
    },
    new ApiResource()
    { 
        Name = "paymentApi",
        DisplayName = "PaymentApi",
        Scopes= new string[] {"test2"}
    }
}

然后在ApiScope中添加相应的Scope

new List<ApiScope>
{         
    new ApiScope()
    {
        Name = "test"
    },
    new ApiScope
    {
        Name = "apiResource"
    },
    new ApiScope
    { 
        Name = "test2"
    }
}

IdentityServerApi中配置Client(我不确定你的ClientSecrets是否可以验证,因为我根据官方文档进行了配置):

new Client
{
    ClientId = "web",
    ClientSecrets = { new Secret("secret".Sha256()) },

    AllowedGrantTypes = GrantTypes.Code,
            
    // where to redirect to after login
    RedirectUris = { "https://localhost:5002/signin-oidc" },

    // where to redirect to after logout
    PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
    AllowedScopes = new List<string>
     {        
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "apiResource",
        "test",
        "test2"
     }
}

ClientApi中:

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "https://localhost:5001";

    options.ClientId = "web";
    options.ClientSecret = "secret";
    options.ResponseType = "code";

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("apiResource");
    options.Scope.Add("test");
    options.GetClaimsFromUserInfoEndpoint = true;

    options.SaveTokens = true;
});

成功登录后,我可以看到.Token.access_token,这是根据官方文档所建立的示例:

IdentityServer – 配置 Api 资源

将这个access_token复制并粘贴到jwt.io中,你可以看到你的ApiResource包含在aud中:

IdentityServer – 配置 Api 资源

当我在ClientApi中添加一行:

options.Scope.Add("test2");

你可以看到"apiResource"和"paymentApi"都包含在"aud"中:

IdentityServer – 配置 Api 资源

英文:

You set the Scope containing the apiResource field in the client, but did not configure it in the server, Invalid scope is caused because the Scope cannot match.

You need to specify apiResource in AllowedScopes of Client in IdentityServerApi:

new Client
{
    ClientId = &quot;api&quot;,
    ClientSecrets = { new Secret(){
        Value = &quot;supersecretpass&quot;
    }},

    AllowedGrantTypes = GrantTypes.Code,

    // where to redirect after login
    RedirectUris = { &quot;https://localhost:44330/signin-oidc&quot; },

    // where to redirect after logout
    PostLogoutRedirectUris = { &quot;https://localhost:44330/signout-callback-oidc&quot; },

    AllowedScopes = new List&lt;string&gt;
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        &quot;verification&quot;,
        &quot;apiResource&quot;
    }
}

Then you add the scope apiResource in MyApi, you should be able to successfully run.

builder.Services.AddAuthentication(options =&gt;
{
    options.DefaultScheme = &quot;Cookies&quot;;
    options.DefaultChallengeScheme = &quot;oidc&quot;;
})
.AddCookie(&quot;Cookies&quot;)
.AddOpenIdConnect(&quot;oidc&quot;, options =&gt;
{
    options.Authority = &quot;&lt;idsvr-url&gt;&quot;;

    options.ClientId = &quot;api&quot;;
    options.ClientSecret = &quot;supersecretpass&quot;;
    options.ResponseType = &quot;code&quot;;

    options.Scope.Clear();
    options.Scope.Add(&quot;openid&quot;);
    options.Scope.Add(&quot;profile&quot;);
    options.Scope.Add(&quot;verification&quot;);
    options.Scope.Add(&quot;apiResource&quot;);
    options.GetClaimsFromUserInfoEndpoint = true;

    options.SaveTokens = true;
});

For more details about ApiResource and ApiScope, you can refer to this link.

Update:

The scopes added in AllowedScopes should be defined. You only defined test in apiScopes, not apiResource.

var apiScopes = new List&lt;ApiScope&gt;()
{
    new ApiScope()
    {
        Name = &quot;test&quot;,
    },
    //add this
    new ApiScope
    {
        Name = &quot;apiResource&quot;
    },
};

Regarding the "Aud" claim, you don't need to manually configure it. When you add an ApiResource and specify the corresponding Scope, when the client uses this Scope to make a request, the obtained Token will have the Aud claim by default.

For example, I added two ApiResources and specified the corresponding Scope:

new List&lt;ApiResource&gt;
{
    new ApiResource()
    {
        Name = &quot;apiResource&quot;,
        DisplayName =&quot;ApiResource&quot;,
        Scopes = new string[] {&quot;test&quot; }
    },
    new ApiResource()
    { 
        Name = &quot;paymentApi&quot;,
        DisplayName = &quot;PaymentApi&quot;,
        Scopes= new string[] {&quot;test2&quot;}
    }
};

Then add the corresponding Scope in ApiScope:

new List&lt;ApiScope&gt;
{         
    new ApiScope()
    {
        Name = &quot;test&quot;
    },
    new ApiScope
    {
        Name = &quot;apiResource&quot;
    },
    new ApiScope
    { 
        Name = &quot;test2&quot;
    }
};

Configure Client in IdentityServerApi(I'm not sure whether your ClientSecrets can be verified, because I configured it according to the official document):

new Client
{
    ClientId = &quot;web&quot;,
    ClientSecrets = { new Secret(&quot;secret&quot;.Sha256()) },

    AllowedGrantTypes = GrantTypes.Code,
            
    // where to redirect to after login
    RedirectUris = { &quot;https://localhost:5002/signin-oidc&quot; },

    // where to redirect to after logout
    PostLogoutRedirectUris = { &quot;https://localhost:5002/signout-callback-oidc&quot; },
    AllowedScopes = new List&lt;string&gt;
     {        
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        &quot;apiResource&quot;,
        &quot;test&quot;,
        &quot;test2&quot;
     }
}

In ClientApi:

builder.Services.AddAuthentication(options =&gt;
{
    options.DefaultScheme = &quot;Cookies&quot;;
    options.DefaultChallengeScheme = &quot;oidc&quot;;
})
.AddCookie(&quot;Cookies&quot;)
.AddOpenIdConnect(&quot;oidc&quot;, options =&gt;
{
    options.Authority = &quot;https://localhost:5001&quot;;

    options.ClientId = &quot;web&quot;;
    options.ClientSecret = &quot;secret&quot;;
    options.ResponseType = &quot;code&quot;;

    options.Scope.Clear();
    options.Scope.Add(&quot;openid&quot;);
    options.Scope.Add(&quot;profile&quot;);
    options.Scope.Add(&quot;apiResource&quot;);
    options.Scope.Add(&quot;test&quot;);
    options.GetClaimsFromUserInfoEndpoint = true;

    options.SaveTokens = true;
});

I can see .Token.access_token after successfully logging in with the example built by referring to the official document:
IdentityServer – 配置 Api 资源

Copy this access_token and paste in jwt.io, you can see that your ApiResource is included in the aud:
IdentityServer – 配置 Api 资源

When I add a line in ClientApi:

options.Scope.Add(&quot;test2&quot;);

You can see both apiResource and paymentApi are included in aud:
IdentityServer – 配置 Api 资源

huangapple
  • 本文由 发表于 2023年5月8日 00:55:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76195204.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定