Short version: I am having trouble in merging together the correct Authentication config in my .NET Core MVC Website to allow my users to authenticate against Azure Active Directory, but to also allow a Daemon connection (from a Console App) in, too.
Long version: I've got a .NET Core MVC website, which authenticates against Azure Active Directory perfectly fine when using the following in the ConfigureServices method in the Startup.cs:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddSignIn("AzureAd", Configuration, options => Configuration.Bind("AzureAd", options));
I am also trying to get my .NET Core Console App to call into the APIs (as a Daemon connection) into the above MVC website (all is configured in the App Registration section in my Microsoft Azure account). I can connect the Console App to the MVC website and it will successfully hit an Action Result in a controller but only if I am using the following in the ConfigureServices method in the Startup.cs of the website:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddProtectedWebApi("AzureAd", Configuration, options => Configuration.Bind("AzureAD", options));
BASICALLY, if I only use the OpenIdConnect option, my web users can access the website but my console app is denied. If I only use the JwtBearer option, then my Console App can connect, but my web users are denied.
I have Google-Bing'd all day and I'm struggling to get a mash-up of these two configurations to work without knocking the other out.
I have tried to use the .AddJwtBearer() method, but am completely confused by it:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddSignIn("AzureAd", Configuration, options => Configuration.Bind("AzureAd", options))
.AddJwtBearer(options => Configuration.Bind("AzureAD", options));
How do these work together, such that both can be in place and my web app works through a browser, and the Console App (Daemon) works too? Can I bind both to my appsettings.json file??
Incidentally, the appsettings.json file looks like this:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "zzzzzzzzzzzzzz.onmicrosoft.com",
"TenantId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
"ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath ": "/signout-callback-oidc",
"ClientSecret": "myAzureClientSecret"
}
}
UPDATE 2020-06-15: Having working on/off of this for AGES, I've found a suitable resolution that works, hence my awarding the bounty points to @michael-shterenberg. ALSO, I now know that I have a great deal to learn from @gary-archer and his impressive blog site. I just happened to get success from Michael's input.
Here's the mods to the Startup.cs file, within the ASP.NET Core MVC Web App in the diagram above:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddSignIn("AzureAd", Configuration, options =>
Configuration.Bind("AzureAd", options))
.AddJwtBearer(o =>
{
o.Authority = "https://login.microsoftonline.com/common";
o.TokenValidationParameters.ValidateAudience = false;
o.TokenValidationParameters.ValidateIssuer = false;
});
services.AddAuthorization(options =>
{
options.AddPolicy("UserAndApp", builder =>
{
builder.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
builder.AuthenticationSchemes.Add(OpenIdConnectDefaults.AuthenticationScheme);
builder.RequireAuthenticatedUser();
});
});
...coupled with the use of the following attribute on the Controller that I'm trying to call from the Daemon app.
[Authorize("UserAndApp")]
My users can still log into the website using the Azure Active Directory processes and now my automated processes can log in, too.
In case anyone is struggling to understand how the Azure App Registration side of all of this works, try this really explanatory blog post:
Secure a .NET Core API using Bearer Authentication
(I wish that I had seen that earlier, when I was trying to get my head around how the Azure App Registration process works!)
Here is the solution that worked for me (Tested on ASP .NET Core 2.1 and 3.1)
AddAuthentication
should be without parameters:services.AddAuthentication()
.AddAzureAD(options => Configuration.Bind("AzureAd", options))
.AddJwtBearer(o=> {
o.Authority = "https://login.microsoftonline.com/common";
o.TokenValidationParameters.ValidateAudience = false;
o.TokenValidationParameters.ValidateIssuer = false;
});
I used AddAzureAd
and not AddSignIn
(is that a custom external library you are using?)
services.AddAuthorization(options =>
{
options.AddPolicy("UserAndApp", builer =>
{
builer.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
builer.AuthenticationSchemes.Add(AzureADDefaults.AuthenticationScheme);
builer.RequireAuthenticatedUser();
});
});
[Authorize("UserAndApp")]
public class HomeController : Controller
Some explanation on the mechanics:
You don't want to setup automatic authentication scheme since this will be the default schema run in the authorization middleware, while you have 2 different types
The policy will try run both authentication handlers, if one of them succeeds then authentication succeeded
Note: if you send a request with an invalid Bearer token, both authetnication handlers will fail, in this case the AzureADDefaults
will "win" since it actually implement a challenge method and will redirect you (status code 302), so make sure to handle this in your app
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With