How can I add more than one AddAuthentication to builder.services? - c#

I'm new to asp core, I have implement the following to login AD identity,
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
and also the following to login with user name / password and add cookie,
builder.Services.AddAuthentication("Auth1").AddCookie("Auth1", options => { options.Cookie.Name = "Auth1"; });
but how can I add both authentication to allow either case to login?
I have both tested working properly but cannot add both to builder.services.
Please recommend web page reference or approach to implement this.

Related

User.Claims is empty for every page outside of Areas/Identity

Authorization doesn't work on Razor pages outside of Areas/Identity, cannot figure out why:
Steps to reproduce:
Fresh app. ASP.NET Core Blazor WASM Hosted
Create page: Areas/Identity/Index2.cshtml
Create page: Areas/NewArea/NewPage.cshtml
Register and Log in.
Access Index2 and NewPage and break in OnGet method while debugging.
Index2: property User.Claims contains multiple claims.
NewPage: property User.Claims is empty
Why the authorization is not "propagated" into NewPage?
When I set #page "/Identity/NewPage" it works so it is somehow related to Identity Area (??).
EDITS:
(Placing page into Pages folder of the server project results the same (no auth))
Reason why do I care:
For quick loading speed I want my index page (Index.cshtml) to be razor page (not Blazor wasm). I need to display login status on index page.
More research:
I have found exactly the same (unresolved, but closed) issue in aspnetcore repo. https://github.com/dotnet/aspnetcore/issues/34080
This issue describe the same scenario I have: Authorise normal Razor Pages in a Blazor WebAssemby App? and there is also a solution:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
})
Another same issue on SO: Blazor WebAssembly with server side Razor page authorize problem
But I still don't understand why is this happening.
EDIT - Solution
The solution with changing DefaultAuthenticateScheme didn't work well after all - it break authorization for api controllers and wasm app.
This may be caused by my setup - I am not actually using server app for "hosting" the Blazor. Blazor app is just SPA using server app for auth and api.
Anyway: Instead of changing default schema for "everything" I change it only for particular .cshtml pages:
[AllowAnonymous]
[Authorize(AuthenticationSchemes = "Identity.Application")]
With that ↑ everything works. But you have to paste this into every .cshtml page... Or - you can configure it in Program.cs:
//add policy which uses ApplicationScheme
services.AddAuthorization(config =>
{
config.AddPolicy("RazorPagesAuthPolicy",
policyBuilder =>
{
policyBuilder.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme);
policyBuilder.RequireAssertion(context => true);//has to be here
});
}
//...
services.AddAuthentication()//keep this
.AddIdentityServerJwt();
//...
services.AddRazorPages(cfg =>
{
cfg.Conventions.AddFolderApplicationModelConvention("/", mo =>
{//add authorize and allowAnonymous filters
mo.Filters.Add(new AuthorizeFilter("RazorPagesAuthPolicy"));
mo.Filters.Add(new AllowAnonymousFilter());
}
);
}
);
which I am not sure if it is way to go
As I've previously mentioned, you can either use cookies authentication or Jwt authentication in order to access the ClaimsPrincipal from the Server project. The solution proposed is fine, and it is used to configure the cookies middleware used by the identity system. Incidentally, it should be:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
}).AddIdentityServerJwt();
But I still don't understand why is this happening
Do you mean why the code above works ?
The code above works because it configures the cookies middleware to authenticate the user, tells the Identity System what scheme to use, and how to challenge the user. Understand this, the Server side of your app is not authenticated, unless you configure it to perform authentication, which differs from the authentication performed when you are re-directed from the WebAssembly Blazor App. I'll try to clarify this by giving you this example:
If you created a Blazor Server App with individual accounts, and then added client SignalR to enable a chat system in your app, and you want to secure your hub object with the Authorize attribute, an authenticated user to your Blazor Server App won't be able to access secured end points on the hub, unless he is authenticated on the hub as well. How to authenticate the user ? Simply by passing the application cookie to the hub. This is more or less simialr to the issue under discussion; that is, though the Identity folder and its content are authenticated (and authorized), the hosting Server App is not.
Note: Its hard to internalized this on first shot, but rest assured that the proposed code not only works but is also 100% kosher.

Problem with custom claims when Windows authentication is used

I'm trying to implement Windows authentication for Identity Server 4.
In my code, I use similar solutions to the one described in Identity server 4 windows authentication using extension Grant not working
First of all, I use the HttpSys server with a proper scheme
builder.WebHost.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM;;
options.Authentication.AllowAnonymous = false;
});
then I add Negotiate for Authentication:
builder.Services.AddAuthentication(IdentityServerConstants.LocalApi.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.ApiName = "server.api";
})
.AddNegotiate()
.AddLocalApi();
I also extend Identity Server with such code:
.AddExtensionGrantValidator<WinAuthGrantValidator>()
(the code of WinAuthGrantValidator can be found in this answer Identity server 4 windows authentication using extension Grant not working)
This seems to work and the user is authenticated. WindowsIdentity is in HttpContext.User.Identity - so far, so good.
Now is my problem, I have also registered ProfileService in the Identity Server 4 like so:
.AddProfileService<ProfileService>()
which adds some data to the logged in user (as an additional Claim) - this works if the user is logged in with username and password.
Unfortunately when I use Windows Authentication for logging, then ProfileService is invoked and adds some claims to IssuedClaims but later (in application code) I try to read those Claims from HttpContext.User it doesn't have it. Would you help me figure out why this is happening?
Thanks a lot!

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
});

Confusion about Google OAuth packages in ASP.NET Core

I need to access some Google APIs (via Google.Apis.* NuGet Packages). Therefore I need to use the Google.Apis.Auth.AspNetCore package as described in the
official documentation:
services
.AddAuthentication(o =>
{
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddGoogleOpenIdConnect(options =>
{
options.ClientId = googleClientId;
options.ClientSecret = googleClientSecret;
});
On the other hand, I use classical ASP.NET Core Identity, especially the Google external login setup
using the
Microsoft.AspNetCore.Authentication.Google NuGet package which is initialized like this:
services
.AddAuthentication()
.AddGoogle(options =>
{
options.ClientId = googleClientId;
options.ClientSecret = googleClientSecret;
});
Is there a way to share the OAuth configurations, logins, ...? 🤔 Both packages use their own OAuth intialization code... 💭 Will I face problems calling both AddGoogle() and AddGoogleOpenIdConnect()?
Background info
Authorization is OAuth2. You would use it for example to request access to a users google drive account. Once you have been granted access you would be given a refresh token and you would be able to access the users data when ever you need.
Open id connect its authentication or login. It tells you that a user is in fact logged in it doesn't give you access to more then the users profile information by default.
Google.Apis.Auth.AspNetCore
Strictly speaking the Google APIs library normally only gave you access to authorization. However Google.Apis.Auth.AspNetCore does add an open id connect component to to the authentication in the form of AddGoogleOpenIdConnect which will give you both authentication and authorization at the same time.
Microsoft.AspNetCore.Authentication.Google
Is as its states Authentication or login. The user is logging in and authenticating that they are currently accessing your application.
recap
Microsoft.AspNetCore.Authentication.Google only gives you authentication or login, while Google.Apis.Auth.AspNetCore will potentially give you login and authorization.
Adding both
TBH I question why you would add both. Technically if you did and they didn't bash heads with each other which they might. You may end up with a double login. As these two libraries will both be trying to Authenticate the user. You will need two access tokens one for each library. I'm not sure if they set different cookies or not if they set the same cookie then they will reset each other cookies causing the user to need to login or authorize the application again to reset the cookie for each library you are trying to use.
Not to mention what adding [Authorize] attribute to a method is going to do how will it know which one to pick if your adding to authorization forms

Can't Add Authorization Convention for Area Razor Page

Using .net Core 3.1 I followed the instructions in the official docs (Scaffold Identity in ASP.NET Core projects) to scaffold Identity onto my empty Web project.
I'm using claims-based authorization. In my Startup.cs file I created the following policy:
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy =>
{
policy.RequireClaim("CanListUsers", true.ToString());
policy.RequireClaim("CanEditUsers", true.ToString());
});
});
and immediately after that I am gating certain Area pages, as follows:
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeAreaPage("Identity", "/Account/Register", "Admin");
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
});
I want to shut down registration to "Admin" users only. And I want to allow only signed in users to manage accounts.
I can't gate the Registration page. I can still access it without a logged-in user. Why not?
On the other hand, strangely, if I remove the authorization convention /Account/Manage, the page will redirect anonymous users to Login. How is that happening?
I think I figured out both questions.
I can't gate the Registration page. I can still access it without a logged-in user. Why not?
The page was decorated with [AllowAnonymous]. 🤦 Clearly decorators have higher priority than Conventions set at configuration time.
On the other hand, strangely, if I remove the authorization convention /Account/Manage, the page will redirect anonymous users to Login. How is that happening?
I noticed that all the pages that should be accessible by not-logged-in users are decorated with [AllowAnonymous]. I'm thinking that the scaffolding, being an Identity template, assumes the implementer will eventually add
app.UseAuthentication();
app.UseAuthorization();
My thinking here is that these Middlewares automatically clamp down all pages, essentially strapping an [Authorize] attribute on all pages so that a page that can allow not-logged-in users needs to opt-out with a [AllowAnonymous].
These are not much more than educated guesses, so please feel free to correct me in the comments 👍

Categories