OWIN - Forbid sharing of cookies between subdomains - asp.net

I have simple application with following OWIN auth setup:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
var options = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "app",
};
options.ExpireTimeSpan = TimeSpan.FromHours(2);
options.LoginPath = new PathString("/account/login");
options.Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager, IdentityUser, Guid>(
TimeSpan.FromSeconds(30),
(manager, user) => user.GenerateUserIdentityAsync(manager),
claim => Guid.Parse(claim.GetUserId()))
};
app.UseCookieAuthentication(options);
}
}
If I host application on two subdomains test.app.com and demo.app.com, I need to log in into both applications separately, which is good.
But, if I use Fiddler to catch request/response, extract cookies from test.app.com and using Fiddler to access demo.app.com with same cookies, it will return results.
I'm wondering, how we can prevent this, to disable access with same cookies from one subdomain to another subdomain.
I have single instance of application and both subdomains point to it.
Thank you!

Related

User unauthorized after Azure AD login to different application simultaneously

I have two MVC applications AppA and AppB, and implemented Azure AD authentication for login.
I am able to sign-in successfully to both applications.
But the issue is, after I login to AppA and then to AppB, after sometime when I return back to AppA I am facing the issue where user has been logged out, and it again redirects to login screen (in AppA).
After I login to AppA (second time) and go back to AppB (user in AppB is logged out).
Client IDs are different ; TenandID is same. Both apps are hosted in same server.
Startup file:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
OnValidateIdentity = MyCookieValidateIdentity
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
//CookieManager=new SameSiteCookieManager(new SystemWebCookieManager()),
Authority = "https://login.microsoftonline.com/xxxxxx/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.DomainHint = "xyz.com";
return Task.FromResult(0);
},
// SecurityTokenValidated = OnSecurityTokenValidated,
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
}
actionContext.RequestContext.Principal.Identity.IsAuthenticated is returning False
I am assuming it has to do something with the cookie. Can someone please help resolve this ?
Edit:
Debugged further and found:
Initially if the cookies for AppA are set as:
.AspNet.Cookies = A_abc123 ; ASP.NET_SessionId = A_def456
And for AppB .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
Then after I click any link in AppA, the cookie's values are updated with AppB's cookies, i.e. .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
.AspNet.Cookies ASP.NET_SessionId
AppA A_abc123 A_def456
AppB B_mno123 B_pqr456
AppA B_mno123 B_pqr456
One thing that you need to do is to configure the Data Protection API so that both services uses the same cookie protection key. Out of the box each service creates its own unique key, and a cookie from one service is not valid in a different service.
I also did a blog post about the data protection API here.
See
How to: Use Data Protection
Get started with the Data Protection APIs in ASP.NET Core
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,// DefaultAuthenticationTypes.ApplicationCookie,
CookieName = ".AspNet.AppA.Cookies",
SlidingExpiration = true,
CookieManager = new SystemWebCookieManager(),
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
//... code removed for brevity //
}
The Default Cookie Name set by the application was: .AspNet.Cookies
And when I modified the default cookie name, the issue got resolved. Each application was generating its own cookiename and hence the other application was not signing out the user.

Adding AD authentication to OWIN Server Middleware pipeline

I have inherited a project that has been developed using OWIN, Server Middleware that manages a kind-of WebApi in order to communicate with mobile devices using ApiKeys. The Server side has a small web interface (which really is a set of test pages) but did not have authentication added. I am trying to wrap my head around the different frameworks being used and the ways one can authenticate around these OWIN techniques.
Let me show what I have first:
public class Startup
{
public void Configuration(IAppBuilder app)
{
log.Info("RT Server app starting up ...");
// Initialize the ApiKey Needed for ApiClient Library
ApiClient.ApiKey = Globals.ApiKey;
// Initialize the Services Library
Services.Startup.Initialize();//creates a configuration map of values for devices
// Setup Server Middleware
app.Use(typeof(ServerMiddleware), "RTrak.Server", "RTrak.Server");
app.Use(typeof(ServerMiddleware), "RTrak.Server.Pages", "RTrak.Server");
// HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];//throws an KeyNotFoundException
// listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
//ConfigureAuth(app)
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = MyAuthentication.ApplicationCookie,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider(),
CookieName = "RTrakCookie",
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromHours(12), // ...
});
}
the ServerMiddleware
public ServerMiddleware(OwinMiddleware next, string baseNamespace, string defaultClass) : base(next)
{
BaseNamespace = baseNamespace;
DefaultClass = defaultClass;
}
public override async Task Invoke(IOwinContext owinContext)
{
var absolutePath = owinContext.Request.Uri.AbsolutePath;
string serverNamespace = BaseNamespace;
Type type;
string classToLoad = "";
if (absolutePath == "/")
classToLoad = DefaultClass;
else
{
classToLoad = absolutePath.Substring(1).Replace('/', '.');
if (classToLoad.EndsWith("."))
classToLoad = classToLoad.Substring(0, classToLoad.Length - 1);
}
type = Type.GetType($"{serverNamespace}.{classToLoad}, {serverNamespace}", false, true);
if (type == null)
{
classToLoad += ".Default";
type = Type.GetType($"{serverNamespace}.{classToLoad}, {serverNamespace}", false, true);
}
if (type != null)
{
try
{
object objService = Activator.CreateInstance(type);
((Resource)objService).Execute(owinContext);
}
catch (System.MissingMethodException)
{
//"403 INVALID URL");
}
}
else
await Next.Invoke(owinContext);
}
}
That ServerMiddleware is first calling Default Pages class that is HTML markup which links to the other test Pages
A thought was to add an MVC LoginController with AdAuthenticationService managing cookies Model to manage login that is configured as part of the Startup noted in the line ConfigAuth(app), but the middleware is ignoring the controller. Is MVC appropriate here?
then, I am looking at this ServerMiddleware and trying to understand how to intercept the Default page browser call with ActiveDirectory authentication.
I know that I may be overlooking something. Many thanks for anything (suggestions or resources) you can offer to help clear up this confusion for me.
What I did to resolve this was to leave the OWIN Middleware objects alone except for Startup.cs had to define CookieAuthentication route
app.UseCookieAuthentication(new Microsoft.Owin.Security.Cookies.CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
LoginPath = new Microsoft.Owin.PathString("/Auth/Login")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
for the pages that were built as OWIN Resources. These OWIN Resource "Pages" then check
if (!this.Context.Authentication.User.Identity.IsAuthenticated)
I then implement an MVC controller that uses the AdAuthenticationService as above and UserManager to manage the AD credentials and Identity where the OWIN resource redirects to the MVC view+controller for authentication. That controller handles the login page actions. Upon authentication, the MVC redirects to the OWIN resource pages.
Thus, OWIN Middleware and MVC can live side-by-side so long as OWIN does not try to define the routes that MVC wants to use. Owin can maintain its own authentication as well.

User claims Azure active Directory in asp.net mvc

I've just put my website on azure and I have some trouble with user claims.
I want to create special access from user who are from a special group in my azure active directory and when I was coding my website on localhost I make it this way :
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieSecure = CookieSecureOption.Always,
ExpireTimeSpan = System.TimeSpan.FromMinutes(15),
SlidingExpiration = true,
CookieHttpOnly = true
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = context =>
{
if(context.AuthenticationTicket.Identity.Claims.Any(x => x.Type == "groups" && x.Value == "587642-sff4-f4c0-8085-ssdfe45d87ed"))
{
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles","Admin"));
}
else
{
context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "User"));
}
return Task.FromResult(0);
}
}
});
}
If the user is in the group, he get the claim "Admin" and if he is not, he get "User".
On localhost, It was working perfectly but now I putted it on Azure, it doesn't go in the "Admin" claim anymore, only giving the User claim...
Can someone explain me where the problem come from?
Thanks in advance !
EDIT : I've made some test and it appears that the application is not finding groups or value(587642-sff4-f4c0-8085-ssdfe45d87ed) in context.AuthenticationTicket.Identity.Claims
Try to log out your claims and in particular what groups are present in the claims. Otherwise: Do you have access to more than one AAD tenant? Maybe you're on the wrong tenant when you go in the cloud.

Mixing Owin Asp.Net Identity Cookie Authentication with Owin OpenId Authentication

Does anyone know a good example of mixing Owin Asp.Net Identity Cookie Authentication (local db) with Owin OpenId Authentication (cloud)? Users could then choose to login/register with either creating new user&pass (stored in local database) or via e.g. Office 365 account. But all users will use the claims and roles in the asp.net Identity (local database).
I have done it like this, but I have some weird issues. Here is my ConfigureAuth method in Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
//app.Properties["Microsoft.Owin.Security.Constants.DefaultSignInAsAuthenticationType"] = "ExternalCookie";
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
// LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri
});
}
Logoff method in AccountController
public ActionResult LogOff()
{
//AuthenticationManager.SignOut();
AuthenticationManager.SignOut(
DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.ApplicationCookie,
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType
);
return RedirectToAction("Login", "Account");
}
Here is the issue, I tried to explain on another thread which hasn't got any response yet.
Link for the question

Server side claims caching with Owin Authentication

I have an application that used to use FormsAuthentication, and a while ago I switched it to use the IdentityModel from WindowsIdentityFramework so that I could benefit from claims based authentication, but it was rather ugly to use and implement. So now I'm looking at OwinAuthentication.
I'm looking at OwinAuthentication and the Asp.Net Identity framework. But the Asp.Net Identity framework's only implementation at the moment uses EntityModel and I'm using nHibernate. So for now I'm looking to try bypassing Asp.Net Identity and just use the Owin Authentication directly. I was finally able to get a working login using the tips from "How do I ignore the Identity Framework magic and just use the OWIN auth middleware to get the claims I seek?", but now my cookie holding the claims is rather large. When I used the IdentityModel I was able to use a server side caching mechanism that cached the claims on the server and the cookie just held a simple token for the cached information. Is there a similar feature in OwinAuthentication, or would I have to implement it myself?
I expect I'm going to be in one of these boats...
The cookie stays as 3KB, oh well it's a little large.
Enable a feature similar to IdentityModel's SessionCaching in Owin that I don't know about.
Write my own implementation to cache the information causing the cookie to bloat and see if I can hook it up when I configure Owin at application startup.
I'm doing this all wrong and there's an approach I've not thought of or I'm misusing something in Owin.
public class OwinConfiguration
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Active,
CookieHttpOnly = true,
CookieName = "Application",
ExpireTimeSpan = TimeSpan.FromMinutes(30),
LoginPath = "/Login",
LogoutPath = "/Logout",
ReturnUrlParameter="ReturnUrl",
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = async context =>
{
//handle custom caching here??
}
}
//CookieName = CookieAuthenticationDefaults.CookiePrefix + ExternalAuthentication.ExternalCookieName,
//ExpireTimeSpan = TimeSpan.FromMinutes(5),
});
}
}
UPDATE
I was able to get the desired effect using the information Hongye provided and I came up with the below logic...
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = async context =>
{
var userId = context.Identity.GetUserId(); //Just a simple extension method to get the ID using identity.FindFirst(x => x.Type == ClaimTypes.NameIdentifier) and account for possible NULLs
if (userId == null) return;
var cacheKey = "MyApplication_Claim_Roles_" + userId.ToString();
var cachedClaims = System.Web.HttpContext.Current.Cache[cacheKey] as IEnumerable<Claim>;
if (cachedClaims == null)
{
var securityService = DependencyResolver.Current.GetService<ISecurityService>(); //My own service to get the user's roles from the database
cachedClaims = securityService.GetRoles(context.Identity.Name).Select(role => new Claim(ClaimTypes.Role, role.RoleName));
System.Web.HttpContext.Current.Cache[cacheKey] = cachedClaims;
}
context.Identity.AddClaims(cachedClaims);
}
}
OWIN cookie authentication middleware doesn't support session caching like feature yet. #2 is not an options.
#3 is the right way to go. As Prabu suggested, you should do following in your code:
OnResponseSignIn:
Save context.Identity in cache with a unique key(GUID)
Create a new ClaimsIdentity embedded with the unique key
Replace context.Identity with the new identity
OnValidateIdentity:
Get the unique key claim from context.Identity
Get the cached identity by the unique key
Call context.ReplaceIdentity with the cached identity
I was going to suggest you to gzip the cookie, but I found that OWIN already did that in its TicketSerializer. Not an option for you.
Provider = new CookieAuthenticationProvider()
{
OnResponseSignIn = async context =>
{
// This is the last chance before the ClaimsIdentity get serialized into a cookie.
// You can modify the ClaimsIdentity here and create the mapping here.
// This event is invoked one time on sign in.
},
OnValidateIdentity = async context =>
{
// This method gets invoked for every request after the cookie is converted
// into a ClaimsIdentity. Here you can look up your claims from the mapping table.
}
}
You can implement IAuthenticationSessionStore to store cookies into database.
Here's example for storing cookie in redis.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SessionStore = new RedisSessionStore(new TicketDataFormat(dataProtector)),
LoginPath = new PathString("/Auth/LogOn"),
LogoutPath = new PathString("/Auth/LogOut"),
});
Check out full example at here

Resources