Can we have both OAuth and Certificate authentication in ASP .NET Core 5? - asp.net-core

Currently, we have a working OAuth authentication for our ASP.NET Core 5 Web API. We would like to add a certificate authentication as well to be double sure of our caller. Is there a way to have both of them? I tried the below code but it overrides one over the other.
services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme)
.AddAzureADBearer(options =>
{
options.Instance = aADInstance;
options.ClientId = clientIdWithScope;
options.Domain = aADDomain;
options.TenantId = aADTenantId;
}
)
services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate();

Changing default policy
// Add authentication schemes before, we already did this, so I would skip this part
// Change default Authorization policy
services.AddAuthorization(cfg =>
cfg.DefaultPolicy =
new AuthorizationPolicyBuilder(CertificateAuthenticationDefaults.AuthenticationScheme,
AzureADDefaults.JwtBearerAuthenticationScheme).RequireAuthenticatedUser().Build());
[Authorize] attribute now would require all http request to satisfied both CertificateAuthenticationDefaults.AuthenticationScheme and AzureADDefaults.JwtBearerAuthenticationScheme, that might be not the behavior we want for all of our endpoint, so, be careful with this approach.
Add our own policy
// Add authentication schemes before, we already did this, so I would skip this part
// Adding our own policy
services.AddAuthorization(options =>
{
options.AddPolicy("ComposedPolicy", p =>
{
p.AuthenticationSchemes = new List<string>
{CertificateAuthenticationDefaults.AuthenticationScheme, AzureADDefaults.JwtBearerAuthenticationScheme};
p.RequireAuthenticatedUser();
p.Build();
});
});
[Authorize] attribute behavior now would be untouch, but whenever we want to use our custom policy, we must specify them by [Authorize(Policy = "ComposedPolicy")].
Just choose the approach that suit the best.

Related

Require all policies by default in Api Controllers

I don't know if this may sound stupid, but I'm a bit worried about how authorization is done in Controllers.
Right now I'm using [Authorize] attribute to secure my end points. But, I'm a bit worried I forget to add a policy and anyone could access the end point.
Is there any way to apply all policies by default and just add an attribute forthe ones you want to allow?
In Asp.Net Core 2.x you can use a filter to set a global Authorization Attribute:
services.AddMvc(options =>
{
// This requires an authenticated user for all controllers/actions,
// except when at controller/action the [AllowAnonymous] attribute is set.
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
options.Filters.Add(new AuthorizeFilter(policy));
// In the same way you can set a global AntiforgeryToken
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
In Asp.Net core 3.x endpoint routing is introduced. When enabled you can set this per endpoint, as described in the migration documentation:
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
}).SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
with possible endpoint configuration:
app.UseEndpoints(endpoints =>
{
// equivalent of [Authorize] attribute on each controller:
endpoints.MapDefaultControllerRoute().RequireAuthorization();
});
Not asked, but recommended: Automatically validate antiforgery tokens for unsafe HTTP methods only.

CookiePolicyOptions or CookieAuthenticationOptions?

I'm just starting to get my mind wrapped around some of the .NetCore Identity stuff. However, there are a few things that I'm still a bit confused on. In some examples I've seen things like this:
services.AddAuthentication("mycookie")
.AddCookie("mycookie", options =>
{
options.Cookie.HttpOnly = true,
...
});
services.Configure<CookiePolicyOptions>(options =>
{
options.HttpOnly = true;
...
});
Is CookiePolicyOptions configuring the defaults and the CookieAuthorizationOptions of AddCookie overriding the default for that particular cookie?
Also, does the CookiePolicy configuration in the previous example services.Configure<CookiePolicyOptions> have any effect if there is no Cookie Middleware added in Configure? (i.e. app.UseCookiePolicy();)
Lastly, if you set up a Cookie Authentication Scheme (as shown in the code snippet), do you need both app.UseCookiePolicy() and app.UseAuthentication() middleware?
The use of cookie authentication in .net core is not itself the Identity membership system, rather a component that could be used as a standalone Authentication means or set up as part of a broader Identity configuration.
Identity -> Namespace: Microsoft.AspNetCore.Identity
CookieAuthentication -> Microsoft.AspNetCore.Authentication.Cookies
Identity: CookieConfiguration in Identity is an extension config method of the service Collection
CookieAuth: CookiePolicyOptions provides programmatic configuration for the CookiePolicyMiddleware.
services.Configure<CookiePolicyOptions>(options =>
{
options.HttpOnly = true;
...
});
This part is a set up of the Cookie policy in terms of privacy, GDPR(for Europe) and other policies.
The other part provides means to set up cookies in terms of authorization policy of your application:
services.AddAuthentication("mycookie")
.AddCookie("mycookie", options =>
{
options.Cookie.HttpOnly = true,
...
});
Unfortunately this is all I have as information. I hope more people can add some deeper understanding of the topic.
EDIT - 2022:
A more comprehensive article : https://www.reddit.com/r/dotnet/comments/we9qx8/a_comprehensive_overview_of_authentication_in/

AspNetCore use cookie and bearer together, and make Authorize attribute to use both by default

I have application where its using Cookie and Bearer token. Since i dont want update each Authorize by providing a schemas I did rewrite default schemas:
services
.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(OAuthValidationDefaults.AuthenticationScheme,
CookieAuthenticationDefaults.AuthenticationScheme,
"Identity.Application")
.RequireAuthenticatedUser()
.Build();
});
services.AddAuthentication()
.AddExternalAuthProviders(Configuration)
.AddFlymarkOpenIdConnectServer()
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Identity/Account/LogIn";
options.SlidingExpiration = true;
options.Events.OnRedirectToLogin = OnRedirectToLogin;
})
.AddOAuthValidation(OAuthValidationDefaults.AuthenticationScheme,
o=>o.Events.OnCreateTicket = OnCreateTicket);
services.ConfigureApplicationCookie(config =>
{
config.Events = new CookieAuthenticationEvents
{
OnRedirectToLogin = OnRedirectToLogin
};
});
I thought that if I add CookieAuthenticationDefaults.AuthenticationScheme then it will work but cookies are not working untill I add Identity.Application
So my question is why its not working or where is constant to use instead of hardcoded string
ASP.NET Core Identity uses its own instances of the cookie authentication handler, magically registered for you when you call services.AddIdentity().
As you figured out, these instances don't use the default scheme name but a name that starts with the Identity. prefix.
In your code, you're basically registering a new cookie handler instance with the default scheme name. But since nothing in your code uses it to issue cookies, it can't validate anything and will always return unauthenticated tickets, which is why it doesn't work until you add the magical Identity.Application, which corresponds to the main cookie handler instance used by ASP.NET Core Identity.
The constant you're looking for is in IdentityConstants.
Note: be extremely careful before adding a default policy that accepts both authentication cookies and bearer tokens, as your app might be vulnerable to CSRF if it doesn't use antiforgery (which is normally the case when developing an API).

Identity Server 4 Client Credentials for custom endpoint on token Server

I implemented a token server using Identity Server 4.
I added a custom API endpoint to the token server and struggle with the authentication. The custom endpoint is inherited from ControllerBase and has 3 methods (GET, POST, DELETE).
I intend to call the custom endpoint from within another API using a dedicated client with credentials (server to server) implemented as HttpClient in .NET Core. There is no user involved into this.
For getting the access token I use the IdentityModel DiscoveryClient and TokenEndpoint.
So in sum I did the following so far:
setup "regular" identity server and validate it works -> it works
implement custom endpoint and test it without authorizatio -> it works
add another api resource ("api.auth") with a custom scope "api.auth.endpoint1"
setup a client with client credentials allowing access to scope "api.auth.endpoint1".
implement the HttpClient and test setup -> I get an access token via the Identity Model Token Endpoint.
Now, when I call the endpoint using the HttpClient with the access token I received I get response code 200 (OK) but the content is the login page of the identity server.
The documentation of Identity Server 4 state the use of
services.AddAuthentication()
.AddIdentityServerAuthentication("token", isAuth =>
{
isAuth.Authority = "base_address_of_identityserver";
isAuth.ApiName = "name_of_api";
});
as well as the use of
[Authorize(AuthenticationSchemes = "token")]
Unfortunatly the compiler state that .AddIdentityServerAuthentication can't be found. Do I miss a special nuget?
The nugets I use on the token server so far are:
IdentityServer4 (v2.2.0)
IdentityServer4.AspNetIdentity (v2.1.0)
IdentityServer4.EntityFramework (v2.1.1)
Figured out that part. The missing nuget for AddIdentityServerAuthentication is:
IdentityServer4.AccessTokenValidation
Struggling with the authorization based on the custom scope.
Does anyone know how the security has to be configured?
Configure a client with ClientGrantTypes = client_credentials and your api like this:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.ApiName = "api.auth";
});
Where ApiName is the name of the resource. Please note that resource != scope. In most samples the resource name is equal to the scope name. But not in your case, where resource name is api.auth and scope name is api.auth.endpoint1.
Configure the client to request the scope.
var tokenClient = new TokenClient(disco.TokenEndpoint, clientId, secret);
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api.auth.endpoint1");
IdentityServer will lookup the Resource name and add that to the token as audience (aud) while the scope is added as claim with type scope.
This should be enough to make it work. Also check the sample project.
Custom authentication scheme and scope based policies for different access rights bundled together looks like that:
// Startup.ConfigureServices
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication("CustomAuthEndpointsAuthenticationScheme", options =>
{
options.Authority = "http://localhost:5000";
options.ApiName = "api.auth"; //IdentityServer4.Models.ApiResource.Name aka Audience
});
services.AddAuthorization(options =>
{
options.AddPolicy("Endpoint1Policy", policy => {
policy.AddAuthenticationSchemes(new string[] { "CustomAuthEndpointsAuthenticationScheme" });
policy.RequireScope("api.auth.endpoint1"); } ); //IdentityServer4.Models.Scope.Name
options.AddPolicy("Endpoint2Policy", policy => {
policy.AddAuthenticationSchemes(new string[] { "CustomAuthEndpointsAuthenticationScheme" });
policy.RequireScope("api.auth.endpoint2"); } ); //IdentityServer4.Models.Scope.Name
} );
// securing the custom endpoint controllers with different access rights
[Authorize(AuthenticationSchemes = "CustomAuthEndpointsAuthenticationScheme", Policy = "Endpoint1Policy")]
It seems not to interfere with the IdentityServer4 default endpoints nor with the ASP.NET Core Identity part.

write single API accessible through asp.net identity user and bearer token both

I have created asp.net mvc 6 application and configured asp.net identity users using entity framework 7 working fine. Then I added AspNet.Security.OpenIdConnect.Server token provider server that is also working fine.
Then I created an api controller as follows:
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/values
[Authorize(Policy = "SomePolicy")]
[HttpGet]
public IEnumerable Get()
{
return new string[] { "value1", "value2" };
}
}
Question:
I want to configure authorization in such a way so that either bearer token or asp.net identity user is valid (and belong to some role), I want to allow the user to access API.
Here is what I tried in startup.cs:
services.AddAuthorization(options => {
// Add a new policy requiring a "scope" claim
// containing the "api-resource-controller" value.
options.AddPolicy("API", policy => {
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
policy.RequireClaim(OpenIdConnectConstants.Claims.Scope, "offline_access");
});
});
then if I add [Authorize(Policy="API")] to my api controller, that is ONLY respecting bearer tokens, not identity users.
Any help is appreciated!
policy.AddAuthenticationSchemes supports multiple schemes, so you could - in theory - do something like that:
services.AddAuthorization(options => {
options.AddPolicy("API", policy => {
policy.AddAuthenticationSchemes(
/* Scheme 1: */ JwtBearerDefaults.AuthenticationScheme,
/* Scheme 2: */ typeof(IdentityCookieOptions).Namespace + ".Application");
});
});
Note: typeof(IdentityCookieOptions).Namespace + ".Application" is the
default authentication scheme used by ASP.NET Identity 3:
https://github.com/aspnet/Identity/blob/3.0.0-rc1/src/Microsoft.AspNet.Identity/IdentityCookieOptions.cs#L61
Alternatively, you could also remove the policy.AddAuthenticationSchemes call and configure the bearer and cookies middleware to use automatic authentication (AutomaticAuthenticate = true, which is the default value for the cookies middleware, but not for the JWT middleware).
In practice, it's absolutely not recommended as it defeats the whole purpose of using bearer-only authentication: mitigating XSRF attacks. If you really want to support cookies + bearer authentication, you should strongly consider implementing XSRF countermeasures.