I converted my application from .NET 5 to .NET 6. In the old version the response from the api was very fast. But in the new version it is very slow. The record has only 2 objects. This is my code. Please help. Thank!
In .NET 5 EF core:
namespace AspNetCoreTemplate.IoC
{
public static class NativeInjectorConfig
{
public static void RegisterServices(this IServiceCollection services)
{
services.AddScoped<ISongService, SongService>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
}
}
}
In .Net 6 EF core:
public class CoreModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(GenericRepository<>)).As(typeof(IGenericRepository<>)).InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Service<>)).As(typeof(IGenericService<>)).InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
var apiAssembly = Assembly.GetExecutingAssembly();
var repoAssembly = Assembly.GetAssembly(typeof(DatabaseContext));
var serviceAssembly = Assembly.GetAssembly(typeof(MapProfile));
builder.RegisterAssemblyTypes(apiAssembly, repoAssembly, serviceAssembly).Where(x => x.Name.EndsWith("Repository")).AsImplementedInterfaces().InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(apiAssembly, repoAssembly, serviceAssembly).Where(x => x.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();
}
}
My response:
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers(options => options.Filters.Add(new ValidateFilterAttribute())).AddFluentValidation(x => x.RegisterValidatorsFromAssemblyContaining<UserLoginDtoValidator>());
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidIssuer = builder.Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped(typeof(NotFoundIdFilter<>));
builder.Services.AddScoped(typeof(NotFoundUpdateFilter<>));
builder.Services.AddAutoMapper(typeof(MapProfile));
builder.Services.AddDbContext<DatabaseContext>(x =>
{
x.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"), option =>
{
option.MigrationsAssembly(Assembly.GetAssembly(typeof(DatabaseContext)).GetName().Name);
});
});
builder.Host.UseServiceProviderFactory
(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => containerBuilder.RegisterModule(new CoreModule()));
var app = builder.Build();
Old repo: https://github.com/ThanhDeveloper/AspNetCoreWebApplicationTemplate/blob/faf8296e20/AspNetCoreTemplate/IoC/NativeInjectorConfig.cs
New repo: https://github.com/ThanhDeveloper/AspNetCoreWebApplicationTemplate
I have a dotnet core 5 API with swagger enabled.
I have added authorization in swagger api and works fine token is sent with the request.
But i have a requirement in which i need to send header value along with the request, for that purpose i have created a class:
public class SwaggerHeaderFilter : IOperationFilter {
public void Apply(OpenApiOperation operation, OperationFilterContext context) {
if (operation.Parameters == null)
operation.Parameters = new List<OpenApiParameter>();
var scheme = new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" } };
operation.Security.Add(new OpenApiSecurityRequirement {
[scheme] = new List<string>()
});
operation.Parameters.Add(new OpenApiParameter {
Name = "channelId",
In = ParameterLocation.Header,
Required = false // set to false if this is optional
});
}
}
And in startup.cs file i have used it like this:
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
Description = "Please enter token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
c.OperationFilter<SwaggerHeaderFilter>();
});
But the problem is when i add custom header i am not getting anything in token
I want to access the values of the Configurations Variables in another class.
Here is the appsetting.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Configurations": {
"FirstVerificationValidationDurationMinutes": 2,
"ResendedVerificationValidationDurationMinutes": 2,
"JwtRegisterTokenValidationDurationMinutes": 30,
"JwtLoginTokenValidationDurationMinutes": 30
}
}
And Here I want to use JwtLoginTokenValidationDurationMinutes's value (which is declared inside appsettings.json as you can see ablove) inside the class below:
public async Task<UserCredentialDto> JwtAuthentication(UserCredentialViewModel userCredential)
{
var user = await _userService.LoginUser(userCredential);
if (user is null)
{
return new UserCredentialDto
{
Status = new StatusMaker().ErrorStatus(user.Status.Message)
};
}
//TODO
var _key = "This is for test";
var tokenHandler = new JwtSecurityTokenHandler();
var tokenKey = Encoding.ASCII.GetBytes(_key);
//var tokenValidationDuration = HERE I NEED THE VALUE //HOW TO ACCESS VALUES FROM appsettings.json?
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddMinutes(/*tokenValidationDuration*/),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(tokenKey),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return new UserCredentialDto
{
Token = tokenHandler.WriteToken(token)
};
}
}
So how can I access values from appsettings.json ?
I tried using Microsoft.Extensions.Configuration but it seems it doesn't exist in .Net 6.
var JwtLoginTokenValidationDurationMinutes = builder.Configuration.GetSection("Configurations:JwtLoginTokenValidationDurationMinutes").Value;
I had identityProviderService working with my API and web app, then I updated i updated my dependecies(identityServer4) and that resulted constantly in the following error either I use code or I hybrid.
IdentityServer4 Error: Invalid_scope
I checked the log and the scope was fine but I can resolve why I keep getting this error! The details as follows:
My local Idp (Identityserver4) log and the main error =>
IdentityServer4.Hosting.IdentityServerMiddleware: Information:
Invoking IdentityServer endpoint:
IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
IdentityServer4.Validation.DefaultResourceValidator: Error: Scope
Bd.Web.Api not found in store.
IdentityServer4.Endpoints.AuthorizeEndpoint: Error: Request validation
failed IdentityServer4.Endpoints.AuthorizeEndpoint: Information: {
"ClientId": "BdWebAppClientId", "ClientName": "Bd web Client
Application", "RedirectUri": "https://localhost:xxxxx/signin-oidc",
"AllowedRedirectUris": [
"https://localhost:44386/signin-oidc" ], "SubjectId": "anonymous", "ResponseType": "code", "ResponseMode": "form_post",
"GrantType": "authorization_code", "RequestedScopes": "openid
profile Bd.Web.Api", "State":
"CfDJ8Foioa24zShFmzIFmkTbGBTrbiiEwQYmHGMRUN7FwsfKMgY2-olWJA1XVlAkA0uCWIR6HhMdu2X1exzVKTNFTcAD456Z0r3es5ki377uBEJgjA9jmyQFWWzTZV6_7GEmIC39xUh_b_YAqXgtzO0olZ52beNFuxruk_NshL47NhwcaETCH2cy3XTvRN0NTxZHmxVWglo13iSE7RVpNghHc7pBW7jCv7cB2RldQnEvsJ4s56AdiICw9sdKEJ5mQNoXngshanycX4MmD3qaW0TX6knY43pAqMuPgEEVqd7BXKt_koQfiQuAP3pQNbsyOAb1jtoZ8egUHiKgXjofs8ci2i4",
"PromptMode": "", "Raw": {
"client_id": "BdWebAppClientId",
"redirect_uri": "https://localhost:xxxxx/signin-oidc",
"response_type": "code",
"scope": "openid profile Bd.Web.Api",
"response_mode": "form_post",
"nonce": "637284460180108591.ZjIxYjhlZGEtYjk0Mi00M2UxLWExNWItOGYzMjhjODEyMGQzZmU5NjZmZDAtOTQwYi00YTFlLWJlMWUtM2U3YzBhM2NmNjQ4",
"state": "CfDJ8Foioa24zShFmzIFmkTbGBTrbiiEwQYmHGMRUN7FwsfKMgY2-olWJA1XVlAkA0uCWIR6HhMdu2X1exzVKTNFTcAD456Z0r3es5ki377uBEJgjA9jmyQFWWzTZV6_7GEmIC39xUh_b_YAqXgtzO0olZ52beNFuxruk_NshL47NhwcaETCH2cy3XTvRN0NTxZHmxVWglo13iSE7RVpNghHc7pBW7jCv7cB2RldQnEvsJ4s56AdiICw9sdKEJ5mQNoXngshanycX4MmD3qaW0TX6knY43pAqMuPgEEVqd7BXKt_koQfiQuAP3pQNbsyOAb1jtoZ8egUHiKgXjofs8ci2i4",
"x-client-SKU": "ID_NETSTANDARD2_0",
"x-client-ver": "5.5.0.0" }
My config comprises of three classes User, Resources and Client as follows:
public class Users
{
public static List<TestUser> Get()
{
return new List<TestUser> {
new TestUser {
SubjectId = "5BE86359-073C-434B-AD2D-A3932222DABE",
Username = "scott",
Password = "password",
Claims = new List<Claim> {
new Claim(JwtClaimTypes.Email, "scott#scottbrady91.com"),
new Claim(JwtClaimTypes.Role, "admin")
}
}
};
}
}
public class Resources
{
public static IEnumerable<IdentityResource> Ids()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Address()
};
}
public static IEnumerable<ApiResource> Apis()
{
return new List<ApiResource>
{
new ApiResource("Bd.Web.Api", "Bd Web Api") {
Description = "BD API Access",
//UserClaims = new List<string> {"role"},
ApiSecrets = new List<Secret> {new Secret("secret".Sha256())}
}
};
}
}
public class Clients
{
public static IEnumerable<Client> Get()
{
return new List<Client> {
new Client {
ClientId = "BdWebAppClientId",
ClientName = "Bd web Client Application",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = false,
ClientSecrets = new List<Secret> {
new Secret("secret".Sha256())},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Address,
"Bd.Web.Api"
},
RedirectUris = new List<string>{"https://localhost:44386/signin-oidc"},
PostLogoutRedirectUris = new List<string>{ "https://localhost:44386/sigout-callback-oidc" }
}
};
}
}
public class Startup
{
// 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.AddControllersWithViews();
services.AddIdentityServer()
.AddInMemoryClients(Clients.Get())
.AddInMemoryIdentityResources(Resources.Ids())
.AddInMemoryApiResources(Resources.Apis())
.AddTestUsers(Users.Get())
.AddDeveloperSigningCredential();
}
// 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.UseRouting();
app.UseIdentityServer();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
//endpoints.MapGet("/", async context =>
//{
// await context.Response.WriteAsync("Hello World!");
//});
});
}
}
My web app startup as follows:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
}
public IConfiguration Configuration { get; }
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.None;
});
services.AddMvc(options => { options.EnableEndpointRouting = false; }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddHttpContextAccessor();
services.AddTransient<BearerTokenHandler>();
services.AddHttpClient("ApiClient", client =>
{
//client.BaseAddress = new Uri("https://bandapi.azurewebsites.net/api/");
client.BaseAddress = new Uri("https://localhost:44301/api/");
//client.BaseAddress = new Uri("https://192.168.1.25:44301/api/");
client.Timeout = new TimeSpan(0, 0, 30);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<BearerTokenHandler>();
services.AddHttpClient("IdpClient", client =>
{
client.BaseAddress = new Uri("https://localhost:xxxxx/");
client.Timeout = new TimeSpan(0, 0, 30);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<BearerTokenHandler>();
services.AddAuthentication(options =>
{
options.DefaultScheme = "BdWebAppCookies";
options.DefaultChallengeScheme = "oidc";
//options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
//options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie("BdWebAppCookies" /*CookieAuthenticationDefaults.AuthenticationScheme*/, options =>
{
options.AccessDeniedPath = "/Authentication/AccessDenied";
})
.AddOpenIdConnect("oidc" /*OpenIdConnectDefaults.AuthenticationScheme*/, options =>
{
options.Authority = "https://localhost:xxxx/";
//options.RequireHttpsMetadata = true;
options.ClientId = "BdWebAppClientId";
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("address");
options.Scope.Add("Bd.Web.Api");
options.ClientSecret = "secret";
options.ResponseType = "code";
options.UsePkce = false;
options.SignInScheme = "BdWebAppCookies";
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
In IdentityServer4 4.x the handling of api scopes was changed. Now you need to define all available api scopes and provide them to the configuration.
The updated documentation shows how it's done. https://identityserver4.readthedocs.io/en/latest/topics/resources.html#scopes
public static IEnumerable<ApiScope> GetApiScopes()
{
return new List<ApiScope>
{
new ApiScope(name: "read", displayName: "Read your data."),
new ApiScope(name: "write", displayName: "Write your data."),
new ApiScope(name: "delete", displayName: "Delete your data.")
};
}
Then add the scopes to your IdentityServer configuration:
services.AddIdentityServer()
.AddInMemoryApiScopes(Config.GetApiScopes());
Furthermore you need to add the scopes to your already defined ApiResources as described here: https://identityserver4.readthedocs.io/en/latest/topics/resources.html#api-resources
public static readonly IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("invoices", "Invoice API")
{
Scopes = { "invoice.read", "invoice.pay", "manage" }
},
new ApiResource("customers", "Customer API")
{
Scopes = { "customer.read", "customer.contact", "manage" }
}
};
}
I was had the same problem after updating to .NET Core 3.1 and IndentityServer4 V4.0.0. Only way I could get it to work is to remove the scope parameter on the client API request, using Angular 9 with angular-oauth2-oidc V9.2.1
const header = new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' });
const params = new HttpParams()
.append('username', userName)
.append('password', password)
.append('client_id', this.clientId)
.append('grant_type', 'password');
this.oauthService.issuer = this.baseUrl;
return from(this.oauthService.loadDiscoveryDocument())
.pipe(mergeMap(() => {
return this.http.post(this.oauthService.tokenEndpoint, params, { headers: header });
}));
This is NOT the way to do this, if you remove the scope then none of the profile information will be returned.
I am currently using ASP.NET Core Identity. I cannot figure out the setting to extend the session length but I keep getting logged out - I assume there's a sliding expiration of ~20 minutes, but I can't find the setting. Note, I am using Google as external OAuth.
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
o.SecurityStampValidationInterval = TimeSpan.FromHours(8);
o.Cookies.ExternalCookie.ExpireTimeSpan = TimeSpan.FromHours(8);
o.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromHours(8);
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
app.UseIdentityServer();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = $"http://localhost:55504/",
RequireHttpsMetadata = false,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"name",
"given_name",
"family_name",
"role"
}
});
var googleOptions = serviceProvider.GetRequiredService<GoogleOptions>();
app.UseGoogleAuthentication(new GoogleOptions
{
AuthenticationScheme = "Google",
SignInScheme = "Identity.External",
ClientId = googleOptions.ClientId,
ClientSecret = googleOptions.ClientSecret
});
This question\answer is specific to Identity Server 4.
You would do something like in your Configure:
app.UseGoogleAuthentication(new GoogleOptions
{
SignInScheme = "Identity.External", // this is the name of the cookie middleware registered by UseIdentity()
ClientId = Configuration["ExternalAuthentication:Google:ClientId"],
ClientSecret = Configuration["ExternalAuthentication:Google:ClientSecret"]
});
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = $"http://localhost:55504/",
RequireHttpsMetadata = false,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"name",
"given_name",
"family_name",
"role"
}
// CookieLifetime default is 10 Hours
Authentication.CookieLifetime = TimeSpan.FromHours(24);
// Default CookieSlidingExpiration = false;
Authentication.CookieSlidingExpiration = true;
});
and in your ConfigureServices
// Identity
// https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity
// http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html
services.AddIdentity<ApplicationUser, IdentityRole>(o => {
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<AuthDbContext>()
.AddDefaultTokenProviders();