Environment
I have a special case with a single ASP.Core 5 web application hosted on a wildcard domain.
I have an infinite number of dynamic sub-domains, and there is a Single-Sign-On OpenID authority responsible for authentication and authorizating what user has access to what domain.
For example, all these domains go to the same ASP.Core web application, and many more:
- device1.mydomain.io
- device2.mydomain.io
- device3.mydomain.io
- deviceN.mydomain.io
- anything.mydomain.io
The Single-Sign-On server will refuse to sign your login if the return URL during the OIDC- redirect points to a sub-domain that your user should not have access to. Either you have access to that particular sub-domain or you do not.
Considerations so far
So far, we have added event handlers to the OpenID cycle of the webserver to dynamically pick an OIDC Client ID based on the URL we were contacted on before the redirect to Single-Sign-On server.
After the redirect, this application will also refuse to accept the token signed by the Single-Sign-On server if it was signed for a different redirect URL than this application was contacted on. This to prevent someone from copying the token, and changing the URL and trying to use the same token for a different sub-domain the user should not have access to.
There are no longer any security problems that I can see in the OpenID redirect-cycle itself. And all here is working fine.
Problem
However now there is a security problem after the cookie has been signed when using the service.
- The user has access to
domain1.mydomain.io
, but no access todomain2.mydomain.io
. - The user logs into
domain1.mydomain.io
and ASP.Core service signs a cookie. - The user copies the cookie into Postman and uses it to contact
domain2.mydomain.io
. - Now the user has access to
domain2.mydomain.io
too, since the ASP.Core service never checks which domain the cookie was signed for.
How can I make the ASP.Core cookie-authentication middleware check which domain the cookie was signed for, and refuse it if the domain differs from the one we were contacted at?
The Startup.cs code
void AddOpenIdConnectServices(IServiceCollection services, IDataProtectionProvider dataProtectionProvider)
{
services
.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie(o =>
{
o.DataProtectionProvider = dataProtectionProvider;
o.Cookie.SameSite = SameSiteMode.None;
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = this.config.OpenId_Authority;
options.ClientId = this.config.OpenId_ClientId;
options.RequireHttpsMetadata = true;
options.SaveTokens = true;
// Ensure that the "state" sent to the SSO server is encrypted with the same secret as the other webservers use when scaled to >1.
// Without this login will fail because we're unable to decrypt the "state" at "signin-oidc" endpoint when coming back from the SSO-server.
options.DataProtectionProvider = dataProtectionProvider;
// Customize OpenID so that we can provide the SSO- server a dynamic client-id based on which hostname we were contacted on.
// We need to intercept the redirection to the SSO- server, as well as the audience/client-id validation when the JWT- token is returned from the SSO- server.
DynamicOpenIdClientHandler dynamicClientId = new DynamicOpenIdClientHandler(clientIdPrefix: options.ClientId);
options.Events.OnRedirectToIdentityProvider = dynamicClientId.OnRedirectToidentityProvider;
options.TokenValidationParameters.AudienceValidator = dynamicClientId.AudienceValidator;
options.Events.OnTokenValidated = dynamicClientId.OnTokenValidated;
});
}
Similar questions
This is a similar question, however I'm not sure customizing the cookie manager is the best way to go, or if it solves the problem.