Código
O código apresentado é apenas uma seleção do código de exemplo no GitHub.
O pacote ITfoxtec Identity Saml2 é integrado numa aplicação ASP.NET MVC Core Relying Party (RP) através da configuração no Startup e adicionando um Auth Controller com os quatro métodos seguintes. O binding apresentado pode ser alterado conforme necessário dependendo dos requisitos.
É ainda possível definir alguns parâmetros opcionais no Saml2AuthnRequest e no Saml2LogoutRequest. No Saml2AuthnRequest, por exemplo, o ForceAuthn é suportado, o que irá forçar o utilizador a introduzir credenciais mesmo que já exista um contexto SSO no Security Token Service (STS) / Identity Provider (IdP).
Adicionar configuração ao método ConfigureServices no Startup
Configuração usando metadados do 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();
Configuração sem metadados.
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étodo Login no 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étodo AssertionConsumerService no Auth Controller
Após um login bem-sucedido ou falhado, o método ACS recebe a resposta.
[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étodo Logout no 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étodo LoggedOut no Auth Controller
Após um logout bem-sucedido ou falhado, o método LoggedOut recebe a resposta.
[Route("LoggedOut")]
public IActionResult LoggedOut()
{
var httpRequest = Request.ToGenericHttpRequest(validate: true);
httpRequest.Binding.Unbind(httpRequest, new Saml2LogoutResponse(config));
return Redirect(Url.Content("~/"));
}
Método SingleLogout no Auth Controller
Recebe um pedido Single Logout e envia uma resposta.
[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();
}