ASP.net MVC Core and IdentityServer 4: Setting defaultScheme in AddAuthentication - authentication

I am looking at the code below. The AddAuthentication added defaultScheme with "Cookies". Does this mean the current mvc application only accept Cookie authentication but not Access Token by default.
services.AddOptions();
//services.Configure(Configuration);
services.AddDistributedMemoryCache(); // Adds a default in-memory implementation of IDistributedCache
services.AddSession();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
Currently, I wanted to have access one single page with my mobile application which authenticated with it's access token which logged in from the app itself.
I wonder how do I request the webpage inside my webview by using AccessToken instead of Cookie.
There is something called Authorize attribute with difference acceptable scheme I can pass in. I wonder is this the way to set it up.
[Authorize(AuthenticationSchemes =
JwtBearerDefaults.AuthenticationScheme)]
That is for Accesstoken only, if I need both I add cookie as well

options.DefaultScheme = "Cookies";
This means that the authentication scheme, if not specified otherwise, will be "Cookies".
options.DefaultChallengeScheme = "oidc";
This means that the default challenge authentication scheme, if not specified otherwise, will be "oidc".
This is how the OIDC and Cookie authentication schemes will usually work with each other: The application will attempt to authenticate the user using the existing cookie. If that fails (because there’s no cookie), then an authentication challenge will be made using the OIDC scheme. This will then relay the authentication to the external provider, and when that succeeds, the OIDC scheme will sign the user in using the Cookie authentication scheme. This creates the cookie, so on the next request, the cookie authentication scheme will be able to authenticate the user (without having to ask the OIDC scheme again).
If you want other authentication schemes to work, then you will have to add those too. AddAuthentication(…).AddCookie(…).AddOpenIdConnect(…) will just set this chain up. If you also want a JWT bearer authentication, you need to configure that as well.
But just because you .AddJwtBearer(…) that does not mean that anything about the normal flow will change: The Cookie scheme will still be the default, and the OIDC scheme will still be the default challenge. As I said above: Unless you specify otherwise.
So when you want to authorize the user using JWT Bearer authentication, you will need to trigger that explicitly. As you have noticed yourself, this can be done using the Authorize attribute. But in order for that to work, you will still have to set up the JWT Bearer authentication properly. But then it can work in parallel to the already set up Cookie/OIDC setup.

Related

Azure AD Client Credentials and Interactive in same ASP.NET Core API

I have an ASP.NET Core (3.1) web API with a couple of client UI apps. Authentication is via Azure AD, everything is working, using:
services.AddAuthentication()
.AddAzureADBearer(options => Configuration.Bind("AzureAD", options))
I also want to allow machine to machine API access using the Client Credentials flow. I can also get this working using the same app registration.
However, I need a way to validate which flow the request is using, as I want to expose functionality using Client Credentials API to API requests that I don't want interactive users to have access to.
What is the best way to make this work?
I have created a separate app registration in AAD that the Client Secret for the Client Credentials grant is on, and I have it adding permissions (as roles) to the token. And in the app registration for the API, I have granted permission to the Client Credentials app registration. But if I obtain a token with this flow, I can't authenticate. I have found that changing the scope in the token request to match the scope on the API app registration gives me a token that allows me to access the API, but then it is missing the app roles.
One the interactive token there are some user specific claims. So one workaround would be to check for the presence of these claims and disallow the functionality I want to restrict if they are present, but this seems a little hacky.
What else can I do? Is there a way to make both login flows work? Or another option that I've missed?
In case anyone else needs to get this working, I got it working by switching from:
services.AddAuthentication()
.AddAzureADBearer(options => Configuration.Bind("AzureAD", options))
to:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.MetadataAddress = $"https://login.microsoftonline.com/mydomain.onmicrosoft.com/.well-known/openid-configuration";
options.TokenValidationParameters.ValidateAudience = false;
});
There are some additional steps too, as mentioned in the question. I created a separate app registration in AAD, and in the app registration for the API granted permission to the new app registration. In the new app registration I had to edit the manifest to get the scope I wanted included as a role (scopes are only assigned to user tokens, not tokens obtained with the client credentials grant).
With the token working that has the role data, for requests to my restricted endpoint I can just check that it's there:
public bool ValidateScope(string scopeName)
{
return _httpContextAccessor.HttpContext.User.IsInRole(scopeName);
}
bool authorised = _clientCredentialsService.ValidateScope("restricted");
if (!authorised)
{
throw new UnauthorizedAccessException("Attempt to access restricted functionality as a regular user");
}
(I have a filter that picks up this exception and bubbles it up to the consumer as a 403).
If anyone else is doing this you can see I've set ValidateAudience to false, so you probably want to add some policies if you do this.

JWT authentication and login path

I have a MVC web application that uses JWT token to authenticate users. It calls an authentication/login API to generate and retrieve the token. Since we are in a single sign on setup there won't be a login page that I can redirect the users to. The authentication happens behind without users knowing based on some information. The only way I have found to make this API login call is by decorating the controller(s) in the web application with an Authorization filter and making the httpclient call to the login API in the filter. Is there any way to configure the login URL globally in the MVC application so that every controller will be protected. Something like the following in the cookie authentication that can be configured for the JWT authentication.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/User/Login";
options.LogoutPath = "/User/Logout";
});
Or what are the other places where I can make this httpClient call to the API to retrieve the token and subsequently authenticate the users. Let me know if what I explained here doesn't make sense.

AddAuthentication causes havoc with AddDefaultIdentity in Aspnet Core 3.1

I created a aspnet core website with local identity storage.
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
Worked fine. Then I added social authentications according to the manual, and it works fine.
Next I added api authentication as in the Xamarin.Essentials documentation. It states that before social authentication I must do AddAuthentication.
services.AddAuthentication(o =>
{
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie();
When I add those lines, the working login doesn't work at all anymore. The pages looks like they work, but the user is never in the logged in state.
What happens to AddDefaultIdentity when I AddAuthentication?
You can only have one Default authentication scheme in you application. AddDefaultIdentity is adding a cookie authentication as default authentication scheme and it's what Identity is working with.
When you add a new authentication for APIs, you are overriding the DefaultScheme here o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; which was what Identity works with.
If you want a new authentication scheme for APIs, you should just add the authentication without setting the default authentication and give it a name,
services.AddAuthentication()
.AddCookie("A_NEW_SCHEME_NAME", ... );
and add AuthorzieAttribute for this authentication scheme at your controllers/actions.
[Authorize(AuthenticationSchemes = "A_NEW_SCHEME_NAME")]

Connect my JWT to .NET Core's IHttpContextAccessor.User

I have a JWT that is sent in a header with they key "X-JWT-Assertion".
It arrives just fine. I can validate successfully it using a IAuthorizationFilter.
I then inject a IHttpContextAccessor into my controller and check the User property (a System.Security.Claims.ClaimsIdentity). It has no claims in it despite my JWT having many claims.
My guess is that my WSO2 server is sending the JWT in a non-standard header name so the .NET Core code does not know it is there.
I could just manually access the JWT to get my claims out, but I would rather use the built in .NET methods.
Is there someway I can connect my JWT with the IHttpContextAccessor.User object?
First, you don't need to inject IHttpContextAccessor to your controller for accessing the user. Controller already has a User property.
Second, You need to add jwt authentication in your startup.cs and then change the way it reads the token from with events.
services.AddAuthentication()
.AddJwtBearer(configureOptions =>
{
configureOptions.Events.OnMessageReceived = context =>
{
context.Token = context.HttpContext.Request.Headers["X-JWT-Assertion"];
return Task.CompletedTask;
};
});

How to set up cookie based authentication with NancyFx and IdentityServer3 (non-API website)

We have an environment with the following:
Standalone IdentityServer3 instance (issues reference tokens, not jwt)
ASP.NET WebAPI resource server
.NET client applications that authenticate against IdSvr (via resource owner flow)
...and now we'd like to start adding an OWIN-hosted web app that will use NancyFx to serve server-rendered pages as well as a couple AngularJS SPAs. This Nancy website will NOT host any APIs, but may consume data from our existing API. I'd like to add authentication in the OWIN pipeline to help secure our Angular applications from being sent down to users who don't have access.
This would be in contrast to sending down the SPA code, and having Angular determine if the user should see anything. In that case we've already exposed the javascript code base, and this we want to avoid.
I'm trying to understand how I should configure this Nancy site to authenticate users against IdentityServer using the implicit flow. I have implemented this authentication scheme in standalone SPAs before (where all authentication was handled by AngularJS code and tokens were stored in HTML5 local storage), but I'm a bit lost on how to properly tackle this within the OWIN pipeline.
I'm thinking that the OWIN cookie authentication middle-ware is the answer, but does that mean the following?
I need to redirect the user to IdentityServer (using the proper url arguments for implicit flow)?
IdentityServer will redirect the user back to my site on a successful login, so is that where I hook into the OWIN Authorization manager to set the appropriate cookie?
...or am I thinking about this all wrong?
For reference, I've read through the following posts, and they're very helpful but I'm not quite seeing the big picture with OWIN. I'm going to experiment with the UseOpenIdConnectAuthentication middle-ware next, but I would appreciate any guidance SO might have here.
http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
https://github.com/IdentityServer/IdentityServer3/issues/487
Fundamentally, implementing OpenID Connect authentication in a Nancy app hosted via OWIN is really not different from implementing it in any MVC/Katana app (the Thinktecture team has a sample for this scenario: https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Clients/MVC%20OWIN%20Client)
You basically need 3 things: the cookie middleware, the OpenID Connect middleware and the Nancy middleware:
public class Startup {
public void Configuration(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
// Set the address of your OpenID Connect server:
Authority = "http://localhost:54541/"
// Set your client identifier here:
ClientId = "myClient",
// Set the redirect_uri and post_logout_redirect_uri
// corresponding to your application:
RedirectUri = "http://localhost:56765/oidc",
PostLogoutRedirectUri = "http://localhost:56765/"
});
app.UseNancy(options => options.PerformPassThrough = context => context.Response.StatusCode == HttpStatusCode.NotFound);
}
}
If you're looking for a functional demo, you can take a look at https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Nancy/Nancy.Client (note: it doesn't use IdentityServer3 for the OIDC server part but it shouldn't make any difference for the client app).