Blazor server and Google Auth - c#

I tried to implement google auth to my blazor app. but I received this error:
`ArgumentException: The 'ClientSecret' option must be provided. (Parameter 'ClientSecret')
Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions.Validate()
Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions.Validate(string scheme)
Microsoft.AspNetCore.Authentication.AuthenticationBuilder+<>c__DisplayClass4_0<TOptions, THandler>.<AddSchemeHelper>b__1(TOptions o)
Microsoft.Extensions.Options.ValidateOptions<TOptions>.Validate(string name, TOptions options)
Microsoft.Extensions.Options.OptionsFactory<TOptions>.Create(string name)
Microsoft.Extensions.Options.OptionsCache<TOptions>+<>c__3<TArg>.<GetOrAdd>b__3_0(string name, ValueTuple<Func<string, TArg, TOptions>, TArg> arg)
System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>.GetOrAdd<TArg>(TKey key, Func<TKey, TArg, TValue> valueFactory, TArg factoryArgument)
Microsoft.Extensions.Options.OptionsCache<TOptions>.GetOrAdd<TArg>(string name, Func<string, TArg, TOptions> createOptions, TArg factoryArgument)
Microsoft.Extensions.Options.OptionsMonitor<TOptions>.Get(string name)
Microsoft.AspNetCore.Authentication.AuthenticationHandler<TOptions>.InitializeAsync(AuthenticationScheme scheme, HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationHandlerProvider.GetHandlerAsync(HttpContext context, string authenticationScheme)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
`
And this is my Program.cs
builder.Services.AddAuthentication(options =>
{
}).AddGoogle(options =>
{
options.ClientId = GoogleCredentials.CLientId;
options.ClientSecret = options.ClientSecret;
options.SignInScheme = GoogleDefaults.AuthenticationScheme;
});
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
How can we fix this error?
If someone can help me, reply please.

Related

Error while validating the service descriptor

I am trying to add a handler to my services in Startup.cs. Doing this will give me the following error:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Authorization.IAuthorizationService Lifetime: Transient ImplementationType: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService': Unable to resolve service for type 'Microsoft.AspNetCore.Http.IHttpContextAccessor' while attempting to activate 'ApiPortal.Classes.PolicyAuthorizationHandler')
Here is my Startup.cs ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
services.Configure<OpenIdConnectOptions>("azure", options =>
{
var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingOnTokenValidatedHandler(context);
await context.HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme, context.Principal);
};
var existingOnUserInformationReceived = options.Events.OnUserInformationReceived;
options.Events.OnUserInformationReceived = async context =>
{
await existingOnUserInformationReceived(context);
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("AzureAccount", policy_builder => policy_builder.AddRequirements(new PolicyRequirement()));
});
services.AddScoped<IAuthorizationHandler, PolicyAuthorizationHandler>();
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
}
Here is my PolicyAuthorizationHandler.cs:
public class PolicyAuthorizationHandler : AuthorizationHandler<PolicyRequirement>
{
readonly IHttpContextAccessor _contextAccessor;
public PolicyAuthorizationHandler(IHttpContextAccessor ca)
{
_contextAccessor = ca;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = "";
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = "";
if (await requirement.Pass(context, _contextAccessor, area, controller, action, id))
{
context.Succeed(requirement);
}
}
if (context.Resource is DefaultHttpContext httpContext)
{
var area = "";
var controller = httpContext.Request.RouteValues["controller"].ToString();
var action = httpContext.Request.RouteValues["action"].ToString();
var id = "";
if (await requirement.Pass(context, _contextAccessor, area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
Here is my PolicyRequirement.cs class:
public class PolicyRequirement : IAuthorizationRequirement
{
IHttpContextAccessor _contextAccessor;
AuthorizationHandlerContext _context;
public async Task<bool> Pass(AuthorizationHandlerContext context, IHttpContextAccessor contextAccessor, string area, string controller, string action, string id)
{
_context = context;
_contextAccessor = contextAccessor;
bool authorized = false;
//authorization logic goes here
string email = contextAccessor.HttpContext.User.Identity.Name;
if (email == "myemail#email.com") authorized = true;
return await Task.FromResult(authorized);
}
}
I have already tried changing services.AddScoped<IAuthorizationHandler, PolicyAuthorizationHandler>() to services.AddTransient<IAuthorizationHandler, PolicyAuthorizationHandler>() and services.AddSingleton<IAuthorizationHandler, PolicyAuthorizationHandler>(). These don't seem to work.
I have also taken a look at this and this thread, but I can't seem to find a solution for my problem here as well.
What am I doing wrong and what can I do to fix this error? Thanks in advance.
This error means that PolicyAuthorizationHandler class depends on a service of type IHttpContextAccessor, but that service is not registered in the service container.
you need to register IHttpContextAccessor in ConfigureServices by adding line services.AddHttpContextAccessor();
and after this, you can pass IHttpContextAccessor to the constructor of PolicyAuthorizationHandler
`

Error 500 when try to post to database on .net 6

I have problem in my .Net project with my Controller/service.
My program works but if I want to register user in Postman program throw error 500 as at the bottom.
I really have no idea whats the problem, it works few changes ago.
Can someone explain me why it stop working?
.net 6
DbContext:
public class TrainingAppContext : DbContext
{
public DbSet<User>? User { get; set; }
public DbSet<Calendar>? Calendar { get; set; }
public DbSet<Exercises>? Exercises { get; set; }
public TrainingAppContext(DbContextOptions<TrainingAppContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Property(r => r.Name);
}
public static implicit operator string(TrainingAppContext v)
{
throw new NotImplementedException();
}
}
Controller:
[
Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
public AuthController(LoginService loginService)
{
_loginService = loginService;
}
private LoginService _loginService;
[HttpPost("login")]
public IActionResult Login([FromBody] User user)
{
bool checkUserLogIn = _loginService.CheckUserLogIn(user.Name, user.Password);
if (checkUserLogIn)
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey#345"));
var signingCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name),
};
var tokenOptions = new JwtSecurityToken
(
issuer: "https://localhost:5001",
audience: "https://localhost:5001",
claims: claims,
expires: DateTime.Now.AddMinutes(15),
signingCredentials: signingCredentials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions);
return Ok(new { Token = tokenString });
}
else
{
return Unauthorized();
}
}
[HttpPost("Register")]
public IActionResult Register([FromBody] User user)
{
bool CheckName = _loginService.CheckUserExist(user.Name);
if (!CheckName)
{
if (_loginService.AddUser(user.Name, user.Password, user.Trainer))
{
return Ok();
}
else return BadRequest();
}
else
{
return Unauthorized();
}
}
service:
public class LoginService
{
private TrainingAppContext _context;
public LoginService(TrainingAppContext context)
{
_context = context;
}
public bool CheckUserLogIn(string name, string password)
{
if(_context.User.Where(x => x.Name == name && x.Password == password).Any())
return true;
else return false;
}
public bool CheckUserExist(string name)
{
if (_context.User.Any(x => x.Name == name))
return true;
else return false;
}
public bool AddUser(string name, string password, bool trainer)
{
using (var db = _context)
{
var user = new User { Name = name, Password = password, Trainer = trainer };
db.User.Add(user);
db.SaveChanges();
return true;
}
}
}
program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(opt => {
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "https://localhost:5001",
ValidAudience = "https://localhost:5001",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey#345"))
};
});
builder.Services.AddCors(options =>
{
options.AddPolicy("EnableCORS", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddControllers();
builder.Services.AddDbContext<TrainingAppContext>(
options => options.UseSqlServer("Server=(Localdb)\\mssqllocaldb;Database=TrainingAppData"));
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseCors("EnableCORS");
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
error in console:
fail:
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Unable to resolve service for type 'TrainingApp.Server.Services.LoginService' while attempting to
activate 'TrainingApp.Server.Controllers.AuthController'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method3(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass7_0.b__0(ControllerContext
controllerContext)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass6_0.g__CreateController|0(ControllerContext
controllerContext)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State&
next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker
invoker, Task lastTask, State next, Scope scope, Object state, Boolean
isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker
invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker
invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint
endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext
context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext
context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)
C:\Engineering
Thesis\TrainingApp.Server\TrainingApp.Server\bin\Debug\net6.0\TrainingApp.Server.exe
(process 25696) exited with code -1.
In order to use the loginService as an Injected Dependency, you need to register it in your dependency Container.
The exception states that it was unable to resolve the Login service when attempting to activate the TrainingApp.Server.Controllers.AuthController
You will need to update your Program.cs file and add this line:
builder.Services.AddControllers();
builder.Services.AddDbContext<TrainingAppContext>(
options => options.UseSqlServer("Server=(Localdb)\\mssqllocaldb;Database=TrainingAppData"));
// Add this line to add the login service as a singleton in your DI
// Be sure to add this line before calling the build() function
// If you want it to be a Singleton
// One instance will be created for the lifetime of the application
builder.Services.AddSingleton<LoginService>();
// If you want it to be a Transient
// A new instance will be created each time you inject it
builder.Services.AddTransient<LoginService>();
// If you want it to be a Scoped
// One instance will be created for each HTTP Request
builder.Services.AddScoped<LoginService>();
var app = builder.Build();
This needs to be done for every service you want to use by Dependency Injection
Check this documentation: Dependency injection in ASP.NET Core
Difference between Scoped, Transient and Singleton

Rebus.Async with SimpleInjector Request/Reply MessageCouldNotBeDispatchedToAnyHandlersException

I'm using Rebus.Async to reply a processing data to WebApi request using command CQRS.
1. I registered bus with the following configuration:
public static IContainerRegistry RegisterRebusInMemory(
this IContainerRegistry containerRegistry)
{
switch (_containerAdapter)
{
default: // SimpleInjector
return RegisterRebusInMemoryWithSimpleInjectorContainer(containerRegistry);
}
}
private static IContainerRegistry RegisterRebusInMemoryWithSimpleInjectorContainer(
IContainerRegistry containerRegistry)
{
var rebusQueue = "rebusMessages";
Container container = (Container)containerRegistry.Container;
RegisterRebus(containerRegistry);
container.ConfigureRebus(
configurer => configurer
.Logging(l => l.Console())
.Serialization(s => s.UseNewtonsoftJson(NewtonsoftSettings, Encoding.UTF8))
.Options(o =>
{
o.EnableSynchronousRequestReply();
o.SetNumberOfWorkers(1);
o.SetMaxParallelism(1);
})
.Transport(t => t.UseInMemoryTransport(new InMemNetwork(), rebusQueue))
.Routing(r =>
{
r.TypeBased()
.MapAssemblyOf<IDomainNotification>(rebusQueue)
.MapAssemblyOf<DefaultReplyCommand>(rebusQueue)
.MapCommands(rebusQueue)
.MapEvents(rebusQueue);
})
.Sagas(sagas => sagas.StoreInMemory())
.Subscriptions(s => s.StoreInMemory())
.Start()
);
return containerRegistry;
}
2. I create a Handle to use with request/reply:
public class MyCommandHandler : CommandHandler, IHandleMessages<MyCommand>
{
...
public async Task Handle(MyCommand message)
{
DefaultReplyCommand defaultReply = new DefaultReplyCommand(message.AggregateId);
// Do some stuffs
await _bus.Reply(_reply);
}
...
}
3. When I send the reply with await _bus.Reply(_reply) throw the following exception:
[WRN] Rebus.Retry.ErrorTracking.InMemErrorTracker (Rebus 1 worker 1): Unhandled exception 1 (FINAL) while handling message with ID "request-reply_3452be37-fdfd-4115-89a2-504b4feae22f" Rebus.Exceptions.MessageCouldNotBeDispatchedToAnyHandlersException: Message with ID request-reply_3452be37-fdfd-4115-89a2-504b4feae22f and type AgroHUB.Application.Cattle.Commands.CattleOperation.MoveCattleOperationCommand, AgroHUB.Application.Cattle could not be dispatched to any handlers (and will not be retried under the default fail-fast settings)
at Rebus.Pipeline.Receive.DispatchIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Sagas.LoadSagaDataStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Pipeline.Receive.ActivateHandlersStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Internals.ReplyHandlerStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Pipeline.Receive.HandleRoutingSlipsStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Pipeline.Receive.DeserializeIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.DataBus.ClaimCheck.HydrateIncomingMessageStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Pipeline.Receive.HandleDeferredMessagesStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Retry.FailFast.FailFastStep.Process(IncomingStepContext context, Func`1 next)
at Rebus.Retry.Simple.SimpleRetryStrategyStep.DispatchWithTrackerIdentifier(Func`1 next, String identifierToTrackMessageBy, ITransactionContext transactionContext, String messageId, String secondLevelMessageId)
Cenario
The Rebus.Async works with native C# Dependency Injection (service.AddScope), but when I change de Cointainer to use Simple Injector the reply doesn't work anymore.
There's some extra configuration to use Rebus.Async with Simple Injector?

System.InvalidOperationException: Scheme already exists: Identity.Application: After Identity Scaffolding

I had an existing ApplicationUser, and I wanted to modify the UI page of Login. So this is why I decided to perform the scaffold operation.
ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
public List<Student> Students { get; set; }
}
But when I use the last CLI command this :
dotnet aspnet-codegenerator identity --files="Account.Login;"
I can't be able to run my web application, this is what it shows on the web :
And then, this is the error which occurs in the log :
Unhandled Exception: System.InvalidOperationException: Scheme already
exists: Identity.Application at
Microsoft.AspNetCore.Authentication.AuthenticationOptions.AddScheme(String
name, Action1 configureBuilder) at
Microsoft.AspNetCore.Authentication.AuthenticationBuilder.<>c__DisplayClass4_02.b__0(AuthenticationOptions
o) at
Microsoft.Extensions.Options.ConfigureNamedOptions1.Configure(String
name, TOptions options) at
Microsoft.Extensions.Options.OptionsFactory1.Create(String name)
at
Microsoft.Extensions.Options.OptionsManager1.<>c__DisplayClass5_0.<Get>b__0()
at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode) at
System.Lazy1.ExecutionAndPublication(LazyHelper
executionAndPublication, Boolean useDefaultConstructor) at
System.Lazy1.CreateValue() at
Microsoft.Extensions.Options.OptionsCache1.GetOrAdd(String name,
Func1 createOptions) at
Microsoft.Extensions.Options.OptionsManager1.Get(String name) at
Microsoft.Extensions.Options.OptionsManager1.get_Value() at
Microsoft.AspNetCore.Authentication.AuthenticationSchemeProvider..ctor(IOptions1
options, IDictionary2 schemes) at
Microsoft.AspNetCore.Authentication.AuthenticationSchemeProvider..ctor(IOptions1
options)
--- End of stack trace from previous location where exception was thrown --- at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite
constructorCallSite, ServiceProviderEngineScope scope) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(IServiceCallSite
callSite, TArgument argument) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite
scopedCallSite, ServiceProviderEngineScope scope) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite
singletonCallSite, ServiceProviderEngineScope scope) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite
callSite, TArgument argument) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.b__0(ServiceProviderEngineScope
scope) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type
serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type
serviceType) at
Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type
serviceType) at
Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider
provider) at
Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider
provider, Type instanceType, Object[] parameters) at
Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass4_0.b__0(RequestDelegate
next) at
Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.Build() at
Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication() at
Microsoft.AspNetCore.Hosting.Internal.WebHost.StartAsync(CancellationToken
cancellationToken) at
Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host,
CancellationToken token, String shutdownMessage) at
Microsoft.AspNetCore.Hosting.WebHostExtensions.RunAsync(IWebHost host,
CancellationToken token) at
Microsoft.AspNetCore.Hosting.WebHostExtensions.Run(IWebHost host)
at ICFERApp.Program.Main(String[] args) in
/Users/idrislutaaya/RiderProjects/ICFERApp/ICFERApp/Program.cs:line 17
I had a look on this question but it didn't help me.
Actually what surprises me, there's a DBClass which creates it's self in this directory ~/Areas/Identity/Data, below :
public class ICFERAppIdentityDbContext : IdentityDbContext<IdentityUser>
{
public ICFERAppIdentityDbContext(DbContextOptions<ICFERAppIdentityDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
Yet I was having an existing one here :
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>()
.HasMany(s => s.Students)
.WithOne(u => u.User);
builder.Entity<Student>()
.HasOne(e => e.Education)
.WithOne(s => s.Student);
builder.Entity<Student>()
.HasOne(g => g.Guardian)
.WithOne(s => s.Student);
builder.Entity<Student>()
.HasOne(p => p.Parents)
.WithOne(s => s.Student);
builder.Entity<Student>()
.HasOne(s => s.Siblings)
.WithOne(s => s.Student);
builder.Entity<Siblings>()
.Property(p => p.Id)
.ValueGeneratedOnAdd();
builder.Entity<Siblings>()
.HasKey(x => new { x.Id, x.StudentId});
builder.Entity<Education>()
.Property(p => p.Id)
.ValueGeneratedOnAdd();
builder.Entity<Education>()
.HasKey(x => new { x.Id, x.StudentId});
builder.Entity<Guardian>()
.Property(p => p.Id)
.ValueGeneratedOnAdd();
builder.Entity<Guardian>()
.HasKey(x => new { x.Id, x.StudentId});
builder.Entity<Parents>()
.Property(p => p.Id)
.ValueGeneratedOnAdd();
builder.Entity<Parents>()
.HasKey(x => new { x.Id, x.StudentId});
}
}
Then, in my Startup.cs file, I followed what the Microsoft doc says :
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.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(
Configuration.GetConnectionString("DefaultConnection")));
services.AddTransient<IStudentRepository, StudentRepository>();
services.AddMvc().AddNToastNotifyToastr();
services.AddJsReport(new LocalReporting()
.UseBinary(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? JsReportBinary.GetBinary()
: jsreport.Binary.OSX.JsReportBinary.GetBinary())
.AsUtility()
.Create());
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddRazorPagesOptions(options =>
{
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout");
});
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = $"/Identity/Account/Login";
options.LogoutPath = $"/Identity/Account/Logout";
options.AccessDeniedPath = $"/Identity/Account/AccessDenied";
});
}
// 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();
app.UseDatabaseErrorPage();
}
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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseNToastNotify();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Student}/{action=Index}/{id?}");
});
}
}
With all the information above, what really went wrong with my implementation?
Check the IdentityHostingStartup.cs and comment out below line if it exits :
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ICFERAppIdentityDbContext>();
In short only one identity configuration is needed . Here is a related discussion .

Access IMemoryCache in IServiceCollection extension

I have registered IMemoryCache by calling services.AddMemoryCache() in my Startup.cs
What I want to do do is to be able to use this in an IServiceCollection extension method where I add JWT Authentication. The signing key for the token is stored in Azure Keyvault and I'd like to cache the key when I retrieve it
public static IServiceCollection AddJWTAuth(this IServiceCollection services)
{
services.AddAuthentication(options=>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwt =>
{
jwt .TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, tokenValidationParameters) =>
{
// Get signing from KV and add it to memory cache
// Use signing key from cache
}
};
});
return services;
}
What's a good way of being able to do this?
When you need to access what is already inside your service collection, you can use BuildServiceProvider() which will give you a service provider with the so far configured services. Note that you must add your memory cache before your AddJWTAuth method.
public static IServiceCollection AddJWTAuth(this IServiceCollection services)
{
var cache = services.BuildServiceProvider().GetRequiredService<IMemoryCache>()
services.AddAuthentication(options=>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwt =>
{
jwt .TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, tokenValidationParameters) =>
{
cache..GetOrCreate(...);
}
};
});
return services;
}

Categories