Multiple configurations for one OpenIdConnect service - asp.net-core

I have two URLs that can be directed to my web app. Depending on the URL, I want to change what OpenIdConect (OIDC) configuration to use. I want to be able to do this without restarting the app. This requirement came after the web app was created and now needs to support two URLs.
Let's say the URLs are:
internal-mywebapp.company.com
mywebapp.company.com
The original code in Startup.cs : ConfigureServices :
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = oidcOptions.CookieSchemeName;
options.Authority = oidcOptions.AuthServerUrl;
options.ClientId = "ExternalClientId";
options.ClientSecret = oidcOptions.ClientSecret;
options.ResponseType = oidcOptions.ResponseType;
options.SaveTokens = true;
foreach (var claim in oidcOptions.RequestClaims)
{
options.Scope.Add(claim);
}
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.DeleteClaim("sid");
options.Events.OnRedirectToIdentityProvider =
EventsOnRedirectToIdentityProvider(applicationOptions);
options.Events.OnRemoteFailure = EventsOnRemoteFailure();
})
Now, my thinking is to add code the the Configure method and, if the URL starts with 'internal', use the configuration for 'internal'. I'm newer to configuring a web app to use authentication providers so I'm not too aware of the possibilities.
One thing that came up when researching this was to add a second OIDC block to AddOpenIdConnect like this and then do something in the Configure method to switch to the needed configuration :
.AddOpenIdConnect("oidc-external", options =>
{
options.SignInScheme = oidcOptions.CookieSchemeName;
options.Authority = oidcOptions.AuthServerUrl;
options.ClientId = "ExternalClientId";
options.ClientSecret = oidcOptions.ClientSecret;
options.ResponseType = oidcOptions.ResponseType;
options.SaveTokens = true;
foreach (var claim in oidcOptions.RequestClaims)
{
options.Scope.Add(claim);
}
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.DeleteClaim("sid");
options.ClaimActions.MapUniqueJsonKey("userLdapid", "userLdapid");
options.ClaimActions.MapUniqueJsonKey("fpUserRole", "fpUserRole");
options.ClaimActions.MapUniqueJsonKey("userType", "userType");
options.Events.OnRedirectToIdentityProvider =
EventsOnRedirectToIdentityProvider(applicationOptions);
options.Events.OnRemoteFailure = EventsOnRemoteFailure();
})
.AddOpenIdConnect("oidc-internal", options =>
{
options.SignInScheme = oidcOptions.CookieSchemeName;
options.Authority = oidcOptions.AuthServerUrl;
options.ClientId = "InternalClientId";
options.ClientSecret = oidcOptions.ClientSecret;
options.ResponseType = oidcOptions.ResponseType;
options.SaveTokens = true;
foreach (var claim in oidcOptions.RequestClaims)
{
options.Scope.Add(claim);
}
options.GetClaimsFromUserInfoEndpoint = true;
options.ClaimActions.DeleteClaim("sid");
options.ClaimActions.MapUniqueJsonKey("userLdapid", "userLdapid");
options.ClaimActions.MapUniqueJsonKey("fpUserRole", "fpUserRole");
options.ClaimActions.MapUniqueJsonKey("userType", "userType");
options.Events.OnRedirectToIdentityProvider =
EventsOnRedirectToIdentityProvider(applicationOptions);
options.Events.OnRemoteFailure = EventsOnRemoteFailure();
});
I could be overlooking a different approach as posts I have found sort of hint that this can be done but nothing quite fit.

#PabloRecalde : I'm not sure if was entirely clear of what I wanted.
To rectify this, we decided to split out the same code base into two separate instances with the help of our CI/CD pipeline. We were trying to use the same instance and figure out a way to switch between two OIDC based on the host name for each request. This proved difficult.

Related

ASP.NET Core how to block specific passwords

I have the following code I modified from the original ASP.NET Core project template
builder.Services.AddDefaultIdentity<IdentityUser>(
options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
}
)
.AddEntityFrameworkStores<ApplicationDbContext>();
But how do I make ASP.NET Core return an error for specific passwords?
Let's say I have an array of banned passwords like this:
var bannedPasswords = new[] {"password", "qwertyuiop", "12345678"};

Looping custom claims on login - Asp.net core

Our ASP.NET MVC application connects to IdentityServer 3 with the following config and able to access all the custom claims
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = IdentityServerUrl,
ClientId = IdentityClientId,
ResponseType = "id_token token",
Scope = "openid profile myScope",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
var newIdentity = new ClaimsIdentity(
n.AuthenticationTicket.Identity.AuthenticationType,
"name",
"myrole");
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo"),
n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => newIdentity.AddClaim(new Claim(ui.Item1, ui.Item2)));
var sid = n.AuthenticationTicket.Identity.Claims.FirstOrDefault(x => x.Type == "sid");
if (sid != null)
{
newIdentity.AddClaim(new Claim("sid", sid.Value));
}
n.AuthenticationTicket = new AuthenticationTicket(
newIdentity,
n.AuthenticationTicket.Properties);
}
}
});
Now we want to upgrade and connect to IdentityServer 3 with .net core
We tried below code but I am not getting the sure how to loop through all the custom claims
.AddOpenIdConnect("oidc", options =>
{
options.Authority = IdentityClientUrl;
options.ClientId = IdentityClientId;
options.ResponseType = OpenIdConnectResponseType.IdTokenToken;
options.Scope.Clear();
options.Scope.Add("profile");
options.Scope.Add("openid");
options.Scope.Add("email");
options.Scope.Add("myScope");
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "myrole"
};
options.SaveTokens = true;
options.ClaimActions.MapUniqueJsonKey("myrole", "myrole", "string");
});
In the existing approach, i am able to get all the claims from userInfo, so I can loop and add everything.
In the asp.net core - however I can map them using ClaimActions, eachone at a time. Is there any way I can loop throug all of them and add all of them - say I don't know the claimType!
Any help please?
You can try this to map all the claims using the MapAllExcept method, like:
options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce");

How to add : "config.SignIn.RequireConfirmedEmail = true;" Extended ApplicationUser

I want to add in : config.SignIn.RequireConfirmedEmail = true; to my startup code in .net core 2.2.
The example given by Microsoft shows how to do it for the stock standard identity user, but I have extended the user and it is now ApplicationUser.
services.AddIdentity<ApplicationUser, ApplicationRole>(
options => options.Stores.MaxLengthForKeys = 128
);
But in Microsoft example, it is like this (see below) which does not suit my needs...
services.AddDefaultIdentity<IdentityUser>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
How do I include this in my code using config ?
Try this code:
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Stores.MaxLengthForKeys = 128;
options.SignIn.RequireConfirmedEmail = true;
});

Identityserver4 doesn't work by ip address

I've used IdentityServer4 with asp net core Web, all works fine when debug in localhost:50481, but when I use myipaddress:50481 on the same computer and debug mode, it failed. I do not use a temporary credential, instead, I created a RSA cert:
.AddSigningCredential(Config.GetSigningCertificate())
public static RsaSecurityKey GetSigningCertificate()
{
var filename = Path.Combine(Directory.GetCurrentDirectory(), "certificateKey.rsa");
if (File.Exists(filename))
{
var keyFile = File.ReadAllText(filename);
var tempKey = JsonConvert.DeserializeObject<TemporaryRsaKey>(keyFile, new JsonSerializerSettings() { ContractResolver = new RsaKeyContractResolver() });
return CreateRsaSecurityKey(tempKey.Parameters, tempKey.KeyId);
}
else
{
var key = CreateRsaSecurityKey();
RSAParameters parameters;
if (key.Rsa != null)
parameters = key.Rsa.ExportParameters(includePrivateParameters: true);
else
parameters = key.Parameters;
var tempKey = new TemporaryRsaKey
{
Parameters = parameters,
KeyId = key.KeyId
};
File.WriteAllText(filename, JsonConvert.SerializeObject(tempKey, new JsonSerializerSettings() { ContractResolver = new RsaKeyContractResolver() }));
return CreateRsaSecurityKey(tempKey.Parameters, tempKey.KeyId);
}
}
I also checked the jwks of localhost and ipaddress, they are matched.
When I publish the project to local IIS, localhost does not work too, present a 500 Internal error.
all the url in my app is "http://localhost:50481"
I have to say this is a stupid mistake, I have not notice the authConfig,
let config;
if (window.location.hostname === 'localhost') {
config = configForDevelopment;
} else {
config = configForProduction;
}
when I use ip address, the config is switch to prod, change localhost to my ip address make sense.
hope it could others.

Update claims after login with identityserver3 2.1.1

We need to update users claims after they log in to our website. This is caused by changes in the users licenses done by another part of our system.
However I am not able to comprehend how to update the claims without logout/login.
Rigth now this is our client setup
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
//user validation host
Authority = UrlConstants.BaseAddress,
//Client that the user is validating against
ClientId = guid,//if not convertet to Gui the compare from the server fails
RedirectUri = UrlConstants.RedirectUrl,
PostLogoutRedirectUri = UrlConstants.RedirectUrl,
ResponseType = "code id_token token",
Scope = "openid profile email roles licens umbraco_api umbracoaccess",
UseTokenLifetime = false,
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
_logger.Info("ConfigureAuth", "Token valdidated");
var id = n.AuthenticationTicket.Identity;
var nid = new ClaimsIdentity(
id.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role);
// get userinfo data
var uri = new Uri(n.Options.Authority + "/connect/userinfo");
var userInfoClient = new UserInfoClient(uri,n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Item1, ui.Item2)));
var licens = id.FindAll(LicenseScope.Licens);
nid.AddClaims(licens);
// keep the id_token for logout
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
_logger.Info("ConfigureAuth", "AuthenticationTicket created");
},
RedirectToIdentityProvider = async n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
_logger.Debug("ConfigureAuth", "id_token for logout set on request");
_logger.Debug("ConfigureAuth", "Old PostLogoutRedirectUri: {0}", n.ProtocolMessage.PostLogoutRedirectUri.ToString());
n.ProtocolMessage.IdTokenHint = idTokenHint;
var urlReferrer = HttpContext.Current.Request.UrlReferrer.ToString();
if (!urlReferrer.Contains("localhost"))
{
n.ProtocolMessage.PostLogoutRedirectUri = GetRedirectUrl();
}
else
{
n.ProtocolMessage.PostLogoutRedirectUri = urlReferrer;
}
_logger.Debug("ConfigureAuth", string.Format("Setting PostLogoutRedirectUri to: {0}", n.ProtocolMessage.PostLogoutRedirectUri.ToString()));
}
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest)
{
n.ProtocolMessage.RedirectUri = GetRedirectUrl2();
n.ProtocolMessage.AcrValues = GetCurrentUmbracoId();
_logger.Debug("ConfigureAuth", string.Format("Setting RedirectUri to: {0}", n.ProtocolMessage.RedirectUri.ToString()));
}
},
}
});
We get our custom claims in SecurityTokenValidated
var licens = id.FindAll(LicenseScope.Licens);
nid.AddClaims(licens);
I do not follow how to get this without doing a login? Any help is highly appreciated.
That's a reminder that you should not put claims into tokens that might change during the lifetime of the session.
That said - you can set a new cookie at any point in time.
Reach into the OWIN authentication manager and call the SignIn method. Pass the claims identity that you want to serialize into the cookie.
e.g.
Request.GetOwinContext().Authentication.SignIn(newIdentity);