I wanted to add my own custom data for users so I was following the tutorial
Add, download, and delete custom user data to Identity in an ASP.NET Core project
I already had an existing application so I could not follow that tutorial line by line (my existing application has a database of users already). I didn't get very far in it when I hit the error: System.InvalidOperationException: Scheme already exists: Identity.Application
I used the scaffolder to try to add ... (? the code ?)
I've gone through the links below but to no avail
Scheme already exists: Identity.Application #8223 seems most relevant
InvalidOperationException: Scheme already exists ... #1412
AddIdentity() fails "InvalidOperationException: Scheme already exists: Identity.Application"
It seems like a lot of other people add problems with calling the identity twice but I'm only calling it once in my code. I've also tried commenting out the line entirely in the startup and then it says there is none defined and gets mad at me. I've also tried switch form the default as shown below.
{
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.AddIdentity<IdentityUser, IdentityRole>()
// services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<WebApp1.Models.WebApp1Context>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// 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("/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.UseAuthentication();
app.UseMvc();
}
I feel like I shouldn't be getting the exception thrown and yet.... any advice on a fix?
edit: relevant steps i took until i got this error.
Create project to use invidual user accounts in process creation
override with scaffolder,
and create a secondary user model that you can override.
migrate and update database run.
Try renaming your IdentityUser class to something unique from AspNetIdentity classes. Then make sure you are inheriting from IdentityUser
For example here is a class
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsDisabled { get; set; }
}
And this is the startup
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityContext>()
.AddDefaultTokenProviders();
The marked answer is not the correct answer. I had the same issue and the reason is that when adding ALL identity scaffolding (via dotnet aspnet-codegenerator as Visual Studio crashes ),it creates a IdentityHostingStartup class under areas/identity. This class duplicates the identity setup in startup.cs. So deleting this class fixed the problem.
I had the same error, but the problem was that I called it twice:
_ = services.AddDefaultIdentity<IdentityUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
This ASP Identity Core InvalidOperationException is thrown mostly, when a duplicate call to function exists in Startup.cs or any class in the project using ASP Identity Core, what is required for ASP Identity to work in Startup class:
public void ConfigureServices(IServiceCollection services)
{
_ = services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("AmpCoreDb")));
_ = services.AddDefaultIdentity<IdentityUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
_ = services.AddScoped<AuthenticationStateProvider,
RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
_ = services.AddDatabaseDeveloperPageExceptionFilter();
}
Related
I'll try to describe problem as simple as possible.
Here is my current situation: I have 2 ASP.NET Web API apps. First app issues OAuth access tokens in exchange on user's login and password, let's call this app Issuer. The second app has several controllers, protected with [Authorize] attributes, it allows access to this controllers only for authorized users, who supplied valid access tokens. Let's call this app Consumer.
For now both web apps are hosted on Windows and running on .NET Framework 4.7. Both apps use OWIN and share same machine key - latter is necessary for Consumer app to be able to accept tokens issued by Issuer app.
Here are some details on the Issuer app.
OWIN startup class:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
try
{
var config = new HttpConfiguration();
WebApiConfig.Register(config);
ConfigureAuth(app);
app.UseCors(...);
app.UseWebApi(config);
}
catch (Exception ex)
{
...
}
}
}
public void ConfigureAuth(IAppBuilder app)
{
...
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
Provider = new CustomProvider(...)
};
app.UseOAuthBearerTokens(oAuthServerOptions);
}
CustomProvider class inherits from OAuthAuthorizationServerProvider and contains some logic.
web.config part:
...
<system.web>
<machineKey configSource="....config" />
</system.web>
...
And here are some details of the Consumer app.
OWIN startup class:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
try
{
var config = new HttpConfiguration();
WebApiConfig.Register(config);
ConfigureAuth(app);
app.UseCors(...);
app.UseWebApi(config);
}
catch (Exception ex)
{
...
}
}
}
public void ConfigureAuth(IAppBuilder app)
{
...
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions());
}
Web.config part is completely same as in the Issuer app.
So this is what I have.
And this is what I'm trying to achieve: I need to port Issuer app to .NET 6 and it will run on Linux. But ported app must produce authorization tokens which will be accepted by Consumer app. The Consumer app can not be ported to .NET 6 for now for various reasons.
So I'm trying to setup and use OpenIddict for issuing tokens. But I can't configure OpenIddict to produce tokens compatible with the existing Consumer app.
What I have in ported Issuer app:
protected override void ConfigureServices(IServiceCollection services)
{
...
services.AddIdentityCore<ApplicationUser>(io =>
{
...
})
.AddUserStore<...>()
.AddUserManager<...>()
.AddDefaultTokenProviders();
services.AddOpenIddict()
.AddServer(o =>
{
o.UseDataProtection()
.Configure(c => c.DataProtectionProvider =
new MachineKeyDataProtector("validation key from machine key config file",
"decryption key"));
o.SetTokenEndpointUris("...");
o.SetAccessTokenLifetime(...)
o.DisableAccessTokenEncryption();
o.AllowPasswordFlow();
o.AllowRefreshTokenFlow();
o.UseAspNetCore()
.EnableTokenEndpointPassthrough();
o.AddEphemeralEncryptionKey()
.AddEphemeralSigningKey();
o.EnableDegradedMode();
o.AddEventHandler<OpenIddictServerEvents.ValidateTokenRequestContext>(c => c.UseInlineHandler((context) =>
{
return ValueTask.CompletedTask;
}));
})
}
I'm using MachineKeyDataProtector class from the AspNetTicketBridge NuGet package
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(options =>
{
options.MapControllers();
options.MapDefaultControllerRoute();
});
}
As a result tokens are issued but they are not accepted by the Consumer app - it returns an "401 Unauthorized" error.
What have I done wrong or missed? Or is there any simpler approach?
Any help or suggestion is greatly appreciated :) Many thanks.
I am using .NET 5.0 and HotChoclate framework to create a GraphQL API.
Below is my ConfigureServices Method
public void ConfigureServices(IServiceCollection services)
{
services.AddPooledDbContextFactory<CommanderContext>(options =>
{
options.UseSqlServer(_configuration.GetConnectionString("default"));
});
services.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddFiltering()
.AddSorting()
.AddProjections();
}
To resolve the concurrency issues. I am using AddPooledDbContextFactory() method.
https://chillicream.com/docs/hotchocolate/integrations/entity-framework.
Below is my Configure Method
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
InitializeDatabase(app);
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGraphQL();
});
}
private void InitializeDatabase(IApplicationBuilder app)
{
using var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope();
scope.ServiceProvider.GetRequiredService<CommanderContext>().Database.Migrate(); //Here I get the exception.
}
Since the application is in development, I want the migrations to be automatically applied to the database.
But when I try and get instance of my context class in InitializeDatabase() method, I get the above exception.
Now I have a little understanding that instead of getting a single instance my context class I am getting back a pool of context class.
My Question is: How to I automatically apply the migrations to my database.
Any help is appreciated.
Thanks
AddPooledDbContextFactory method registers IDbContextFactory<TContext> instead the DbContext itself, that's why you can't use GetRequiredService<TContext>. Instead, you should retrieve the factory, use it to obtain TContext instance, do your work and then Dispose it (which in fact will return it to the pool). You can't rely on DI to do that for you because it is not resolved (thus not maintained) by the DI system, but from another abstraction (called factory pattern).
Something like this:
using var context = scope.ServiceProvider
.GetRequiredService<IDbContextFactory<CommanderContext>>()
.CreateDbContext();
context.Database.Migrate();
I am using Azure B2C in a Razor Pages web app on dotnet core 3.1 and I want to log information to my database whenever a user is added or changed. I also want to confirm a user is in the database when they sign in, and add them if they are not. Right now I'm just trying to get the "new user" running. I don't know how to get logging and configuration into my static class given the event has a specific signature.
public UserRepository(ILogger<UserRepository> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
I have been following this article and I have read this post and have implemented a static class with:
public async static Task<Task> OnTicketReceivedCallback(TicketReceivedContext context)
{
//OnTicketReceived
//Check if user just completed signup flow
List<Claim> claims = context.Principal.Claims.ToList();
bool isNewUser = claims.FirstOrDefault(x => x.Type == "newUser") == null ? false : true;
//If so, do what needs to be done
if (isNewUser)
{
//This is where I am getting the error and don't know how to push the logger and configuration through the stack
UserRepository repo = new UserRepository();
}
return Task.CompletedTask;
}
Being called from the startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAdB2C");
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
options.Events.OnTicketReceived = B2CExtensions.OnTicketReceivedCallback
);
services.AddRazorPages()
.AddMicrosoftIdentityUI();
}
Make OnTicketReceivedCallback class non static and register it as a singleton. That way you can pass an ILogger trough the constructor.
I am creating an API. I use swagger but due to a huge number of controllers and actions, I want to split API endpoint by domain. To get this I thought about versioning of the API. I thought about using the Status of ApiVersion. The code of my controllers is below.
[ApiVersion("1.0-First")] //This is ApiVersion MajorVersion = 1, Status = "First"
[Route("api/v{version:apiVersion}/[controller]")]
public class FirstController
[ApiVersion("1.0-Second")]
[Route("api/v{version:apiVersion}/other")]
public class SecondController
My swagger looks fine and the definitions of parts of API are good. (I know that path should be without capital letters - this is for test purposes only)
But swagger can't reach any endpoint. Because the valid endpoint is at /api/v1.0-First/First not /api/v1/First.
My startUp class looks like below:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().AddApiExplorer();
services.AddApiVersioning(c =>
{
c.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader("V"),
new UrlSegmentApiVersionReader());
c.ReportApiVersions = false;
c.DefaultApiVersion = new ApiVersion(1, 0);
});
services.AddVersionedApiExplorer(options =>
{
options.SubstituteApiVersionInUrl = true;
options.SubstitutionFormat = "V";
options.DefaultApiVersion = new ApiVersion(1, 0);
});
services.RegisterSwaggerConfiguration();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseMvc();
app.AddSwagger(app.ApplicationServices.GetService<IApiVersionDescriptionProvider>(), Configuration);
}
There is some static class I wrote to add the dependencies based on IApiVersionDescriptionProvider
public static class SwaggerExtension
{
public static void RegisterSwaggerConfiguration(this IServiceCollection services)
{
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
services.AddSwaggerGen();
}
public static void AddSwagger(this IApplicationBuilder app, IApiVersionDescriptionProvider provider, IConfiguration configuration)
{
var prefix = "swagger";
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.RoutePrefix = string.Empty;
foreach (var description in provider.ApiVersionDescriptions)
{
c.SwaggerEndpoint($"{prefix}/{description.GroupName}/swagger.json", description.GroupName);
}
});
}
}
And another class for SwaggerDoc generation.
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
private readonly IApiVersionDescriptionProvider provider;
private readonly IConfiguration configuration;
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider, IConfiguration configuration)
{
this.provider = provider;
this.configuration = configuration;
}
public void Configure(SwaggerGenOptions options)
{
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
}
private OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var info = new OpenApiInfo()
{
Title = description.GroupName,
Version = description.ApiVersion.ToString(),
};
if (description.IsDeprecated)
{
info.Description += " This API version has been deprecated.";
}
return info;
}
}
I want to get the routing work as api/v1/First or api/v1.0/First (this should not matter).
Maybe writting some custom middleware to handle this case would be good idea?
By now I am out of ideas and in general I couldn't find any articles about using status of ApiVersion.
EDIT:
Changed Title.
We had a similar problem some time ago. We needed to split an Api by a customer privilege/domain. The research took some time as well :), please note that we are using NSwag.
So as you already mentioned (custom middleware) we've created a custom OperationProcessor and used base type checking. Take a look at an example:
services.AddOpenApiDocument(document =>
{
document.Title = "API A";
document.OperationProcessors.Insert(0, new IncludeAApiControllersInSwagger());
});
services.AddOpenApiDocument(document =>
{
document.Title = "API B";
document.OperationProcessors.Insert(0, new IncludeBApiControllersInSwagger());
});
and then
private class IncludeAApiControllersInSwagger : IOperationProcessor
{
public bool Process(OperationProcessorContext context)
{
return IsControllerInType(context, typeof(AApiController));
}
}
private class IncludeBApiControllersInSwagger : IOperationProcessor
{
public bool Process(OperationProcessorContext context)
{
return IsControllerInType(context, typeof(BApiController));
}
}
The last step would be to build a proper inheritance over your controllers.
An API version is always an API version; the values are explicit - by design. There is no universe where 1.0-First can map to an API, but not include the status.
The status is most useful for pre-releases. For example, you might have /first?api-version=1.0-preview.1. When you have a volatile, preview version of an API, this prevents you from having to bump up to 1.1 and so on. 1.0 is greater than 1.0-preview.1.
From your description, it sounds like you want to group or categorize your APIs by an additional level. The Swagger UI only supports a single level of grouping, but ASP.NET API Versioning 7.0+ now has support to make custom group names with API versions easy to configure using the FormatGroupName option.
If your API has a custom group name like this:
[ApiVersion(1.0)]
[ApiExplorerSettings(GroupName = "First")]
[Route("api/v{version:apiVersion}/[controller]")]
public class FirstController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok();
}
You can now configure the combination of both like this:
services.AddApiVersioning()
.AddApiExplorer(
options =>
{
options.SubstituteApiVersionInUrl = true;
options.FormatGroupName = (group, version) => $"{version}-{group}";
});
This only works if you set a custom group name and define a callback. The rules are:
Default configuration; formatted ApiVersion
Group name set, but not callback; use group name
Group name and callback set; result for callback with group and formatted ApiVersion
Only callback set; ignored and uses default configuration as there's no group name
The ApiVersion is formatted according to GroupNameFormat. By default, this will simply be ApiVersion.ToString(). You can still use it if you want to. For example, if GroupNameFormat = "'v'VVV";, then the formatted name via the callback will result in v1-First.
Despite all of this configuration and grouping, the route to your API will still be: api/v1/first. I believe that will get you both of your goals.
Ive been recently using hangfire to process lengthy jobs enabling me to return API calls more efficiently within an ASP MVC Core application.
i have implimented this by adding the following to startup.cs
public class Startup
{
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()));
services.AddHangfire(configuration => configuration.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("AllowAll");
app.UseMvc();
app.UseHangfireDashboard();
app.UseHangfireServer();
}
and then calling this within an action on a controller
using (new BackgroundJobServer())
{
formData.DateCreated = DateTime.Now;
formData.Source = "Web";
formData.StatusItem = _context.Status.FirstOrDefault(x => x.Default == true);
_context.Lead.Add(formData);
_context.SaveChanges();
}
Now i have a need to send a an email at 1am every day with the status of records from the database.
Im slightly confused on the implementation from the following perspective:
-Where do i implement the background job?
-Do i put the call in a method, if so how is this called/
-I cannot find BackgroundJob.AddOrUpdate, which i understand is used for the recurring tasks
-The schedule method takes a timespan object, but all examples are using CRON.
I have all the code to create the email, i just need help scheduling it
Thanks for your help in advance.