ASP.NET OWIN - Adding WsFederation at runtime - c#

I'd like to add support for administrators to add a trusted federation to my site. I'm trying to do this in my controller without having to restart the web app using:
app.Map("/Account", configuration =>
{
var provider = new WsFederationAuthenticationOptions
{
AuthenticationType = organizationModel.ADFS_Domain,
MetadataAddress = organizationModel.ADFS_MetadataAddress,
BackchannelCertificateValidator = null,
Wtrealm = organizationModel.ADFS_Realm,
Wreply = serveraddress + " /Account/" + Guid.NewGuid().ToString(),
};
configuration.UseWsFederationAuthentication(provider);
});
The code above executes without errors, but the provider is not added to the Owin context. Calling...
HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes()
does not include the new option... :(
Just to clarify, I can do the same thing in my Startup.Auth.cs, but again, I don't want to restart the host...
Any ideas?

Related

Azure AD authentication loop on IIS

I have a weird issue, everytime I restart my IIS server or service (i.e. IISreset) my application goes into an almost infinite login loop. After about 15 minutes it does back to normal.
I'm using the default Visual studio Blazor server template with Azure AD module, I have a feeling it's to do with a server side token cache but I'm unsure. I have the same config running on an Azure Web app and I have no issues, here's my authentication from my Program.cs:
var builder = WebApplication.CreateBuilder(args);
var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ') ?? builder.Configuration["MicrosoftGraph:Scopes"]?.Split(' ');
// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
.AddInMemoryTokenCaches()
.AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
.AddDistributedTokenCaches();
IConfigurationSection SQL = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build().GetSection("SQL");
SqlConnectionStringBuilder SQLbuilder = new()
{
DataSource = SQL["DataSource"],
UserID = SQL["UserID"],
Password = SQL["Password"],
InitialCatalog = SQL["InitialCatalog"]
};
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = SQLbuilder.ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
});
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
Any ideas on what I'm missing?

Sustainsys.Saml2 - Saml2/Acs endpoint returns Error 500 when processing SSO

I've created a C# ASP .Net Core 6.0 application, and trying to implement SSO with Azure AD using Sustainsys.Saml2, specifically with the Sustainsys.Saml2.AspNetCore2 package. Having tested the implementation on my development machine with localhost, I can see it works as expected and authenticates the user, populates the Identity model, and redirects to correct URL.
However, when deployed into the test environment, using a dockerized version, the behaviour changes. When triggering SSO, the user is authenticated successfully in Azure, but when returning to the app, it returns an Error 500 at the Saml2/Acs endpoint. Reviewing the logs show no indication of any errors, and instead report successful authentication for the user.
The Program.cs configuration:
builder.Services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = "Saml2";
})
.AddSaml2(options =>
{
var logger = new LoggerFactory();
options.SPOptions.Logger = new AspNetCoreLoggerAdapter(logger.CreateLogger<Saml2Handler>());
options.SPOptions.EntityId = new EntityId(AppConfig.Saml_EntityID);
options.IdentityProviders.Add(
new IdentityProvider(new EntityId(AppConfig.Saml_AzureID), options.SPOptions)
{
Binding = Saml2BindingType.HttpRedirect,
LoadMetadata = true,
MetadataLocation = AppConfig.Saml_Metadata,
DisableOutboundLogoutRequests = false,
AllowUnsolicitedAuthnResponse = true
});
options.SPOptions.PublicOrigin = new Uri(AppConfig.BaseUrl);
options.SPOptions.ReturnUrl = new Uri(AppConfig.BaseUrl);
options.SPOptions.WantAssertionsSigned = true;
options.SPOptions.AuthenticateRequestSigningBehavior = SigningBehavior.Always;
options.SPOptions.ServiceCertificates.Add(new X509Certificate2(AppConfig.Saml_Cert_Path));
})
.AddCookie();
While troubleshooting the issue, I stumbled across some confusing behaviour that may or may not indicate what may be the issue. If I follow the following steps, I can end up at a point where the user is authenticated and can use the applications:
Click 'Login' to trigger the Saml authentication.
Hit the Error 500 at Saml2/Acs.
Click 'refresh', and 'continue' to resubmit the request.
The browser then continues to the intended URL, but says 'Connection Refused'
Use the browser back buttons to return to the application home screen, and refresh the page... Viola! Logged in!
Furthermore, when inspecting the request headers on the Saml2/Acs endpoint, I can see a Saml response is returned, which I can manually decode from base64 and read the correct information!
As mentioned, the logs don't mention any errors, just:
Initiating login to https://sts.windows.net/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/
and
Successfully processed SAML response _ba082bb8-7d2c-4aa4-a7dc-b1520312d084 and authenticated a*******#********.com
Any assistance, or guidance to a resolution would be much appreciated!
Maybe this is not relevant for .Net Core, but for .net framework 4.8
there was the following issues:
ReturnUrl of Service Provider was wrong: http://locahost/mysite/saml2/acs instead of correct one http://locahost/mysite/ (with trailing slash). Because of this, there was indefinite loop to http://locahost/mysite/saml2/acs`.
SPOptions spOptions = new SPOptions() { EntityId = new EntityId(spMetadataUrl), ReturnUrl = new Uri(hostUrl + "/"), DiscoveryServiceUrl = new Uri(hostUrl + #"/DiscoveryService"), Organization = organization, AuthenticateRequestSigningBehavior = SigningBehavior.Never, RequestedAuthnContext = requestedAuthnContext, Logger = logger, PublicOrigin = hostUri };
DO NOT USE UseExternalSignInCookie mehod, otherwise ClaimPrincipal will not set for current Thread (cookies will be parsed to claims, although latter will not be set, this can be checked with code below):
Saml2AuthenticationOptions options = CreateSaml2Options(configuration, certificate); options.SPOptions.Saml2PSecurityTokenHandler = new MySaml2PSecurityTokenHandler();
public class MySaml2PSecurityTokenHandler : Sustainsys.Saml2.Saml2P.Saml2PSecurityTokenHandler
{
protected override ClaimsIdentity CreateClaimsIdentity(Saml2SecurityToken samlToken, string issuer, TokenValidationParameters validationParameters)
{
ClaimsIdentity identity = base.CreateClaimsIdentity(samlToken, issuer, validationParameters);
Claim claim = new Claim("Name", "jon.doe");
Claim[] claims = new Claim[] { claim };
identity.AddClaims(claims);
return identity;
}
}
Additionally only for Net Framework 4.8, because of owin vs System.Web cookies bug, CookieManager should be used. Code:
var cookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebCookieManager();
Saml2AuthenticationOptions options = CreateSaml2Options(configuration, certificate);
CookieAuthenticationOptions cookieAuthentication = new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString(configuration.ServiceProviderSignOnUrl),
CookieManager = сookieManager,
ReturnUrlParameter = GetBase() + "/",
Provider = new CookieAuthenticationProvider()
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = (context) =>
{
var newIdentity = new ClaimsIdentity(context.Identity);
int newcount = 1;
newIdentity.AddClaim(new Claim("SIMPLECOUNT", newcount.ToString()));
context.ReplaceIdentity(newIdentity);
return Task.FromResult<object>(null);
}
}
};
app.UseCookieAuthentication(cookieAuthentication);
app.SetDefaultSignInAsAuthenticationType(cookieAuthentication.AuthenticationType);
//app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseSaml2Authentication(options);

Why does GetExternalLoginInfoAsync() return null when I use OpenIDConnect to authenticate?

I am having a really hard time getting OpenIDConnect to work properly with this asp.net 4.6 mvc 5 project. Recently I was getting a 404 error and ended up adding a custom route to the callback action to get it to work past that. AFter that I started getting a error about GetExternalLoginInfoAsync() returning null and getting a nullreferenceexception. This is what my config looks like with some things redacted, along with the routing. Also the image provided is one of the errors I get,if i dont use custom routing i just get a 404 error.
OpenIdConnect nonce cookie is there, also in the URL it is sending the state and the code
https://localhost:44348/signin-oidc/?code=e33dab0ae5e9640ef731c460180780092703727b&state=OpenIdConnect.AuthenticationProperties%3D-JlLC-VdZi-KZst8OY4JikRrl59vm19HATAcOaqUv8a22U8ch9gC_IJARHlsvaDKZQrqfeTewtdk5d-KcZSrUR3qCoJVcmzNRDP8C0JJ2NH9ql42J3H1xkxEzoAvJ0wxITy-tCj5H-N-bYhMZbO4kB8s2S4msCF0kEDzgipoPmGfZfreUeyYcerwK_OJGH3uYKUYa1NjqA0G-hlhiYpUj8DUp59EXpDz6sr1wtohTiI
Code
var json = "";
using (WebClient wc = new WebClient())
{
json = wc.DownloadString("http://orders.data443.com/oauth/openid-configuration.php");
}
var settings = new OpenIdConnectAuthenticationOptions
{
Authority = "https://orders.data443.com/oauth/",
ClientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
RedirectUri = "https://localhost:44348/signin-oidc/",
CallbackPath = new PathString("/signin-oidc/"),
Configuration = new OpenIdConnectConfiguration(json),
ResponseType = OpenIdConnectResponseType.Code,
SignInAsAuthenticationType = "Cookies",
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
},
Scope = "openid email profile"
};
app.UseOpenIdConnectAuthentication(settings);
Routing
routes.MapRoute(
name: "signin-oidc",
url: "signin-oidc",
defaults: new { controller = "Account", action = "ExternalLoginCallback" });
I have recently dealt with these issues as well. There is an incompatibility between OWIN and ASP.NET pre-Core where cookies set by the OWIN middleware are obliterated by System.Web. Microsoft has provided some workarounds, but there's "No Cure-all" (read through the wiki!). You need to use one of their ICookieManager implementations that uses the System.Web.HttpContextBase object model to prevent cookie conflicts.
...
CookieManager = new SystemWebCookieManager(),
...
Make sure you use it throughout OWIN. You'll probably at least set it in CookieAuthenticationOptions and OpenIdConnectAuthenticationOptions and you'll need to replace app.UseExternalSignInCookie() with something like this:
// app.UseExternalSignInCookie();
app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ExternalCookie);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
AuthenticationMode = AuthenticationMode.Passive,
CookieName = ".AspNet." + DefaultAuthenticationTypes.ExternalCookie,
ExpireTimeSpan = TimeSpan.FromMinutes(5),
CookieManager = new SystemWebCookieManager()
});
Wiki:
https://github.com/aspnet/AspNetKatana/wiki/System.Web-response-cookie-integration-issues
Bugs:
https://web.archive.org/web/20170912171644/https://katanaproject.codeplex.com/workitem/197
https://github.com/aspnet/AspNetKatana/issues/331

Why does .NET Core authentication fail after setting a cookie path?

When I am setting a cookie path the following way, in the startup.cs
.AddCookie(opts =>
{
opts.AccessDeniedPath = "/Account/AccessDenied";
opts.LoginPath = "/Account/SignIn";
opts.Cookie.HttpOnly = true;
opts.Cookie.SecurePolicy = CookieSecurePolicy.Always;
opts.SessionStore = authSessionStore;
opts.Cookie.Path = "/api";
});
Authentication fails. To authenticate I am using Azure/Microsoft login, if does make sense. It just keeps refreshing the browser when trying to log in, I mean when I press the login button to go to the Microsoft login page, no error in the console whatsoever. Inside the output I can see the following lines:
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information:
Authorization failed for user: (null).
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing
ChallengeResult with authentication schemes ().
Any ideas on why this happens, and how I could fix it by keeping the path specified?
It works fine when the path is removed. I'm using .NET Core 2.0
If you are using .Net Core 2.1.x onwards then you need to add IsEssential = true
.AddCookie(opts =>
{
opts.AccessDeniedPath = "/Account/AccessDenied";
opts.LoginPath = "/Account/SignIn";
opts.Cookie.HttpOnly = true;
opts.Cookie.SecurePolicy = CookieSecurePolicy.Always;
opts.SessionStore = authSessionStore;
opts.Cookie.Path = "/api";
opts.Cookie.IsEssential = true;
});

thinktecture identity server v3 managing users in Asp.Net Identity v2

I want to use Identity Server v3 as a central authentication point for several internal web apps and I want to use Asp.Net Identity 2 as my repo for users and associated claims. I already have the two wired up together and I can see the Asp.Net Identity database created and populated when I authenticate with one of the social providers.
Update:
I can't get the Identity Manager UI to render. When I try to navigate to https://localhost:44333/#/users/create, it just displays the content of /index.html
The Thinktecture Identity Manager packages have been installed as required but I can’t find my way to the UI.
here is my Configuration method from the Startup class in my Host project:
public void Configuration(IAppBuilder app)
{
app.Map("/core", coreApp =>
{
var factory = new Thinktecture.IdentityManager.Host.AspNetIdentityIdentityManagerFactory("AspId");
coreApp.UseIdentityManager(new IdentityManagerConfiguration()
{
IdentityManagerFactory = factory.Create,
});
var idsrvOptions = new IdentityServerOptions
{
IssuerUri = "https://idsrv3.com",
SiteName = "Thinktecture IdentityServer v3 - beta 3",
Factory = Factory.Configure("AspId"),
SigningCertificate = Cert.Load(),
CorsPolicy = CorsPolicy.AllowAll,
CspOptions = new CspOptions
{
ReportEndpoint = EndpointSettings.Enabled,
},
AuthenticationOptions = new AuthenticationOptions
{
IdentityProviders = ConfigureIdentityProviders,
}
};
coreApp.UseIdentityServer(idsrvOptions);
});
This is probably very simple. Any help greatly appreciated.
Scott
my url should have been /core/#/users/create. It works now.

Categories

Resources