Reputation: 1753
I have multi tenant application where each tenant can use different IdP to authenticate. Below code correctly redirects to IdP but problem is to get back the response to ACS endpoint.
Key is the Configuration method which configures the paths and their authentication:
[assembly: OwinStartup(typeof(SSOSamlDemoASPNET.App_Start.Startup))]
namespace SSOSamlDemoASPNET.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/client/okta", (appx) =>
{
ConfigureAuthentication(appx, "/client/okta/Saml2", ...);
});
app.Map("/client/azuread", (appx) =>
{
ConfigureAuthentication(appx, "/client/azuread/Saml2", ...);
});
}
private static void ConfigureAuthentication(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieName = "LoggedUser",
CookiePath = "/",
CookieManager = new SystemWebCookieManager(),
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
ConfigureSaml(app, modulePath, audience, issuer, metadataUrl);
}
private static void ConfigureSaml(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
var saml2options = new Saml2AuthenticationOptions(false);
var spOptions = new SPOptions
{
EntityId = new EntityId(audience),
ModulePath = modulePath,
PublicOrigin = new Uri("https://localhost:44340/"),
};
spOptions.Logger = new ConsoleLoggerAdapter();
saml2options.SPOptions = spOptions;
saml2options.IdentityProviders.Add(new IdentityProvider(new EntityId(issuer), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
MetadataLocation = metadataUrl,
LoadMetadata = true,
Binding = Saml2BindingType.HttpPost,
});
app.UseSaml2Authentication(saml2options);
}
}
}
Authenticating against individual IdP is done like this:
authProperties.Dictionary["idp"] = "https://sts.windows.net/xxx/";
authProperties.RedirectUri = "https://localhost:44340/client/azuread/ExternalLoginCallback";
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(authProperties, "Saml2");
When inspecting code of the Sustainsys.Saml2 library (especially Saml2AuthenticationHandler). I found the conditions do not take into account OwinRequest.PathBase and therefore the identity is not coming back to the application.
An example can be (Saml2AuthenticationHandler.Invoke method).
Options.SPOptions.ModulePath = /client/azuread/Saml2
Request.Path = /Saml2/Acs
==> therefore the code inside the condition is not executed.public override async Task<bool> InvokeAsync()
{
var Saml2Path = new PathString(Options.SPOptions.ModulePath);
if (Request.Path.StartsWithSegments(Saml2Path, out PathString remainingPath))
{
if (remainingPath == new PathString("/" + CommandFactory.AcsCommandName))
{
var ticket = (MultipleIdentityAuthenticationTicket)await AuthenticateAsync();
if (ticket.Identities.Any())
{
Context.Authentication.SignIn(ticket.Properties, ticket.Identities.ToArray());
// No need to redirect here. Command result is applied in AuthenticateCoreAsync.
}
else
{
Response.Redirect(ticket.Properties.RedirectUri);
}
return true;
}
Is there any way to change this behavioral? e.g. saml2Options.Notifications to get this working?
Upvotes: 0
Views: 562
Reputation: 69280
That is obviously a bug/lack of feature, but nothing that will be fixed on the Owin module - it's on life support.
The solution for a multi tenancy owin app is to register one Saml2 middleware and add multiple IdentityProviders to that one. The middleware will handle all responses on the same endpoint and use the configuration from the right IdentityProvider based on where the response came from.
Upvotes: 2