英文:
Saml2 Sustainsys return 404 when call back from iDP on route Saml2/Acs
问题
我有以下属性:
IDP: Azure
服务提供者: .Net core 6
客户端: Vue 3
以及以下代码:
var samlConfiguration = GetConfiguration<SamlConfiguration>(configuration);
serviceCollection.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = Saml2Defaults.Scheme;
})
.AddCookie()
.AddSaml2(Saml2Defaults.Scheme, options =>
{
options.SPOptions.EntityId = new EntityId(samlConfiguration.ServiceProviderEntityId);
options.SPOptions.ReturnUrl = new Uri(samlConfiguration.AssertionConsumerServiceUrl);
options.SPOptions.PublicOrigin = new Uri(samlConfiguration.PublishOrigin);
var idp = new IdentityProvider(new EntityId(samlConfiguration.IdentityProviderEntityId),
options.SPOptions)
{
LoadMetadata = true,
AllowUnsolicitedAuthnResponse = true,
MetadataLocation = samlConfiguration.MetadataLocation
};
options.IdentityProviders.Add(idp);
});
}
[Route("[controller]")]
[ApiController]
public class Saml2Controller : ControllerBase
{
private readonly SamlConfiguration _samlConfiguration;
private readonly ILogger<Saml2Controller> _logger;
public Saml2Controller(IOptions<SamlConfiguration> options, ILogger<Saml2Controller> logger)
{
_logger = logger;
_samlConfiguration = options.Value;
}
[HttpGet]
public IActionResult Initiate()
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = _samlConfiguration.RedirectUri,
};
return Challenge(authenticationProperties, Saml2Defaults.Scheme);
}
[HttpPost("Acs")]
public async Task AssertionConsumerService()
{
try
{
_logger.LogInformation("-------begin AssertionConsumerService-------");
var result = await HttpContext.AuthenticateAsync(Saml2Defaults.Scheme);
if (!result.Succeeded)
{
throw new Exception("SAML authentication failed.");
}
_logger.LogInformation("-------set claims-------");
var claims = new List<Claim>();
claims.AddRange(result.Principal.Claims);
_logger.LogInformation("-------create ClaimsIdentity-------");
var identity = new ClaimsIdentity(claims, "saml");
var principal = new ClaimsPrincipal(identity);
_logger.LogInformation("-------begin SignIn-------");
await HttpContext.SignInAsync(principal);
_logger.LogInformation("-------end SignIn-------");
}
catch (Exception exception)
{
_logger.LogError("in {@className}\n--Exception: {@exception}\n--StackTrace: {@stackTrace}",
nameof(Saml2Controller), exception.Message, exception.StackTrace);
}
}
[HttpGet("logout")]
public IActionResult Logout()
{
HttpContext.SignOutAsync(Saml2Defaults.Scheme).Wait();
return Redirect("/");
}
}
SAML配置如下:
"SamlConfiguration": {
"ServiceProviderEntityId": "https://sp.example.com/Saml2",
"IdentityProviderEntityId": "https://sts.windows.net/{tenantId}/",
"SingleSignOnServiceUrl": "https://login.microsoftonline.com/{tenantId}/Saml2",
"AssertionConsumerServiceUrl": "https://sp.example.com/Saml2/Acs",
"MetadataLocation": "https://login.microsoftonline.com/{tenantId}/federationmetadata/2007-06/federationmetadata.xml?appid={appId}",
"PublishOrigin": "https://sp.example.com",
"RedirectUri": "https://vue3.example.com"
}
当客户端到达401时,将重定向到 "https://sp.example.com/Saml2",然后服务提供者重定向到iDP,登录后返回到 "https://sp.example.com/Saml2/Acs",最后我们需要将SAML响应传递给客户端,客户端处理该响应。
我的问题是路由 "Saml2/Acs" 保留给了Sustainsys.saml2库,因为我们在通过Postman或手动调用该路由时("Saml2/Acs"),会收到404错误。如何解决这个问题呢?
英文:
I have the following property:
IDP: Azure
Service Provider: .Net core 6
Client: Vue 3
and following code:
var samlConfiguration = GetConfiguration<SamlConfiguration>(configuration);
serviceCollection.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = Saml2Defaults.Scheme;
})
.AddCookie()
.AddSaml2(Saml2Defaults.Scheme, options =>
{
options.SPOptions.EntityId = new EntityId(samlConfiguration.ServiceProviderEntityId);
options.SPOptions.ReturnUrl = new Uri(samlConfiguration.AssertionConsumerServiceUrl);
options.SPOptions.PublicOrigin = new Uri(samlConfiguration.PublishOrigin);
var idp = new IdentityProvider(new EntityId(samlConfiguration.IdentityProviderEntityId),
options.SPOptions)
{
LoadMetadata = true,
AllowUnsolicitedAuthnResponse = true,
MetadataLocation = samlConfiguration.MetadataLocation
};
options.IdentityProviders.Add(idp);
});
[Route("[controller]")]
[ApiController]
public class Saml2Controller : ControllerBase
{
private readonly SamlConfiguration _samlConfiguration;
private readonly ILogger<Saml2Controller> _logger;
public Saml2Controller(IOptions<SamlConfiguration> options, ILogger<Saml2Controller> logger)
{
_logger = logger;
_samlConfiguration = options.Value;
}
[HttpGet]
public IActionResult Initiate()
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = _samlConfiguration.RedirectUri,
};
return Challenge(authenticationProperties, Saml2Defaults.Scheme);
}
[HttpPost("Acs")]
public async Task AssertionConsumerService()
{
try
{
_logger.LogInformation("-------begin AssertionConsumerService-------");
var result = await HttpContext.AuthenticateAsync(Saml2Defaults.Scheme);
if (!result.Succeeded)
{
throw new Exception("SAML authentication failed.");
}
_logger.LogInformation("-------set claims-------");
var claims = new List<Claim>();
claims.AddRange(result.Principal.Claims);
_logger.LogInformation("-------create ClaimsIdentity-------");
var identity = new ClaimsIdentity(claims, "saml");
var principal = new ClaimsPrincipal(identity);
_logger.LogInformation("-------begin SignIn-------");
await HttpContext.SignInAsync(principal);
_logger.LogInformation("-------end SignIn-------");
}
catch (Exception exception)
{
_logger.LogError("in {@className}\n--Exception: {@exception}\n--StackTrace: {@stackTrace}",
nameof(Saml2Controller), exception.Message, exception.StackTrace);
}
}
[HttpGet("logout")]
public IActionResult Logout()
{
HttpContext.SignOutAsync(Saml2Defaults.Scheme).Wait();
return Redirect("/");
}
}
The Saml Configuration is as follows:
"SamlConfiguration": {
"ServiceProviderEntityId": "https://sp.example.com/Saml2",
"IdentityProviderEntityId": "https://sts.windows.net/{tenantId}/",
"SingleSignOnServiceUrl": "https://login.microsoftonline.com/{tenantId}/Saml2",
"AssertionConsumerServiceUrl": "https://sp.example.com/Saml2/Acs",
"MetadataLocation": "https://login.microsoftonline.com/{tenantId}/federationmetadata/2007-06/federationmetadata.xml?appid={appId}",
"PublishOrigin": "https://sp.example.com",
"RedirectUri": "https://vue3.example.com"
}
When client reach to 401 then redirect to "https://sp.example.com/Saml2" and then Service Provider redirect to iDP and after login returns to "https://sp.example.com/Saml2/Acs" and finally we need to pass the Saml Response to Client and Client prosses that response.
My Problem is route "Saml2/Acs" is reserved for Sustainsys.saml2 library because we get the 404 Error when we call that route ("Saml2/Acs") via Postman or manually.
What can be do to solve this issue?
答案1
得分: 0
你的设计有误。服务器端应用程序处理Saml响应并将生成的身份信息存储在Cookie中(通过调用配置的身份验证方案上的SignInAsync)。对于JavaScript应用程序的建议是使用后端前端模式,在该模式下会话在服务器端处理,详见https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps。
英文:
Your design is wrong. The server side application processes the Saml response and stores the resulting identity in a cookie (by calling SignInAsync on the configured authentication scheme). The recommendation for javascript applications is to use a backend for frontend pattern where the session is handled on the server side, see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论