.NET 6 MVC5 ConfigureApplicationCookie for custom application cookies - c#

I have a .NET 5 MVC5 Application.
It use MVC Areas to run 3 applications.
Each one has a main cookie and depending on wheter user is enabled or not it check for additional auth cookies/claims.
On Chrome I receive this error:
A cookie was not sent to an insecure origin from a secure context. Because this cookie would have been sent across schemes on the same site, it was not sent. This behavior enhances the SameSite attribute’s protection of user data from request forgery by network attackers.
Resolve this issue by migrating your site (as defined by the eTLD+1) entirely to HTTPS. It is also recommended to mark the cookie with the Secure attribute if that is not already the case.
On MSDN I read that you need to work with SameSite:
Working with SameSite
In this article I also read:
SameSite and Identity
ASP.NET Core Identity is largely unaffected by SameSite cookies except for advanced scenarios like IFrames or OpenIdConnect integration.
When using Identity, do not add any cookie providers or call
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme),
Identity takes care of that.
I think this is my case so I'm quite safe I guess.
Nonetheless I wanted to do it because... Knowledge.
I use a login + cookie+claim auth:
services.AddMvc(config =>
{
// Requiring authenticated users on the site globally
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
})
// Authentication
var cookiesConfig = this.Configuration.GetSection("CookiesOptions")
.Get<CookiesOptions>();
services.AddAuthentication(
CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
//Various options
});
services.AddAuthorization(options =>
{
options.AddPolicy("Require.SomePolicy1.User", policy =>
policy.RequireClaim("AAAAA_ENABLED_USER", "true")
.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme)
);
options.AddPolicy("Require.SomePolicy2", policy =>
policy.RequireClaim("AAAAA_CAPA_ENABLED_USER", "true")
.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme)
);
And I wanted to try the SameSite thing with what I've found Here in this article
services.ConfigureApplicationCookie(options =>
{
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.HttpOnly = true;
}
);
Now as you can see down there only the aspnet cookie is set to strict and the others aren't.
How can I set every cookie to strict and remove those chrome warnings?
Microsoft suggestion is to add the request manually to every reponse like this:
// Add the cookie to the response cookie collection
Response.Cookies.Append("MyCookie", "cookieValue", cookieOptions);
but it's quite horrible, even if I do it using an ActionFilter.
Isn't there a way to set it up for every cookie?
I see that
options.Cookie..
Has also a CookieBuilder property. Maybe that's the one to try?
Thanks

Related

.net cookie authentication for specific URLs

I have been using cookie authentication in my MVC application. But now that I want to introduce Rest APIs in it, I want the cookie authentication to be only on my MVC part of the application. Is there any configuration to not use these return paths when the URL starts from /API
services.ConfigureApplicationCookie(options => {
// Set authentication request paths.
options.LoginPath = "/Identity/Account/Login";
options.LogoutPath = "/Identity/Account/Logout";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
// Set cookies to expire after 2 days.
options.ExpireTimeSpan = TimeSpan.FromDays(2);
// Do not update cookie expiration between logins.
options.SlidingExpiration = false;
// TODO set HttpOnly? set SecurePolicy to Always?
});
I did read the documentation but didn't find any such thing. it is possible with OAuth, but cant find anything in cookie auth.
You shouldn't have to change the URL paths in your setup.
I would use attributes on the controllers you want to protect - and not change your startup options.
Put an [Authorize] tag on the controllers you want to force people to sign in on and leave it off for your API controllers with public access.

Dynamic configuration of Azure B2C with user flows in a .NET 6 API

I have a .NET 6 API with authorization via a B2C tenant. This B2C tenant has two user flows:
B2C_1_CustomerSignUpAndSignIn
B2C_1_EmployeeSignUpAndSignIn
We also have a manager application which has to connect to the same API, which makes 3 schemes in total.
In my API I have configured the following in the Program.cs
builder
.Services
.AddAuthentication()
.AddScheme<JwtBearerOptions, CustomJwtHandler>("AzureB2cApplicationBased", options => builder.Configuration.Bind("AzureB2cApplicationBasedJwtBearerOptions", options))
.AddScheme<JwtBearerOptions, CustomJwtHandler>("AzureB2cCustomerSignUpAndSignIn", options => builder.Configuration.Bind("AzureB2cCustomerSignUpAndSignInJwtBearerOptions", options))
.AddScheme<JwtBearerOptions, CustomJwtHandler>("AzureB2cEmployeeSignUpAndSignIn", options => builder.Configuration.Bind("AzureB2cEmployeeSignUpAndSignInJwtBearerOptions", options))
.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>()); // Without this JwtBearerOptions.ConfigurationManager is null.
builder
.Services
.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("AzureB2cApplicationBased", "AzureB2cCustomerSignUpAndSignIn", "AzureB2cEmployeeSignUpAndSignIn")
.Build();
options.AddPolicy(
"Employee",
policy =>
{
// All authentication schemes must be added, otherwise an empty ClaimsPrincipal is given to the API methods.
policy.AddAuthenticationSchemes("AzureB2cApplicationBased", "AzureB2cCustomerSignUpAndSignIn", "AzureB2cEmployeeSignUpAndSignIn");
policy.Requirements.Add(new RoleRequirement("Employee"));
});
options.AddPolicy(
"RegisteredUser",
policy =>
{
// All authentication schemes must be added, otherwise an empty ClaimsPrincipal is given to the API methods.
policy.AddAuthenticationSchemes("AzureB2cApplicationBased", "AzureB2cCustomerSignUpAndSignIn", "AzureB2cEmployeeSignUpAndSignIn");
policy.Requirements.Add(new RoleRequirement("RegisteredUser"));
});
options.AddPolicy(
"VerifiedCustomer",
policy =>
{
// All authentication schemes must be added, otherwise an empty ClaimsPrincipal is given to the API methods.
policy.AddAuthenticationSchemes("AzureB2cApplicationBased", "AzureB2cCustomerSignUpAndSignIn", "AzureB2cEmployeeSignUpAndSignIn");
policy.Requirements.Add(new RoleRequirement("VerifiedCustomer"));
});
options.AddPolicy(
"CustomerOrVerifiedCustomer",
policy =>
{
// All authentication schemes must be added, otherwise an empty ClaimsPrincipal is given to the API methods.
policy.AddAuthenticationSchemes("AzureB2cApplicationBased", "AzureB2cCustomerSignUpAndSignIn", "AzureB2cEmployeeSignUpAndSignIn");
policy.Requirements.Add(new RoleRequirement("CustomerOrVerifiedCustomer"));
});
});
This code has been here for ages, it works, but I've never been happy with it.
I'm using a custom JwtBearerHandler called CustomJwtHandler which makes sure a token is only verified against the user flow it was created for. If I didn't then the code tried to authenticate against all the user flows, which slows down the authenticated requests by a great deal when you have a lot of user flows. We used to have more, so the difference is less present now, but still noticable. Is there a better way?
Currently if I add or remove a user flow, I have to manually update the Program.cs to remove or add this user flow to the AddAuthentication and AddAuthorization methods. If deletion is forgotten, then the authorized endpoints throw an error after the API is reloaded (read: after the scheme metadata cache is cleared), which is what happened today on development. Therefore, I would prefer this to be dynamic. I tried calling the Graph API (see https://learn.microsoft.com/en-us/graph/api/identitycontainer-list-b2cuserflows?view=graph-rest-beta&tabs=powershell) using the client credentials flow after granting the API IdentityUserFlow.Read.All, IdentityUserFlow.ReadWrite.All rights to build this dynamically, but calling this Graph API endpoint doesn't seem to work as I can only use the default scope (note: calling other endpoints does work). Is there a better way?
I don't like that the authorization metadata cache is only loaded the first time anyone is making an authorized request. I want to preload this. Can this be done?
I noticed that, since upgrading to .NET 6, when I remove the code adding all the authentication schemes to the different policies, I still retrieve a ClaimsPrincipal, so I'm assuming something changed here and it is no longer necessary.

MASL OAuth Supporting Multiple Authentication flows

I have a client application that accesses my WebAPI (1) using Integrated Windows Authentication and the Authorization code [Authentication flow].
Now I need to have a second WebAPI (2) access the original WebAPI (1) as well using the Client Credentials Authorization code Authentication flow.
My question is whether you can configure WebAPI (1) to permit EITHER flow and where to do it or whether I would have to build a whole separate API to handle the Authorization code flow? I believe it would be in the ConfigureServices method of Startup.cs but I'm not certain how adding a a Client Credentials filter policy would be handled by the controller's filters.
services.AddControllers(options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireClaim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
//Add Authorization code filter here as well??
});
In the StartUp of WebAPI (1), RequireAuthenticationUser adds DenyAnonymousAuthorizationRequirement to the current instance which enforces that the current user is authenticated. This is, obviously, not going to work for the service-to-service authorization I am looking to implement so needed to be removed to permit both the Authentication flow AND ACL-based, Client Credentials Authentication flow.
Also, to get WebAPI (1) to permit the ACL-based authenticated token of WebAPI (2), I needed to add the following to the configuration of WebAPI (1):
{
"AzureAD"
{
// other properties
"AllowWebApiToBeAuthorizedByACL" : true,
// other properties
}
}
The resulting configuration policy ended up looking like:
var policy = new AuthorizationPolicyBuilder()
.RequireClaim("appid")
.Build();
Hoping someone stumbling on this finds this and it saves them time.

ASP Net Identity - Adding a second authentication cookie

I'm trying to use a second cookie so that other applications in a subdomain can validate, if a user is logged in in my application. However i don't trust the other applications thats why I'm planning to use two Authentication Cookies. One for my own Application (IdentityCookie) and one so that the other Apps can access the login status (SubCookie).
I'm using ASP.NET Identity for cookie creation and account management.
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>().AddSignInManager<AuthSignInManager<ApplicationUser>>();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
options.CookieManager = new CookieManager();
});
The custom CookieManager is responsible for creating and deleting both Cookies at the same time.
My issue now is that I can't find a way to validate the cookies. I attempted to use a custom Authenticationhandler for the validation of the subCookie, but if I add the following code the IdentityCookie validation stops working:
services.AddAuthentication("CookieBearer").AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("CookieBearer", o => {});
It seemes like after adding this line only the Custom Handler is used and the one provided by Identity is ignored. Is there any way to add multiple AuthenticationHandlers with ASP.NET Identity? I want to use the custom AuthenticationHandler just as a fallback option. So that if authentication by Identity fails the custom AuthenticationHandler is used.
I know I can chain Authentication Schemes/Methods like this, but I'm not sure how to do it in combination with Identity.
services
.AddAuthentication()
.AddJwtBearer("Custom1", options =>
{
//Configure here
})
.AddJwtBearer("Custom2", options =>
{
// Configure here
});

How to Set Up CORS enabled domain authentication cookie .Net Core?

I am trying to authenticate CORS origin requests and set Claims principle with the user of internal company single sign on utility. I have the current setting so far, the cookie will never get created on the domain set at the authentication setup.
I have an Angular client application and .Net Core 3.0 Webapi, the requirement is for the client to be able to set authentication for future api calls.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.Name = "access_token";
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.Domain = "localhost:xxxx";
});
//CORS
services.AddCors(options =>
{
options.AddPolicy(
"AllowOrigin",
builder => builder.WithOrigins("localhost:xxxx")
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod());
});
//Sign In
HttpContext.SignInAsync(
scheme: CookieAuthenticationDefaults.AuthenticationScheme,
principal: new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme)),
properties: new AuthenticationProperties { ExpiresUtc = DateTime.Now.Add(120) });
I am testing this all on local so both URLS are localhost with different ports
Angular is hosted: http://localhost:xxxx
WebAPi is hosted :http://localhost:xxx2
http request from Angular to webapi is http://localhost:xxx2/api/auth which has the SignInAsync call, the company single sign does a username but the cookie never gets created. If I remove the options.Cookie.Domain = "localhost:xxxx"; the cookie does get created on the webapi domain http://localhost:xxx2. I must be missing something here.
After reading up some other posts on stackoverflow , it tuned out that AllowAllOrigins will only fix this problem but poses a threat.
So I ended up fixing this issue with JWT - setting authorization token for every request sent from client interface. This issue was caused due to fact that the client and WebApi are hosted on different domains.

Categories