Code
Le code présenté n’est qu’une sélection du code d’exemple sur GitHub.
Le package ITfoxtec Identity Saml2 est intégré dans une application ASP.NET MVC Core Relying Party (RP) via la configuration dans Startup et l’ajout d’un Auth Controller avec les quatre méthodes suivantes. Le binding présenté peut être modifié selon les besoins.
Il est également possible de définir des paramètres optionnels sur Saml2AuthnRequest et Saml2LogoutRequest. Sur Saml2AuthnRequest, par exemple, ForceAuthn est pris en charge, ce qui force l’utilisateur à saisir ses identifiants même si un contexte SSO existe déjà sur le Security Token Service (STS) / Identity Provider (IdP).
Ajouter la configuration à la méthode ConfigureServices dans Startup
Configuration à l’aide des métadonnées IdP.
services.Configure<Saml2Configuration>(Configuration.GetSection("Saml2"));
services.Configure<Saml2Configuration>(saml2Configuration =>
{
saml2Configuration.SigningCertificate = CertificateUtil.Load(AppEnvironment.MapToPhysicalFilePath(
Configuration["Saml2:SigningCertificateFile"]), Configuration["Saml2:SigningCertificatePassword"]);
saml2Configuration.AllowedAudienceUris.Add(saml2Configuration.Issuer);
var entityDescriptor = new EntityDescriptor();
entityDescriptor.ReadIdPSsoDescriptorFromUrl(new Uri(Configuration["Saml2:IdPMetadata"]));
if (entityDescriptor.IdPSsoDescriptor != null)
{
saml2Configuration.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;
saml2Configuration.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location;
saml2Configuration.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
}
else
{
throw new Exception("IdPSsoDescriptor not loaded from metadata.");
}
});
services.AddSaml2();
Configuration sans métadonnées.
services.Configure<Saml2Configuration>(Configuration.GetSection("Saml2"));
services.Configure<Saml2Configuration>(saml2Configuration =>
{
saml2Configuration.SigningCertificate = CertificateUtil.Load(AppEnvironment.MapToPhysicalFilePath(
Configuration["Saml2:SigningCertificateFile"]), Configuration["Saml2:SigningCertificatePassword"]);
saml2Configuration.AllowedAudienceUris.Add(saml2Configuration.Issuer);
saml2Configuration.SignatureValidationCertificates.Add(CertificateUtil.Load(AppEnvironment.MapToPhysicalFilePath(
Configuration["Saml2:SignatureValidationCertificateFile"])));
});
services.AddSaml2();
Méthode de connexion dans le Auth Controller
[Route("Login")]
public IActionResult Login(string returnUrl = null)
{
var binding = new Saml2RedirectBinding();
binding.SetRelayStateQuery(new Dictionary<string, string>
{ { relayStateReturnUrl, returnUrl ?? Url.Content("~/") } });
return binding.Bind(new Saml2AuthnRequest(config)).ToActionResult();
}
Méthode AssertionConsumerService dans le Auth Controller
Après une connexion réussie ou échouée, la méthode ACS reçoit la réponse.
[Route("AssertionConsumerService")]
public async Task<IActionResult> AssertionConsumerService()
{
var httpRequest = Request.ToGenericHttpRequest(validate: true);
var saml2AuthnResponse = new Saml2AuthnResponse(config);
httpRequest.Binding.Unbind(httpRequest, saml2AuthnResponse);
await saml2AuthnResponse.CreateSession(HttpContext,
ClaimsTransform: (claimsPrincipal) => ClaimsTransform.Transform(claimsPrincipal));
var returnUrl = httpRequest.Binding.GetRelayStateQuery()[relayStateReturnUrl];
return Redirect(string.IsNullOrWhiteSpace(returnUrl) ? Url.Content("~/") : returnUrl);
}
Méthode de déconnexion dans le Auth Controller
[HttpPost("Logout")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
if (!User.Identity.IsAuthenticated)
{
return Redirect(Url.Content("~/"));
}
var binding = new Saml2PostBinding();
var saml2LogoutRequest = await new Saml2LogoutRequest(config, User).DeleteSession(HttpContext);
return binding.Bind(saml2LogoutRequest).ToActionResult();
}
Méthode LoggedOut dans le Auth Controller
Après une déconnexion réussie ou échouée, la méthode logged out reçoit la réponse.
[Route("LoggedOut")]
public IActionResult LoggedOut()
{
var httpRequest = Request.ToGenericHttpRequest(validate: true);
httpRequest.Binding.Unbind(httpRequest, new Saml2LogoutResponse(config));
return Redirect(Url.Content("~/"));
}
Méthode SingleLogout dans le Auth Controller
Reçoit une requête Single Logout et envoie une réponse.
[Route("SingleLogout")]
public async Task<IActionResult> SingleLogout()
{
Saml2StatusCodes status;
var httpRequest = Request.ToGenericHttpRequest(validate: true);
var logoutRequest = new Saml2LogoutRequest(config, User);
try
{
httpRequest.Binding.Unbind(httpRequest, logoutRequest);
status = Saml2StatusCodes.Success;
await logoutRequest.DeleteSession(HttpContext);
}
catch (Exception exc)
{
// log exception
Debug.WriteLine("SingleLogout error: " + exc.ToString());
status = Saml2StatusCodes.RequestDenied;
}
var responseBinding = new Saml2PostBinding();
responseBinding.RelayState = httpRequest.Binding.RelayState;
var saml2LogoutResponse = new Saml2LogoutResponse(config)
{
InResponseToAsString = logoutRequest.IdAsString,
Status = status,
};
return responseBinding.Bind(saml2LogoutResponse).ToActionResult();
}