AspNetCore Web Api Microsoft Account Authentication - c#

My basic requirement is a Web Api that exposes some REST resources. Authentication is required to access any resource, and I want that to happen via Microsoft Accounts. This is to be a web api for programmatic access.
I started along this path: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/microsoft-logins?view=aspnetcore-2.2
And have got to the end. It probably works fine except I get this:
InvalidOperationException: The default Identity UI layout requires a partial view '_LoginPartial' usually located at '/Pages/_LoginPartial' or at '/Views/Shared/_LoginPartial' to work.
But I don't want a UI with a sign in experience. I want apps (and users from clients such as browsers) to authenticate via Microsoft and then access my REST resources.
My configure services looks like this:
services.AddIdentity<IdentityUser, IdentityRole>()
.AddDefaultTokenProviders()
//.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<IdentityDbContext>();
services.AddAuthentication().AddMicrosoftAccount(microsoftOptions =>
{
microsoftOptions.ClientId = _config["Authentication:Microsoft:ApplicationId"];
microsoftOptions.ClientSecret = _config["Authentication:Microsoft:Password"];
});
And then:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseAuthentication();
Program just does:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseUrls("http://localhost:5000", "https://localhost:5001");

You have implemented the Microsoft Authentication AND the login process in the same application, this kind of solution produce a cookie for the ASP.NET.
You probably want clients to authenticate, via OAuth, passing a Bearer Token.
In this case you must use a JwtBearer token authentication.
In this scenario your application DO NOT provide a UI for the authentication (like the example), instead ONLY validate/authenticate the token received.
Here some references
jwt auth in asp.net core
jwt validation
token authenticationin Asp.NET
Authentication in ASP.NET Core JWT

Related

Google API .NET Client - How do I get OAuth2 Access Token and Refresh token for C# ASP.NET Core Web API client to authenticate YouTube Data API v3

How do I get OAuth2 Access Token and Refresh token for C# ASP.NET Core Web API client to authenticate YouTube Data API v3
There is no UI for a username to manually enter their username and password, then receive code to get the token in this scenario. No redirect_uri is required.
How can I get the access token and refresh token
I once solved a similar issue with Microsoft Azure AD, solution on stackoverflow
I just can't find any information regarding Google Cloud Platform .NET clients for this scenario
You can not use client login (username and password) with any Google api since 2015. You will need to use Oauth2 in order to authenticate your user.
You will need to configure the library first.
public void ConfigureServices(IServiceCollection services)
{
...
// This configures Google.Apis.Auth.AspNetCore3 for use in this app.
services
.AddAuthentication(o =>
{
// This forces challenge results to be handled by Google OpenID Handler, so there's no
// need to add an AccountController that emits challenges for Login.
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// This forces forbid results to be handled by Google OpenID Handler, which checks if
// extra scopes are required and does automatic incremental auth.
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// Default scheme that will handle everything else.
// Once a user is authenticated, the OAuth2 token info is stored in cookies.
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
options.ClientId = {YOUR_CLIENT_ID};
options.ClientSecret = {YOUR_CLIENT_SECRET};
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseAuthentication();
app.UseAuthorization();
...
}
Then you can make any call you like to the YouTube API. When this endpoint is hit the user will be prompted to consent to authorization.
[GoogleScopedAuthorize(YouTubeService.ScopeConstants.Readonly)]
public async Task<IActionResult> YouTubeCall([FromServices] IGoogleAuthProvider auth)
{
GoogleCredential cred = await auth.GetCredentialAsync();
var service = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = cred
});
// your call to the youTube service here.
}
I recommend having a look at the sample for Asp .net core however it is in google drive you will need to alter it.
The client library should be handing all the access tokens and refresh tokens for you, but if you really want to access them there is a bit of information on how here #1725

Asp.Net Core MVC with Windows Authentication on IIS

I have an ASP.NET Core MVC application with Windows Authentication. I published it on IIS server in local network of our company. All works fine, all users log in with their rights. But each time when they open a browser they need to enter their credentials. Each time we see a window for entering the user name and password. How to make a logon automatic?
I added this strings to Startup.cs
public void ConfigureServices(IServiceCollection services)
{
…
services.AddAuthentication(IISDefaults.AuthenticationScheme);
//custom authorization
services.AddAuthorization(options =>
{
options.AddPolicy("Operator", policy => policy.AddRequirements(new CheckADGroupRequirement(Configuration["RolesConfig:Operator"], Configuration["RolesConfig:Manager"])));
options.AddPolicy("Manager", policy => policy.AddRequirements(new CheckADGroupRequirement(Configuration["RolesConfig:Manager"])));
});
services.AddSingleton<IAuthorizationHandler, CheckADGroupHandler>();
//custom authorization
…
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
…
app.UseAuthentication();
…
}
In IIS I set to Enabled only Windows Authentication
The only solution I found it was set Automatic logon in Internet Options but I think it’s bad practice.

.Net Core 3.1 Merging JWT and OpenIDConnect Authentication

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)
Don't set a default authentication scheme since you have 2 types (Cookies and JWT). i.e. your call to 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;
});
Note that I explicitly didn't bind your AD configuration because /common is needed to be applied to the authority (or the tenant id)
Also I set validation for audience and issuer to false so that any AAD token will work for testing. You should obviously set the correct audience/issuer
I used AddAzureAd and not AddSignIn (is that a custom external library you are using?)
Create a policy that accepts both authentication schemes:
services.AddAuthorization(options =>
{
options.AddPolicy("UserAndApp", builer =>
{
builer.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
builer.AuthenticationSchemes.Add(AzureADDefaults.AuthenticationScheme);
builer.RequireAuthenticatedUser();
});
});
Replace this with your existing authorization setup
Use the new policy name in your controller:
[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
It feels like the architecture is not quite right, and you need to separate the 2 roles performed by your Web Back End:
CURRENT WEB ONLY ARCHITECTURE
You have a Web UI front end that uses auth cookies
You have a Web Back End that requires cookies for view requests
You have a Web Back End that requires cookies for API requests
You have a Console Client that cannot use cookies so cannot call API entry points
MULTI CLIENT ARCHITECTURE
You'll need to update the web back end to include API entry points that are secured by OAuth 2.0 access tokens and not by cookies. The console app will then be able to call your web back end.
.NET CORE SUB PATHS
Introduce an additional /api subpath in your web back end. The UseWhen feature will allow you to do this without impacting other web back end behaviour:
/*
* Apply API behaviour to only subpaths, without impacting the rest of the app
*/
app.UseWhen(
ctx => ctx.Request.Path.StartsWithSegments(new PathString("/api")),
api => {
api.useAuthentication();
api.useJwtBearer();
});
EXAMPLE .NET CORE API
For an example that uses subpaths, see my Sample .Net Core API. The startup class is where ASP.Net middleware is wired with different authenticaiton handling for different subpaths.
FUTURE POSSIBILITIES
Once you have the above logical separation you could potentially evolve it further in future, eg to a completely cookieless model:
Develop APIs as completely independent components based only on tokens
Update the Web UI to an SPA that uses client side security libraries
My blog at https://authguidance.com follows this approach, and my sample APIs all support any type of client.

Azure AD SSO: .Net Core 2.1 Single Sign On Based On Organization's Office 365 Username&Password

I have created an ASP.NET Core 2.1 MVC web application and I have used a simple login form to authenticate the users. Now We have decided to remove the login form and use a single sign-on option with my Organization's Office 365 user credentials or my office’s outlook username & password and followed the following Microsoft website but I could not choose the right SSO one.
This web app is a MVP (minimum viable product) project so we just don't want to use our own authentication & authorization process and only my organization people going to use this app so we have decided to use the Organization's Azure AD SSO. I am not using SAML or WS-Federation protocols in my web app but I just wanted to implement the SSO for my project.
I searched many sites on the internet, a few websites explained "No code is required to configure SSO but only Azure AD configurations" and some other websites explained with some piece of code also. So now I am totally confused that how should I achieve the SSO for my simple web application.
Hosted environment: Azure App Service
Application users: only organization users (internal web app)
My Startup.cs code:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSession(options =>
{
options.Cookie.IsEssential = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//Fetching Connection string from APPSETTINGS.JSON
var ConnectionString = Configuration.GetConnectionString("MbkDbConstr");
//Entity Framework
services.AddDbContext<ShardingDbContext>(options => options.UseSqlServer(ConnectionString));
//Automapper Configuration
AutoMapperConfiguration.Configure();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSession();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller:required}/{action}/{id?}",
defaults: new { controller = "UserAccount", action = "UserLogin" });
});
}
}
Note: I have configured the app.UseAuthentication() & other functions but authentication part not used inside my projects.
If you want to Authenticate your users with App Services, refer the document to see how to enable AAD Authentication in app services.
Generally for any web application, you can configure App Registration in Azure AD. You can configure claim attribute as well in order to use SSO feature. Refer the document for how to configure app registration in Azure AD.

Different type of authentication for the same controller

I have WEB API CORE 3.0 back-end application. Its controllers are protected with Azure AD.
For this I Use microsoft identity web library.
In the source code I configure it like this:
public void ConfigureServices(IServiceCollection services)
{
Trace.TraceInformation("Configuring services");
services.AddProtectedWebApi(Configuration, subscribeToJwtBearerMiddlewareDiagnosticsEvents: true)
.AddProtectedApiCallsWebApis(Configuration)
.AddInMemoryTokenCaches();
...
And to protect controller I use [Authorize].
Everything works perfect.
Now I want to add the second way to authorize users (along with Azure AD).
I want that users be able to login either with Azure AD or, say, JWT.
Is it possible to implement it for the same controller?
Is it possible to change the existing authorizing mechanism to allow non-AzureAD users to use the controller.
Sounds like you're trying to make the [Authorize] to allow multiple authentication scheme at the same time. If that's the case, you should firstly register those authentication scheme with AddAuthentication().AddMyScheme1().AddMyScheme2()...:
services.AddAuthentication()
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
.AddJwtBearer(otps=>{
otps.TokenValidationParameters = new TokenValidationParameters{ ...};
});
And then change the default Authorization Policy to authenticate those authentication schemes at the same time. For example, if you want to allow Identity/JwtBearer/AzureAd at the same time, you could do it in following way
services.AddAuthorization(opts =>{
opts.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(
IdentityConstants.ApplicationScheme // ASP.NET Core Identity Authentication
,JwtBearerDefaults.AuthenticationScheme // JwtBearer Authentication
// ,"AzureAD" // AzureAd Authentication
)
.RequireAuthenticatedUser()
.Build();
});
Or if you want to allow only specific user/Role further, feel free to custom it by :
opts.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(
IdentityConstants.ApplicationScheme // ASP.NET Core Identity Authentication
,JwtBearerDefaults.AuthenticationScheme // JwtBearer Authentication
// ,"AzureAD" // AzureAd Authentication
)
.RequireAuthenticatedUser()
.RequireRole(...)
.RequireAssertion(ctx =>{
...
return true_or_false;
})
.Build();

Categories