AuthenticateResult.Succeeded è false con Okta e Sustainsys.SAML2
Ho un'applicazione .Net Core 2 che sfrutta Sustainsys.Saml2.AspNetCor2 (2.7.0). Il front-end è un'applicazione angolare. L'approccio SAML che sto adottando è basato e molto simile all'approccio adottato in questa implementazione di riferimento:https://github.com/hmacat/Saml2WebAPIAndAngularSpaExample
* Tutto funziona bene con l'IDP di prova (https://stubidp.sustainsys.com).
Ma quando proviamo a eseguire l'integrazione con Okta, la proprietà AuthenticateResult.Succeeded nel metodo di callback (vedi sotto) è sempre falsa, anche se il SAML pubblicato nell'endpoint ASC sembra indicare un'autenticazione riuscita. Non stiamo riscontrando alcun errore. Semplicemente non sta riuscendo.
(Tieni presente che la mia azienda non ha accesso a Okta, che è gestito da una società partner.)
Ecco il codice del server nel controller:
[AllowAnonymous]
[HttpPost, HttpGet]
[Route("api/Security/InitiateSamlSingleSignOn")]
public IActionResult InitiateSamlSingleSignOn(string returnUrl)
{
return new ChallengeResult(
Saml2Defaults.Scheme,
new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(SamlLoginCallback), new { returnUrl })
});
}
[AllowAnonymous]
[HttpPost, HttpGet]
[Route("api/Security/SamlLoginCallback")]
public async Task<IActionResult> SamlLoginCallback(string returnUrl)
{
var authenticateResult = await HttpContext.AuthenticateAsync(ApplicationSamlConstants.External);
if (!authenticateResult.Succeeded)
{
return Unauthorized();
}
// more code below, never reached
}
Ecco uno screenshot di alcuni dei SAML inviati da Okta, acquisiti utilizzando l'estensione di Chrome, SAML-tracer:
Non so come indagare ulteriormente su questo. Qualsiasi aiuto sarebbe molto apprezzato!
Nel metodo ConfigureServices, nel caso sia utile, ho quanto segue (nella parte pertinente):
public void ConfigureServices(IServiceCollection services)
{
// [snip]
if (usingSAML)
{
services.Configure<CookiePolicyOptions>(options =>
{
// SameSiteMode.None is required to support SAML SSO.
options.MinimumSameSitePolicy = SameSiteMode.None;
options.CheckConsentNeeded = context => false;
// Some older browsers don't support SameSiteMode.None.
options.OnAppendCookie = cookieContext => SameSite.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext => SameSite.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
authBuilder = services.AddAuthentication(o =>
{
o.DefaultScheme = ApplicationSamlConstants.Application;
o.DefaultSignInScheme = ApplicationSamlConstants.External;
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
authBuilder.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
// see https://stackoverflow.com/questions/46243697/asp-net-core-persistent-authentication-custom-cookie-authentication
options.ExpireTimeSpan = new System.TimeSpan(365, 0, 0, 0, 0);
options.AccessDeniedPath = new PathString("/login");
options.LoginPath = new PathString("/login");
})
.AddCookie(ApplicationSamlConstants.Application)
.AddCookie(ApplicationSamlConstants.External)
.AddSaml2(options =>
{
options.SPOptions.EntityId = new EntityId(this.Configuration["Saml:SPEntityId"]);
options.IdentityProviders.Add(
new IdentityProvider(
new EntityId(this.Configuration["Saml:IDPEntityId"]), options.SPOptions)
{
MetadataLocation = this.Configuration["Saml:IDPMetaDataBaseUrl"],
LoadMetadata = true,
});
options.SPOptions.ServiceCertificates.Add(new X509Certificate2(this.Configuration["Saml:CertificateFileName"]));
});
}
// [snip]
}
AGGIORNAMENTO: Ho modificato il codice per acquisire più informazioni di registrazione e quello che ho scoperto è che, all'endpoint Saml2 / Acs, l'utente viene autenticato. Nei file di registro, vedo questo:
2020-09-14 09:28:09.307 -05:00 [DBG] Signature validation passed for Saml Response Microsoft.IdentityModel.Tokens.Saml2.Saml2Id
2020-09-14 09:28:09.369 -05:00 [DBG] Extracted SAML assertion id1622894416505593469999142
2020-09-14 09:28:09.385 -05:00 [INF] Successfully processed SAML response Microsoft.IdentityModel.Tokens.Saml2.Saml2Id and authenticated [email protected]
Tuttavia, quando arrivo al metodo SamlLoginCallback, queste informazioni di autenticazione non sono presenti nell'AuthenticateResult ottenuto da questa chiamata:
var authenticateResult = await HttpContext.AuthenticateAsync(ApplicationSamlConstants.External);
Le mie informazioni di registrazione personalizzate per l'oggetto risultato dell'autenticazione hanno questo aspetto:
2020-09-14 09:28:09.432 -05:00 [ERR] SAML Authentication Failure: authenticateResult.Failure (Exception object) is null;
No information was returned for the authentication scheme;
authenticateResult.Principal is null;
authenticateResult.Properties is null.
authenticateResult.Ticket is null.
Cosa potrebbe andare storto?
Risposte
La causa principale qui era in definitiva il risultato di differenze nel caso dell'URL utilizzato da Okta rispetto al nostro codice nella logica di reindirizzamento. Gli URL corrispondevano, ma il caso no. Ciò rendeva i cookie illeggibili dai metodi richiamati in seguito che venivano inviati a un URL diverso, anche se la differenza era solo nell'involucro del percorso. Una volta che ci siamo assicurati che tutti i percorsi corrispondessero esattamente, fino al case, ha iniziato a funzionare.