按照您的示例 Idp 代码创建 SamlResponse 时发出问题 - 在 LoginResponse 方法中
Issue creating SamlResponse when following your example Idp code - within the LoginResponse method
中包含的代码创建了一个 IDP
当我尝试使用以下代码绑定 authNResponse 时出现错误:
var responsebinding = new Saml2PostBinding();
responsebinding.Bind(saml2AuthnResponse).XmlDocument.OuterXml;
这与 PostContent 方法中的代码相同,但我选择直接使用此代码,因为我只需要 SamlResponse。
错误是:
Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenWriteException: 'IDX13129: The SAML2:AttributeStatement must contain at least one SAML2:Attribute.'
具有以下简化的堆栈跟踪:
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteAttributeStatement(XmlWriter writer, Saml2AttributeStatement statement)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteStatement(XmlWriter writer, Saml2Statement statement)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteAssertion(XmlWriter writer, Saml2Assertion assertion)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.WriteToken(XmlWriter writer, SecurityToken securityToken)
at ITfoxtec.Identity.Saml2.Tokens.Saml2ResponseSecurityTokenHandler.WriteToken(SecurityToken token)
at ITfoxtec.Identity.Saml2.Saml2AuthnResponse.ToXml()
我几乎完全使用了您的示例代码,请问其中是否存在问题,或者我遗漏了什么?
非常感谢
也许您遗漏了向令牌添加声明和创建令牌的部分?
saml2AuthnResponse.SessionIndex = sessionIndex;
var claimsIdentity = new ClaimsIdentity(claims);
saml2AuthnResponse.NameId = new Saml2NameIdentifier(claimsIdentity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).Single(), NameIdentifierFormats.Persistent);
saml2AuthnResponse.ClaimsIdentity = claimsIdentity;
var token = saml2AuthnResponse.CreateSecurityToken(relyingParty.Issuer, subjectConfirmationLifetime: 5, issuedTokenLifetime: 60);
我发现您需要 ClaimTypes.NameIdentifier 和 ClaimTypes.Email 声明才能成功生成令牌.
这里有更深入的分析和两种可能solutions/workarounds。
情况:为只有一个声明的 ClaimsIdentity 创建 ITfoxtec.Identity.Saml2.Saml2AuthnResponse:nameidentifier。
相关代码片段(不完整,只有相关部分,但 ITFoxttec 示例具有完整代码)
var response = new Saml2AuthnResponse(config);
response.ClaimsIdentity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "someone@somewhere.com") });
response.NameId = new Saml2NameIdentifier(....etc...);
var token = response.CreateSecurityToken(appliesToAddress);
//so far all is well, but the problem has been sneakily introduced!
//which is why the next line will give the error: Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenWriteException: 'IDX13129: The SAML2:AttributeStatement must contain at least one SAML2:Attribute
return binding.Bind(response).ToActionResult();
解释:
ITfoxtec 代码:创建令牌时,nameidentifier 声明已从声明中删除。这是有道理的,因为它应该在 NameId 属性 中。其余声明在 SecurityTokenDescriptor 中设置为 Subject,该 SecurityTokenDescriptor 被提供给 Saml2SecurityTokenHandler,这是 Microsoft 代码。
var tokenDescriptor = new SecurityTokenDescriptor();
tokenDescriptor.Subject = new ClaimsIdentity(claims.Where(c => c.Type != ClaimTypes.NameIdentifier));
此令牌描述符中的声明最终作为生成的 Saml2SecurityToken 中的 AttributeStatement 中的属性(通过 Saml2SecurityTokenHandler.CreateToken(tokendescriptor) 调用)。
不幸的是,如果 nameidentifier 是您拥有的唯一声明,那么您最终会得到一个没有 Attributes 的 AttributeStatement。随后 运行 进入问题,当 binding.Bind(响应)在肠子深处做它的 XML 事情..
除非你应该总是有一个 AttributeStatement,否则它在我看来就像 Microsoft.IdentityModel.Tokens.Saml 库中的错误/边缘情况。
有两种方案可以解决:
防止以无声明结束:只需向身份添加另一个声明,不必是电子邮件,可以是任何内容:
response.ClaimsIdentity.AddClaim(新声明("x", "y"))
在 CreateSecurityToken 调用之后但在调用 Bind 之前,检查 AttributeStatement 是否为空,如果是,则将其删除。一个快速而肮脏的例子:
var x = (Saml2AttributeStatement)token.Assertion.Statements.FirstOrDefault(a => a.GetType() == typeof(Saml2AttributeStatement));
如果 (x?.Attributes.Count == 0)
{
token.Assertion.Statements.Remove(x);
}
就个人而言,我更喜欢选项 1,因为它通常使用起来更安全且代码更少。另外,我确信总是可以 'something' 进一步归因于身份...
当我尝试使用以下代码绑定 authNResponse 时出现错误:
var responsebinding = new Saml2PostBinding();
responsebinding.Bind(saml2AuthnResponse).XmlDocument.OuterXml;
这与 PostContent 方法中的代码相同,但我选择直接使用此代码,因为我只需要 SamlResponse。
错误是:
Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenWriteException: 'IDX13129: The SAML2:AttributeStatement must contain at least one SAML2:Attribute.'
具有以下简化的堆栈跟踪:
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteAttributeStatement(XmlWriter writer, Saml2AttributeStatement statement)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteStatement(XmlWriter writer, Saml2Statement statement)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteAssertion(XmlWriter writer, Saml2Assertion assertion)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.WriteToken(XmlWriter writer, SecurityToken securityToken)
at ITfoxtec.Identity.Saml2.Tokens.Saml2ResponseSecurityTokenHandler.WriteToken(SecurityToken token)
at ITfoxtec.Identity.Saml2.Saml2AuthnResponse.ToXml()
我几乎完全使用了您的示例代码,请问其中是否存在问题,或者我遗漏了什么?
非常感谢
也许您遗漏了向令牌添加声明和创建令牌的部分?
saml2AuthnResponse.SessionIndex = sessionIndex;
var claimsIdentity = new ClaimsIdentity(claims);
saml2AuthnResponse.NameId = new Saml2NameIdentifier(claimsIdentity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).Single(), NameIdentifierFormats.Persistent);
saml2AuthnResponse.ClaimsIdentity = claimsIdentity;
var token = saml2AuthnResponse.CreateSecurityToken(relyingParty.Issuer, subjectConfirmationLifetime: 5, issuedTokenLifetime: 60);
我发现您需要 ClaimTypes.NameIdentifier 和 ClaimTypes.Email 声明才能成功生成令牌.
这里有更深入的分析和两种可能solutions/workarounds。
情况:为只有一个声明的 ClaimsIdentity 创建 ITfoxtec.Identity.Saml2.Saml2AuthnResponse:nameidentifier。 相关代码片段(不完整,只有相关部分,但 ITFoxttec 示例具有完整代码)
var response = new Saml2AuthnResponse(config);
response.ClaimsIdentity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "someone@somewhere.com") });
response.NameId = new Saml2NameIdentifier(....etc...);
var token = response.CreateSecurityToken(appliesToAddress);
//so far all is well, but the problem has been sneakily introduced!
//which is why the next line will give the error: Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenWriteException: 'IDX13129: The SAML2:AttributeStatement must contain at least one SAML2:Attribute
return binding.Bind(response).ToActionResult();
解释:
ITfoxtec 代码:创建令牌时,nameidentifier 声明已从声明中删除。这是有道理的,因为它应该在 NameId 属性 中。其余声明在 SecurityTokenDescriptor 中设置为 Subject,该 SecurityTokenDescriptor 被提供给 Saml2SecurityTokenHandler,这是 Microsoft 代码。
var tokenDescriptor = new SecurityTokenDescriptor();
tokenDescriptor.Subject = new ClaimsIdentity(claims.Where(c => c.Type != ClaimTypes.NameIdentifier));
此令牌描述符中的声明最终作为生成的 Saml2SecurityToken 中的 AttributeStatement 中的属性(通过 Saml2SecurityTokenHandler.CreateToken(tokendescriptor) 调用)。
不幸的是,如果 nameidentifier 是您拥有的唯一声明,那么您最终会得到一个没有 Attributes 的 AttributeStatement。随后 运行 进入问题,当 binding.Bind(响应)在肠子深处做它的 XML 事情.. 除非你应该总是有一个 AttributeStatement,否则它在我看来就像 Microsoft.IdentityModel.Tokens.Saml 库中的错误/边缘情况。
有两种方案可以解决:
防止以无声明结束:只需向身份添加另一个声明,不必是电子邮件,可以是任何内容:
response.ClaimsIdentity.AddClaim(新声明("x", "y"))
在 CreateSecurityToken 调用之后但在调用 Bind 之前,检查 AttributeStatement 是否为空,如果是,则将其删除。一个快速而肮脏的例子:
var x = (Saml2AttributeStatement)token.Assertion.Statements.FirstOrDefault(a => a.GetType() == typeof(Saml2AttributeStatement)); 如果 (x?.Attributes.Count == 0) { token.Assertion.Statements.Remove(x); }
就个人而言,我更喜欢选项 1,因为它通常使用起来更安全且代码更少。另外,我确信总是可以 'something' 进一步归因于身份...