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 .
Related
This is my DbContext.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<IdentityUserLogin<string>>(entity => entity.Property(m => m.LoginProvider).HasMaxLength(760));
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySQL("server=localhost;database=authentication;user=root;password=root");
}
}
This is 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.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWTAuthentication", Version = "v1" });
});
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseMySQL("Server=127.0.0.1;Database=authentication;Uid=root;Pwd=root");
});
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = Configuration["JWT:ValidAudience"],
ValidIssuer = Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.Latin1.GetBytes(Configuration["JWT:Secret"]))
};
});
}
// 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();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWTAuthentication v1"));
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: GenericArguments[1], 'Microsoft.AspNetCore.Identity.IdentityRole', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type 'TRole'.
Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
I have simple asp.net core app
Startup:
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)
{
// This is required to be instantiated before the OpenIdConnectOptions starts getting configured.
// By default, the claims mapping will map claim names in the old format to accommodate older SAML applications.
// 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role' instead of 'roles'
// This flag ensures that the ClaimsIdentity claims collection will be built from the claims in the token
// JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
// Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "LuloWebApiCoreMac", Version = "v1" });
});
}
// 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();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LuloWebApiCoreMac v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Program
public class Program
{
public static void Main(string[] args) {
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
appsettings.json
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "xx.com.co",
"TenantId": "xx-c220-48a2-a73f-1177fa2c098e",
"ClientId": "xx-3737-48a5-a6c0-7e3bc4f9a5c9"
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "https://localhost:44351"
}
}
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
As you can see the Client ID is set
However when I do dotnet build and dotnet run and go to the site, I get this error:
OptionsValidationException: IDW10106: The 'ClientId' option must be provided.
Microsoft.Extensions.Options.OptionsFactory<TOptions>.Create(string name)
Microsoft.Extensions.Options.OptionsMonitor<TOptions>+<>c__DisplayClass11_0.<Get>b__0()
System.Lazy<T>.ViaFactory(LazyThreadSafetyMode mode)
System.Lazy<T>.ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor)
System.Lazy<T>.CreateValue()
System.Lazy<T>.get_Value()
Microsoft.Extensions.Options.OptionsCache<TOptions>.GetOrAdd(string name, Func<TOptions> createOptions)
Microsoft.Extensions.Options.OptionsMonitor<TOptions>.Get(string name)
Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderExtensions+<>c__DisplayClass3_0.<AddMicrosoftIdentityWebApiImplementation>b__0(JwtBearerOptions options, IServiceProvider serviceProvider, IOptionsMonitor<MicrosoftIdentityOptions> microsoftIdentityOptionsMonitor)
Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions, TDep1, TDep2>.Configure(string name, TOptions options)
Microsoft.Extensions.Options.OptionsFactory<TOptions>.Create(string name)
Microsoft.Extensions.Options.OptionsMonitor<TOptions>+<>c__DisplayClass11_0.<Get>b__0()
System.Lazy<T>.ViaFactory(LazyThreadSafetyMode mode)
System.Lazy<T>.ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor)
System.Lazy<T>.CreateValue()
System.Lazy<T>.get_Value()
Microsoft.Extensions.Options.OptionsCache<TOptions>.GetOrAdd(string name, Func<TOptions> createOptions)
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.AuthenticationService.AuthenticateAsync(HttpContext context, string scheme)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
You should specify the name you're trying to bind the configuration to. The error is saying it can't find ClientId.
In your case, this should be services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd");
You can read more about the options pattern in ASP.NET Core here.
I need to allow access to a directory of static files only if the user is authenticated.
I have set up a middleware to check each request as it comes. I can get the request no problem, but the Httpcontext User is always null. I am using cookies authentication and not Identity. I have added the default authentication cookies scheme to the sign in as well as the services in startup.cs. I'm not sure why I can't get the user. This is based off of Scott Allen's tutorial https://odetocode.com/blogs/scott/archive/2015/10/06/authorization-policies-and-middleware-in-asp-net-5.aspx
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.
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.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication( options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.AccessDeniedPath = "/Home/Index";
options.LoginPath = "/Identity/Account/Login";
});
services.AddAuthorization(options =>
{
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
// 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.UseProtectFolder(new ProtectFolderOptions
{
Path = "/StaticFiles",
PolicyName = "Authenticated",
});
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "Static_Files")),
RequestPath = "/StaticFiles"
});
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
private object RedirectResult()
{
throw new NotImplementedException();
}
}
MiddleWare
public class ProtectFolderOptions
{
public PathString Path { get; set; }
public string PolicyName { get; set; }
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class ProtectFolderExtensions
{
public static IApplicationBuilder UseProtectFolder(this IApplicationBuilder builder, ProtectFolderOptions options)
{
return builder.UseMiddleware<ProtectFolder>(options);
}
}
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class ProtectFolder
{
private readonly RequestDelegate _next;
private readonly PathString _path;
private readonly string _policyName;
public ProtectFolder(RequestDelegate next,ProtectFolderOptions options)
{
_next = next;
_path = options.Path;
_policyName = options.PolicyName;
}
public async Task Invoke(HttpContext httpContext, IAuthorizationService authorizationService)
{
if (httpContext.Request.Path.StartsWithSegments(_path))
{
var authorized = await authorizationService.AuthorizeAsync(
httpContext.User, null, _policyName);
if (authorized.Succeeded == false)
{
await httpContext.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return;
}
}
await _next(httpContext);
}
}
The middleware is able to check the request. But httpcontext doesn't contain user even after I signed in, and I always get redirected to the login page.
After adding [Authorize] to a controller, I'm always getting a 401 from it. While debugging, I see the return AuthenticateResult.Success being reached, but the code of the controller never is.
What am I doing wrong?
Below is the code for my Startup class and Custom auth classes.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials());
});
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Custom Scheme";
options.DefaultChallengeScheme = "Custom Scheme";
}).AddCustomAuth(o => { });
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("CorsPolicy");
var options = new RewriteOptions().AddRedirectToHttps();
app.UseRewriter(options);
app.UseAuthentication();
app.UseMvc();
}
}
public class CustomAuthOptions : AuthenticationSchemeOptions
{
public ClaimsIdentity Identity { get; set; }
public CustomAuthOptions()
{
}
}
public static class CustomAuthExtensions
{
public static AuthenticationBuilder AddCustomAuth(this AuthenticationBuilder builder, Action<CustomAuthOptions> configureOptions)
{
return builder.AddScheme<CustomAuthOptions, CustomAuthHandler>("Custom Scheme", "Custom Auth", configureOptions);
}
}
internal class CustomAuthHandler : AuthenticationHandler<CustomAuthOptions>
{
public CustomAuthHandler(IOptionsMonitor<CustomAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
string token = Request.Headers["Authorization"];
if (string.IsNullOrEmpty(token))
return AuthenticateResult.Fail("Failing string");
// Using external service to validate token and get user id
int Id = GetUserId(token);
return AuthenticateResult.Success(
new AuthenticationTicket(
new ClaimsPrincipal(
new ClaimsIdentity(
new List<Claim>() { new Claim(ClaimTypes.Sid, Id.ToString()) })),
Scheme.Name));
}
}
The problem is caused by the way you create an instance of ClaimsIdentity in CustomAuthHandler.HandleAuthenticateAsync(). The value of principal.Identity.IsAuthenticated will be false that makes AuthorizeAttribute to consider your request unathorized.
The reason why IsAuthenticated is set to false is described here in detail. To fix it, just use ClaimsIdentity constructor overload that takes authenticationType:
return AuthenticateResult.Success(
new AuthenticationTicket(
new ClaimsPrincipal(
new ClaimsIdentity(
new List<Claim>() { new Claim(ClaimTypes.Sid, Id.ToString()) }, Scheme.Name)),
Scheme.Name));
I am trying to get a token from asp.net core app using OpenIddict.
When I request a token from my endpoint, I am getting this exception:
System.InvalidOperationException: Cannot use table 'OpenIddictApplications' in schema '' for entity 'OpenIddictApplication' since it is being used for another entity.
at Microsoft.EntityFrameworkCore.Internal.ModelValidator.ShowError(String message)
at Microsoft.EntityFrameworkCore.Internal.RelationalModelValidator.EnsureDistinctTableNames(IModel model)
at Microsoft.EntityFrameworkCore.Internal.RelationalModelValidator.Validate(IModel model)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass16_0.<RealizeService>b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServiceCollectionExtensions.<>c.<AddQuery>b__1_3(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass16_0.<RealizeService>b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.EntityFrameworkCore.DbContext.get_QueryProvider()
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.<.ctor>b__3_0()
at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
at OpenID.Controllers.AuthorizeController.<Exchange>d__2.MoveNext() in C:\Users\Enrique_Garcia\Desktop\OpenID\OpenID\Controllers\AuthorizeController.cs:line 35
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__27.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextActionFilterAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextResourceFilter>d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
....
I have already seen this but I am not using Asp.Net Identity.
ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddDbContext<UniverContext>(options =>
{
options.UseSqlServer(Configuration["Data:ConnectionString"]);
options.UseOpenIddict();
});
services.AddOpenIddict(options =>
{
options.AddEntityFrameworkCoreStores<UniverContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/connect/token");
options.AllowPasswordFlow().AllowRefreshTokenFlow();
options.SetAccessTokenLifetime(TimeSpan.FromMinutes(1));
options.SetRefreshTokenLifetime(TimeSpan.FromMinutes(2));
options.DisableHttpsRequirement();
options.UseJsonWebTokens();
options.AddEphemeralSigningKey();
});
}
Configure:
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseDeveloperExceptionPage();
app.UseOpenIddict();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = "http://localhost:5000",
Audience = "resource_server",
RequireHttpsMetadata = false,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = OpenIdConnectConstants.Claims.Subject,
RoleClaimType = OpenIdConnectConstants.Claims.Role
}
});
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvcWithDefaultRoute();
app.UseWelcomePage();
}
This is my UniverContext class:
public partial class UniverContext : DbContext
{
public virtual DbSet<Alumno> Alumno { get; set; }
public virtual DbSet<AlumnoInscrito> AlumnoInscrito { get; set; }
public virtual DbSet<Genero> Genero { get; set; }
public virtual DbSet<OfertaEducativa> OfertaEducativa { get; set; }
public virtual DbSet<OpenIddictApplications> OpenIddictApplications { get; set; }
public virtual DbSet<OpenIddictAuthorizations> OpenIddictAuthorizations { get; set; }
public virtual DbSet<OpenIddictScopes> OpenIddictScopes { get; set; }
public virtual DbSet<OpenIddictTokens> OpenIddictTokens { get; set; }
public virtual DbSet<Usuario> Usuario { get; set; }
public virtual DbSet<UsuarioDetalle> UsuarioDetalle { get; set; }
public UniverContext(DbContextOptions<UniverContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//modelBuilder.UseOpenIddict();
modelBuilder.Entity<Alumno>(entity =>
{
entity.HasIndex(e => e.GeneroId)
.HasName("IX_Alumno_GeneroId");
entity.Property(e => e.AlumnoId).ValueGeneratedNever();
entity.Property(e => e.GeneroId).HasDefaultValueSql("0");
entity.Property(e => e.Nombre).IsRequired();
entity.HasOne(d => d.Genero)
.WithMany(p => p.Alumno)
.HasForeignKey(d => d.GeneroId);
});
modelBuilder.Entity<AlumnoInscrito>(entity =>
{
entity.HasKey(e => new { e.AlumnoId, e.OfertaEducativaId })
.HasName("PK_AlumnoInscrito");
entity.HasIndex(e => e.OfertaEducativaId)
.HasName("IX_AlumnoInscrito_OfertaEducativaId");
entity.HasOne(d => d.Alumno)
.WithMany(p => p.AlumnoInscrito)
.HasForeignKey(d => d.AlumnoId);
entity.HasOne(d => d.OfertaEducativa)
.WithMany(p => p.AlumnoInscrito)
.HasForeignKey(d => d.OfertaEducativaId);
entity.HasOne(d => d.Usuario)
.WithMany(p => p.AlumnoInscrito)
.HasForeignKey(d => d.UsuarioId)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_AlumnoInscrito_Usuario");
});
modelBuilder.Entity<Genero>(entity =>
{
entity.Property(e => e.GeneroId).ValueGeneratedNever();
entity.Property(e => e.Descripcion).IsRequired();
});
modelBuilder.Entity<OfertaEducativa>(entity =>
{
entity.Property(e => e.OfertaEducativaId).ValueGeneratedNever();
entity.Property(e => e.Descripcion).IsRequired();
});
modelBuilder.Entity<OpenIddictApplications>(entity =>
{
entity.HasIndex(e => e.ClientId)
.HasName("IX_OpenIddictApplications_ClientId")
.IsUnique();
entity.Property(e => e.Id).HasMaxLength(450);
entity.Property(e => e.ClientId)
.IsRequired()
.HasMaxLength(450);
});
modelBuilder.Entity<OpenIddictAuthorizations>(entity =>
{
entity.HasIndex(e => e.ApplicationId)
.HasName("IX_OpenIddictAuthorizations_ApplicationId");
entity.Property(e => e.Id).HasMaxLength(450);
entity.Property(e => e.ApplicationId).HasMaxLength(450);
entity.HasOne(d => d.Application)
.WithMany(p => p.OpenIddictAuthorizations)
.HasForeignKey(d => d.ApplicationId);
});
modelBuilder.Entity<OpenIddictScopes>(entity =>
{
entity.Property(e => e.Id).HasMaxLength(450);
});
modelBuilder.Entity<OpenIddictTokens>(entity =>
{
entity.HasIndex(e => e.ApplicationId)
.HasName("IX_OpenIddictTokens_ApplicationId");
entity.HasIndex(e => e.AuthorizationId)
.HasName("IX_OpenIddictTokens_AuthorizationId");
entity.Property(e => e.Id).HasMaxLength(450);
entity.Property(e => e.ApplicationId).HasMaxLength(450);
entity.Property(e => e.AuthorizationId).HasMaxLength(450);
entity.HasOne(d => d.Application)
.WithMany(p => p.OpenIddictTokens)
.HasForeignKey(d => d.ApplicationId);
entity.HasOne(d => d.Authorization)
.WithMany(p => p.OpenIddictTokens)
.HasForeignKey(d => d.AuthorizationId);
});
modelBuilder.Entity<Usuario>(entity =>
{
entity.Property(e => e.UsuarioId).HasDefaultValueSql("0");
entity.Property(e => e.Nombre).IsRequired();
entity.Property(e => e.Paterno)
.IsRequired()
.HasDefaultValueSql("N''");
});
modelBuilder.Entity<UsuarioDetalle>(entity =>
{
entity.HasKey(e => e.UsuarioId)
.HasName("PK_UsuarioDetalle");
entity.Property(e => e.UsuarioId).ValueGeneratedNever();
entity.Property(e => e.Password)
.IsRequired()
.HasColumnType("varchar(50)");
entity.HasOne(d => d.Usuario)
.WithOne(p => p.UsuarioDetalle)
.HasForeignKey<UsuarioDetalle>(d => d.UsuarioId)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_UsuarioDetalle_Usuario");
});
}
}
And what my csproj file contains is:
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AspNetContribOpenIdExtensionsVersion>1.0.0-*</AspNetContribOpenIdExtensionsVersion>
<OpenIddictVersion>1.0.0-*</OpenIddictVersion>
<PackageTargetFallback>portable-net45+win8</PackageTargetFallback>
</PropertyGroup>
<PackageReference Include="AspNet.Security.OAuth.Validation" Version="1.0.0-rtm-0255" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.1.0" />
<PackageReference Include="OpenIddict" Version="$(OpenIddictVersion)" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="$(OpenIddictVersion)" />
<PackageReference Include="OpenIddict.Mvc" Version="$(OpenIddictVersion)" />
The packages openiddict version's is 1.0.0-beta2-0607
What I was trying and it works is this is my AuthorizationController:
if (request.Username != "email#email.com")
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid"
});
}
if (request.Password != "123456+")
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid"
});
}
But after register my DbContext in Startup class, I am trying to use database user's something like:
var user = _context.Usuario.SingleOrDefault(x => x.UsuarioId == int.Parse(request.Username));
In this line, I am getting the error described.
I created a repo if you want to see my AuthorizationController.
The exception sounds clear to me: OpenIddict registers the default entities when you call options.AddOpenIddict() but your own DbContext defines different entities that end up using the same table names (OpenIddictApplications/OpenIddictAuthorizations/OpenIddictScopes/OpenIddictTokens), which is considered as an illegal operation by EntityFramework, given that the entity types differ.
You have two options to fix that:
Use the default OpenIddict entities (e.g OpenIddictApplication vs OpenIddictApplications).
Tell OpenIddict that you prefer using your own entities instead of the built-in ones:
// Register the OpenIddict services.
services.AddOpenIddict(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>()
.ReplaceDefaultEntities<OpenIddictApplications, OpenIddictAuthorizations, OpenIddictScopes, OpenIddictTokens, string>();
// ...
});