UserManager class is null even with dependency injection - c#

I am working on an ASP.NET Core MVC application that uses ASP.NET Core identity for role based authorization and I'm facing an issue trying to figure out how to work with the
UserManager class. The app should work as follows:
A coach logs in to the web app through Identity to work with
runner's data (e.g., taking attendance, starting a practice, etc.).
The app should use the UserManager class to loop through all
identity objects (registered users) so that the coach can manipulate
user's data that have the role of Runner (there are currently 4
roles in the database and I am trying to use the UserManager class
to only to select a specific role [Runner]) and then add the objects
to a list for manipulation.
The problem that I am facing is that the UserManager object that I have created is null and after countless hours of googling, I'm not sure how to fix my problem.
On my first attempt, I received a null error and after a bit of research, I attempted to fix the error through dependency injection. The second error I received is "Unable to resolve service type while attempting to activate". I have tried configuring the service an I am currently at a standstill. How exactly do I configure the service? Here is the Controller class in question and also the Program.CS class in question. I've also added two images that can hopefully explain my problem more.
Controller:
[Authorize(Roles = "Master Admin, Coach")]
[Area("Coach")]
public class TakeAttendanceController : Controller
{
private readonly IUnitOfWork _unitOfWork;
private readonly UserManager<ApplicationUser> _userManager; // the UserManager object in question
public TakeAttendanceController(IUnitOfWork unitOfWork, UserManager<ApplicationUser> userManager)
{
_unitOfWork = unitOfWork;
_userManager = userManager;
}
public IActionResult RecordAttendance()
{
var recordAttendanceViewModel = new RecordAttendanceViewModel();
var userClaimsIdentity = (ClaimsIdentity)User.Identity;
var userClaim = userClaimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
if (userClaim != null)
{
recordAttendanceViewModel.UserCoach = _unitOfWork.ApplicationUser.GetById(userClaim.Value);
}
var users = _userManager.GetUsersInRoleAsync("Runner"); // object is null
return View(recordAttendanceViewModel);
}
public IActionResult RecordWorkouts()
{
return View();
}
}
Program.cs file:
using FinalMockIdentityXCountry.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using FinalMockIdentityXCountry.Models.DataLayer.Repositories.IRepository.Interfaces;
using FinalMockIdentityXCountry.Models.DataLayer.Repositories.IRepository.Classes;
using Microsoft.AspNetCore.Identity.UI.Services;
using FinalMockIdentityXCountry.Models.Utilities;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
var serverVersion = new MySqlServerVersion(new Version(8, 0, 30)); //current version of mysql - 8.0.30
builder.Services.AddDbContext<XCountryDbContext>(options => options.UseMySql(connectionString, serverVersion));
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<XCountryDbContext>();
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddSingleton<IEmailSender, FakeEmailSender>();
builder.Services.ConfigureApplicationCookie(options =>
{
options.LoginPath = $"/Identity/Account/Login";
options.LogoutPath = $"/Identity/Account/Logout";
options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();;
app.UseAuthorization();
app.MapRazorPages();
app.MapControllerRoute(
name: "default",
pattern: "{area=Welcome}/{controller=Home}/{action=Index}/{id?}");
app.Run();
The null error
Service error

shouldnt
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
be
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
Or the injected service be
UserManager<IdentityUser>

Related

No service for type ' Microsoft.AspNetCore.Components.WebAssembly.Authentication.BaseAddressAuthorizationMessageHandler' Error

I am currently working on a project that consists of sub-projects such as WebApp, API, and Client class library. (The project structure is shown below).
Project Solution Structure
Although the project is a web-based project, it uses windows Identity as authentication identity since it is an internal application. I implemented the authorization policy of the WebApp project without any problems by following the steps in the implementation_link.
Now I can control access using DataAnnotation in WebApp (ex. [Authorize(Roles = "Admin"]). If I add Authorization control on the API side, WebApp cannot access this API. This is because of HttpContext.User is null. I found the solution to this problem solution_link. I adapted this solution to the project as below:
ServiceCollectionExtensions.cs in WebApp project:
public static IServiceCollection AddAuraServices(this IServiceCollection serviceCollection, IConfiguration configuration)
{
serviceCollection.AddTransient<IModelDatabaseNamesProvider, StandardCasingModelDatabasesNamesProvider>();
serviceCollection.Configure<RouteOptions>(routeOptions =>
{
routeOptions.ConstraintMap.Add(ModelDatabasesNameConstraint.Name, typeof(ModelDatabasesNameConstraint));
});
serviceCollection.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
serviceCollection.AddScoped<IModelMetadataProvider>(serviceProvider =>
{
var httpContext = serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
var modelName = httpContext.Request.RouteValues["model-name"].ToString();
return new ModelMetadataProvider(modelName);
});
DateOnlyTypeConverter.AddAttributeToType();
serviceCollection.AddHttpClient<UploadRulesClient>("ServerAPI", (httpClient) =>
{
httpClient.BaseAddress = new Uri(configuration["AuraApiClient:BaseAddress"]);
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
serviceCollection.AddHttpClient<ScenarioZipFilesClient>("ServerAPI",(httpClient) =>
{
httpClient.BaseAddress = new Uri(configuration["AuraApiClient:BaseAddress"]);
}).AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
serviceCollection.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("ServerAPI"));
var jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
ClientJsonResponse.Configure(jsonSerializerOptions);
serviceCollection.AddSingleton(jsonSerializerOptions);
serviceCollection.AddAuraDropzoneConfig(configuration);
return serviceCollection;
}
Startup.cs of WebApp:
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.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
services.AddAuthorization();
services.AddControllersWithViews();
//services.AddRazorPages();
services.AddAuraServices(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "model-database", pattern: "{model-name:modeldatabasename}/{controller=ZipFiles}/{action=Index}/{id?}");
endpoints.MapControllerRoute(name: "default", pattern: "", new { controller = "Home", action = "Index" });
//endpoints.MapRazorPages();
});
}
}
But this time I am getting No service for Type Error. How can I solve this problem? Where do you think I am going wrong? Thanks
Edit:
As you can see BaseAddressAuthorizationMessageHandler is in namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication. It is supposed to be used with Blazor WebAssembly apps and it attaches the access token to the authentication header of HttpClient requests. BaseAddressAuthorizationMessageHandler depends on other services like IAccessTokenProvider which is responsible to return the access token. For example in web assembly IAccessTokenProvider default implementation retrieves the access token from browser session storage.
If you want to attach access tokens to your http requests your should probably implement your own DelegatingHandler instead of BaseAddressAuthorizationMessageHandler.
Old answer:
You have to register BaseAddressAuthorizationMessageHandler:
serviceCollection.AddTransient<BaseAddressAuthorizationMessageHandler>();

System Aggregate Exception using .Net

I am working on an mvc .net application. When I run dotnet build the solution is built normally with no errors. However, when I run the solution to display the api using swagger, it throws a system aggregated exception and the run fails. The exception is being thrown at a certain part in my Program.cs file.
The Program.cs file looks something like this:
using Example.Api.Data;
using Example.Services;
using Example.Services.Interfaces;
using Example.Core;
using Example.Core.Interfaces;
using Example.Core.Repositories;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerUI;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAutoMapper(typeof(StartupBase));
builder.Services.AddControllersWithViews();
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddTransient<IApplicantService, ApplicantService>();
builder.Services.AddTransient<IApplicantSurveyChoicesService, ApplicantSurveyChoicesService>();
builder.Services.AddTransient<IApplicantSurveyService, ApplicantSurveyService>();
builder.Services.AddTransient<IChoiceService,ChoiceService>();
//I basically did add transient for everyone of my services
builder.Services.AddSwaggerGen();
var app = builder.Build(); //this is where the exception is being thrown
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
/*app.UseSwagger();
app.UseSwaggerUI();*/
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
options.RoutePrefix = string.Empty;
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();
It throws the following exception: 'System.AggregateException' in Microsoft.Extensions.DependencyInjection.dll
Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Example.Services.Interfaces.IApplicantService Lifetime: Transient ImplementationType: Example.Services.ApplicantService': Unable to resolve service for type 'Example.Core.Interfaces.IUnitOfWork' while attempting to activate 'Example.Services.ApplicantService'.)
and the error appears for every single time I call AddTransient
I am fairly new to the dotnet framework and I am still a beginner and would appreciate your help!
The IUnitOfWork file consists of the following:
public interface IUnitOfWork : IDisposable
{
IApplicantRepository Applicants { get; }
IApplicantSurveyChoicesRepository ApplicantSurveyChoices { get; }
IApplicantSurveyRepository ApplicantSurveys { get; }
IChoiceRepository Choices{ get; }
Task<int> CommitAsync();
}
The UnitOfWork class is just an implementation of the interface as such:
public UnitOfWork(DbContext context)
{
this._context = context;
}
public UnitOfWork(DbContext context, DbContext context1, IHostingEnvironment _environment, IHttpContextAccessor httpContextAccessor)
{
this._context = context;
this._environment = _environment;
_httpContextAccessor = httpContextAccessor;
}
public IApplicantRepository Applicants => _applicantRepository = _applicantRepository ?? new ApplicantRepository(_context, Config, _httpContextAccessor);
public IApplicantSurveyChoicesRepository ApplicantSurveyChoices => _applicantsurveychoicesrepository = _applicantsurveychoicesrepository ?? new ApplicantSurveyChoicesRepository(_context, Config, _httpContextAccessor);
public IApplicantSurveyRepository ApplicantSurveys => _applicantsurveysrepository = _applicantsurveysrepository ?? new ApplicantSurveyRepository(_context, Config, _httpContextAccessor);
public IChoiceRepository Choices => _choicerepository = _choicerepository ?? new ChoiceRepository(_context, Config, _httpContextAccessor);
public async Task<int> CommitAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
I believe swagger is running through your controller endpoints and will activate some services while some services remain just registered but not resolved. So in my theory the error will also happen when you use the logic of the ApplicantService.
The error says it can't resolve the implementation of IUnitOfWork while trying to activate ApplicantService. So I would guess, you have missed a registration from the class UnitOfWork, or the interface referenced in the class UnitOfWork. But that would be the place where I would look.
Singelton, Scope and Transient should not have any relevance at this stage.
I figured out the issue. One of my controllers was not properly routed which led to the exception being thrown!
Thank you everyone for your help!
EDIT!
I also added an empty constructor to each of my services so that the DI can inject into them!
Hope this was of any help to anyone!

AuthenticateResult.Principal is null when trying to authenticate with Google

I'm trying to allow users to log in with their Google account on my ASP.NET Core Blazor app. Whenever I go to my app/login/google-login, everything works as expected. I get redirected to google's login page and I get to choose an account to log in with. After choosing my account, it takes a few seconds to load and then visual studio 2019 tells me this:
System.NullReferenceException: 'Object reference not set to an instance of an object.' Microsoft.AspNetCore.Authentication.AuthenticateResult.Principal.get returned null.
at this block of code:
var claims = response.Principal.Identities.FirstOrDefault().Claims.Select(claim => new
{
claim.Issuer,
claim.OriginalIssuer,
claim.Type,
claim.Value
});
Some debugging has revealed the following:
{"succeeded":false,"ticket":null,"principal":null,"properties":null,"failure":null,"none":true}
This is the response I get from Google's API formatted as JSON. Basically it tells me that the principal is null, which I could have guessed, but the rest is also null. What's going on here? Could this simply be an issue with my scopes on Google's end? I have reasons to believe this isn't the problem though since my app should be able to work with any API response without crashing, right?
Here's my LoginController.cs class:
[AllowAnonymous, Route("login")]
public class LoginController : Controller
{
[Route("google-login")]
public IActionResult GoogleLogin()
{
var properties = new AuthenticationProperties { RedirectUri = Url.Action("GoogleResponse") };
return Challenge(properties, GoogleDefaults.AuthenticationScheme);
}
[Route("google-response")]
public async Task<IActionResult> GoogleResponse()
{
var response = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var claims = response.Principal.Identities.FirstOrDefault().Claims.Select(claim => new
{
claim.Issuer,
claim.OriginalIssuer,
claim.Type,
claim.Value
});
return Json(claims);
}
}
Here's my Startup.cs:
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.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
//services.AddSingleton<WeatherForecastService>();
services.AddDbContext<Context>(options => options.UseSqlServer(Configuration.GetConnectionString("Context")));
services.AddIdentity<User, Role>().AddEntityFrameworkStores<Context>();
services.AddHttpContextAccessor();
services.AddScoped<IReservationService, ReservationService>();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/login/google-login";
})
.AddGoogle(options =>
{
options.ClientId = Configuration["Google:ClientID"];
options.ClientSecret = Configuration["Google:ClientSecret"];
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
If you need any additional info or code, I'll be happy to provide that ASAP.
This line, that is arguably a fairly big part of my project, was preventing communication with Google for who knows what reason.
services.AddIdentity<User, Role>().AddEntityFrameworkStores<Context>();
I now basically just went on without UserManager and RoleManager and just wrote manual methods for accessing AspNetUsers etc.
Probably not a real solution but it is what it is.

ASP.NET Core 3.1 'Cannot resolve scoped service from root provider.'

I am trying to identify the issue I am facing here with my middleware injection. I have added an 'Audit' trail to my application by forwarding requests via HttpContext to my SQL Server DB which then can be viewed by admins in my application under my Events Controller/View.
My StartUp.cs is as follows:
using AWS_Service_Catalog.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.EntityFrameworkCore.SqlServer;
using Microsoft.EntityFrameworkCore;
using AWS_Service_Catalog.Models;
using AWS_Service_Catalog.Data;
using AWS_Service_Catalog.Middleware;
namespace AWS_Service_Catalog
{
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.AddDistributedMemoryCache();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
// Handling SameSite cookie according to https://learn.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
options.HandleSameSiteCookieCompatibility();
});
services.AddOptions();
//Adds authentication/authorization via Microsoft Azure AD
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi(Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' '))
.AddDownstreamWebApi("AWSServiceCatalogAPI", Configuration.GetSection("DownstreamApi:Scopes"))
.AddInMemoryTokenCaches();
services.AddApiService(Configuration);
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
services.AddRazorPages();
services.AddDbContext<EventLogContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MicrosoftSQLServer")));
//Configuring appsettings section AzureAd, into IOptions
services.AddOptions();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//Production Exception Handler ex: API connection failed will trigger exception routed to /Home/Error
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//Handles User Error: 401, 403, 404, etc. Errors caught must land Application side. Errors occured in API with return 500 and be routed via Exception Handler
app.UseStatusCodePagesWithReExecute("/Home/Error", "?status={0}");
app.UseHttpException();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
//Must include Authentication/Authorization under routing
app.UseAuthentication();
app.UseAuthorization();
app.UseEventLogCaptureMiddleware();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Server}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
}
My EventLogCaptureMiddleware class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Identity.Web;
using AWS_Service_Catalog.Models;
using AWS_Service_Catalog.Data;
namespace AWS_Service_Catalog.Middleware
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class EventLogCaptureMiddleware
{
private readonly RequestDelegate _next;
private readonly EventLogContext _context;
public EventLogCaptureMiddleware(RequestDelegate next, EventLogContext context)
{
_next = next;
_context = context;
}
public Task Invoke(HttpContext httpContext)
{
string role;
try
{
role = httpContext.User.Claims.ToArray()[5].Value;
}
catch
{
role = null;
}
var eventLogModel = new EventLogViewModel
{
Timestamp = DateTime.Now,
Role = role,
Method = httpContext.Request.Method,
Upn = httpContext.User.Identity.Name,
Resource = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}{httpContext.Request.Path}{httpContext.Request.QueryString}"
};
_context.Add(eventLogModel);
var tasks = new Task[] { _context.SaveChangesAsync() };
Task.WaitAll(tasks);
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class EventLogCaptureMiddlewareExtensions
{
public static IApplicationBuilder UseEventLogCaptureMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<EventLogCaptureMiddleware>();
}
}
}
I am failing to understand the exact error or the cause. I have looked at this question posted: Cannot resolve scoped service from root provider .Net Core 2
Some elaboration would be wonderful. I can't identify how to resolve this issue until I know what it is. What is interesting to me, this error only occurs when I run my application in IIS Express. Otherwise there seems to be no issue.
Thoughts?

I need to make Active Directory login works on C# whit net core 3.1

my goal is to authenticate the login to a company application through active directory, I currently asked the company's support to create the app registration and install version 3.0.0-rc1.19457.4 in my code (because later It would not let me install them, they said they were not compatible) my project points to netcore3.1 and the web application already had the internal management of login with database, for security this is a new requirement and I never did something similar, so I'm half lost with this.
So, i started reading some guides and posts from here like this: Azure AD Not Authenticating in .NET Core 3.1 including the sample and the git code, but I don't think I have the necessary seniority to understand where the authentication returns, I don't understand if I need to create a view with the name "signin-oid" nor do I understand how to configure the / secret of the home controller.
The debugging and testing process is complicated because the application needs publish and pull request for every change I make, and I can't test this on localhost.
This is my current configuration in Azure, I think it's fine, since almost all the guides said to do this.
Here below I leave the code of both the startup and the controller I want to go to and the appsettings.json
STARTUP
namespace name*
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var config = new ConfigurationBuilder() //newForAD
.SetBasePath(System.IO.Directory.GetCurrentDirectory())//newForAD
.AddJsonFile("appsettings.json", false)//newForAD
.Build();//newForAD
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)//newForAD
.AddAzureAD(options => config.Bind("AzureAd", options));//newForAD
services.AddControllersWithViews();//newForAD
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting(); // UseRouting must come before UseAuthentication //newForAD
app.UseAuthentication(); // UseAuthentication must come before UseAuthorization //newForAD
app.UseAuthorization(); //newForAD
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
Appsettings.json :
"AzureAd": {
"Instance": "https://login.microsoftonline.com",
"Domain": "https://*****.azurewebsites.net/",
"TenantId": "****",
"ClientId": "*****",
"CallbackPath": "/signin-oidc"
}
Then, the Home Controller:
{
[CustomActionFilter]
public class HomeController : Controller
{
private readonly IHomeApplication _home;
private readonly IWebHostEnvironment _environment;
public HomeController(IHomeApplication home, IWebHostEnvironment environment)
{
_home = home;
_environment = environment;
}
[Route("/")]
public IActionResult Index()
{
return Ok("Home page");
}
[Authorize]
[Route("/secret")]
public IActionResult Secret()
{
var identity = ((ClaimsIdentity)HttpContext.User.Identity);
var name = identity.Claims.FirstOrDefault(c => c.Type == "name")?.Value;
var email = identity.Claims.FirstOrDefault(c => c.Type == "email")?.Value;
return new OkObjectResult(new { name, email });
}}}
If someone can help me to make this work it will solve my week
As junnas said, change your domain to mytenant.onmicrosoft.com and it will work well.
I don't understand if I need to create a view with the name "signin-oidc" nor do I understand how to configure the / secret of the home controller.
You need to ensure your claim contain name and email, otherwise it will get error. You can simple set the Authorize attribute on Index and if you can login, it means that you have configure right.
For more details you could refer to this article and sample here.

Categories