dependabot[bot] on nuget
dependabot[bot] on nuget
AcsCommandResultCreated
? Looking at the definitions of CommandResult
and Saml2Response
I don't see a way to get at the identity. I can get the claims useing Saml2Response.GetClaims
but I don't think I can add claims that way.
commandResult.Principal.Claims
builder.AddSaml2(
provider.AuthenticationScheme,
provider.DisplayName,
options =>
{
options.SignInScheme = provider.SignInScheme;
options.SPOptions.EntityId = new EntityId(provider.ServiceProviderEntityId);
options.SPOptions.Logger = adapter;
options.SPOptions.ServiceCertificates.Add(certificate);
options.IdentityProviders.Add(
new IdentityProvider(new EntityId(provider.IdentityProviderEntityId), options.SPOptions)
{
MetadataLocation = provider.MetadataLocation,
LoadMetadata = provider.LoadMetadata,
});
});
KeyNotFoundException: The given key 'Sustainsys.Saml2.Metadata.EntityId' was not present in the dictionary.
AuthenticationProperties
object. Is this the "correct" way to handle a single authentiation scheme with multiple identity providers?
var props = new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(Callback)),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", scheme },
{ "idp", idp }
}
};
SelectIdentityProvider
and GetIdentityProvider
notifications. Anyone can guide me in the right direction would be appreciated. Thank you.
builder.AddSaml2(
saml2Options =>
{
saml2Options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
saml2Options.SPOptions.EntityId = new EntityId(config.Saml2.ServiceProviderEntityId);
saml2Options.SPOptions.ServiceCertificates.Add(certificate);
saml2Options.Notifications.SelectIdentityProvider =
(id, data) => GetProvider(identityProviderStore, id, data, saml2Options, logger);
saml2Options.Notifications.GetIdentityProvider =
(id, data, options) => GetProvider(identityProviderStore, id, data, options, logger);
saml2Options.Notifications.AcsCommandResultCreated =
(commandResult, response) =>
{
if (commandResult.Principal.Identity is ClaimsIdentity identity)
identity.AddClaim(new Claim("in_response_to", response.InResponseTo.Value));
};
});
GetProvider
method looks something like this...
private IdentityProvider GetProvider(IEegIdentityProviderStore identityProviderStore, EntityId id, IDictionary<string,string> data, IOptions options, ILogger logger)
{
Saml2IdentityProvider provider = identityProviderStore.GetSamlProviderByEntityId(id.Id);
if (provider == null)
return null;
idp =
new IdentityProvider(new EntityId(provider.EntityId), options.SPOptions)
{
MetadataLocation = provider.Debug
? provider.DebugMetadataLocation
: provider.MetadataLocation
};
logger.Verbose("Adding Identity Provider: {IdpName}", provider.DisplayName);
if (provider.Debug)
logger.Warning(
"Provider {Provider} is in debug mode, using metadata URL: {MetadataUrl}",
provider.DisplayName,
provider.DebugMetadataLocation);
options.IdentityProviders.Add(idp);
return idp;
}
options.IdentityProviders
collection multiple times. That would also be a problem.
@mlindegarde and @AndersAbel thank you for your input. There are a couple methods there that I don't have a great understanding how they work, in either way here's what I've done adjusting your code to mine. FYI: this project is a legacy Asp.Net Web App (non-MVC).
in my Global.asax
void Application_BeginRequest(object sender, EventArgs e) {
Saml2Config.Initialize();
}
In the Sam2Config
file:
public class Saml2Config {
private static bool _alreadyInitialized;
private static readonly object Lock = new object();
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
public static void Initialize() {
if (_alreadyInitialized) {
return;
}
lock (Lock) {
if (_alreadyInitialized) {
return;
}
spOptions = Options.FromConfiguration.SPOptions;
// Get list of IdentityProviders
var saml2IdentityProvidersRepository = new Saml2IdentityProvidersRepository();
var saml2IdentityProviders = saml2IdentityProvidersRepository.FindByServiceProviderEntityId(spOptions.EntityId.Id, x => x.Saml2BindingType).OrderBy(x => x.OrderPreference);
Options.FromConfiguration.Notifications.SelectIdentityProvider = (id, data) => GetProvider(saml2IdentityProviders, id, data, Options.FromConfiguration, _logger);
Options.FromConfiguration.Notifications.GetIdentityProvider = (id, data, options) => GetProvider(saml2IdentityProviders, id, data, options, _logger);
saml2IdentityProvidersRepository.Dispose();
_alreadyInitialized = true;
}
}
private static IdentityProvider GetProvider(IQueryable<Saml2IdentityProvider> identityProviderStore, EntityId id, IDictionary<string, string> data, IOptions options, ILogger logger) {
var identityProvider = identityProviderStore.FirstOrDefault(x => x.IsActive && x.IdentityProviderEntityId.Equals(id.Id));
if (identityProvider == null) {
return null;
}
var idpEntityId = new EntityId(identityProvider.IdentityProviderEntityId);
var bindingType = EnumHelper.NumToEnum<Sustainsys.Saml2.WebSso.Saml2BindingType>(identityProvider.Saml2BindingType.Value);
var idp = new IdentityProvider(idpEntityId, options.SPOptions) {
MetadataLocation = identityProvider.MetadataLocation,
LoadMetadata = identityProvider.LoadMetadata,
AllowUnsolicitedAuthnResponse = identityProvider.AllowUnsolicitedAuthnResponse,
Binding = bindingType,
};
options.IdentityProviders.Add(idp);
return idp;
}
/External/Challenge?scheme=${scheme}&idp=${entityId}&returnUrl=${returnUrl}
. Your exact route is probably different than mine, but somewhere you should have a route that starts the SAML2 process.
idp
parameter in the query string is where the entity id value comes from.
idp
value is in the AuthenticationProperties.Items
collection:new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(Callback)),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", scheme },
{ "idp", idp }
}
}
idp
value in the Items
collection you should get that value in your GetProvider
method. You can then use that value to look up the data in the database.