I am trying to localize an ASP.Net Core 6 Web Application using RouteDataRequestCultureProvider.
I have configured Program.cs file as follows
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddLocalization(options => { options.ResourcesPath = "Resources"; });
builder.Services.AddMvc().AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = "Resources"; }).AddDataAnnotationsLocalization();
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("el-GR"),
new CultureInfo("en-US"),
};
var requestLocalizationOptions = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-US"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
requestLocalizationOptions.RequestCultureProviders.Clear();
RequestCultureProvider requestProvider = new RouteDataRequestCultureProvider();
requestLocalizationOptions.RequestCultureProviders.Insert(0, requestProvider);
var app = builder.Build();
app.UseRequestLocalization(requestLocalizationOptions);
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{culture}/{controller=Home}/{action=Index}/{id?}");
app.Run();
I have included the following Resource files
Resources/Views/Home/Index.el-GR.resx
Resources/Views/Home/Index.en-US.resx
I have included in Index.cshtml
#inject IViewLocalizer Localizer
and use it #Localizer["PropertyInResxFile"] in Index View file
I make the following requests
el-GR/Home/Index
en-US/Home/Index
I always get the default Culture localization (from Index.en-US.resx)
Thanks
Related
I'm currently building a Razor web application in .NET6 who have 3 cultures.
The problem is all my anchor tags are empty in the DOM, when I click on the Contact link, it doesn't do anything.
<a class="" asp-area="" asp-page="/Contact">Contact</a>
Here is my Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages()
.AddRazorPagesOptions(options =>
{
options.Conventions.AddFolderRouteModelConvention("/", model =>
{
foreach (var selector in model.Selectors)
{
selector.AttributeRouteModel.Template = AttributeRouteModel.CombineTemplates("{culture?}", selector.AttributeRouteModel.Template);
}
});
});
builder.Services.AddMvc().AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix).AddDataAnnotationsLocalization();
builder.Services.Configure<RequestLocalizationOptions>(opt =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en"),
new CultureInfo("fr"),
new CultureInfo("nl")
};
opt.DefaultRequestCulture = new RequestCulture("en");
opt.SupportedCultures = supportedCultures;
opt.SupportedUICultures = supportedCultures;
opt.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider()
{
RouteDataStringKey = "culture",
UIRouteDataStringKey = "culture",
Options = opt
});
});
builder.Services.AddHttpContextAccessor();
builder.Services.AddLocalization(opt => { opt.ResourcesPath = "Resources"; });
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
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();
}
var options = ((IApplicationBuilder)app).ApplicationServices.GetRequiredService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
//app.UseRequestLocalization(((IApplicationBuilder)app).ApplicationServices.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
app.MapRazorPages();
app.Run();
When I manually browse to www.website.com/fr/contact-us then it works, but then when I click on the below anchor (Home), my url change to www.website.com/fr/contact-us/Index
<a asp-area="" asp-page="/">Home</a>
What am I missing?
Add the culture param to the route for all links:
<a asp-area=""
asp-page="/"
asp-route-culture="#CultureInfo.CurrentCulture.Name">Home</a>
Another way:
#{
var cltr = System.Globalization.CultureInfo.CurrentCulture.Name;
}
Home
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'm writing an ASP.Net Core app based on Freeman's book.
And while adding Blazor pages, I got a problem. When you try to go to pages that Blazor is connected to, nothing is displayed on them.
The browser console shows these errors:
Error: System.NullReferenceException: Object reference not set to an
instance of an object. at
Microsoft.AspNetCore.Components.Routing.Router.Refresh(Boolean
isNavigationIntercepted) at
Microsoft.AspNetCore.Components.Routing.Router.SetParametersAsync(ParameterView
parameters)
My Routed.razor file:
<Router AppAssembly="#typeof(Program).Assembly"
AdditionalAssemblies="new[]{typeof(Program).Assembly}">
<Found>
<RouteView RouteData="#context" DefaultLayout="typeof(AdminLayout)" />
</Found>
<NotFound>
<h4 class="bg-danger text-white text-center p-2">
No Matching Route Found
</h4>
</NotFound>
What could be the problem?
I also add a link to the repository of my project:
https://github.com/itehnoviking/CoinStore
I had to "Hack" your Program to get your solution running. Here's my version:
using CoinStore.Models;
using Microsoft.EntityFrameworkCore;
namespace CoinStore
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<StoreDbContext>(opt => opt.UseSqlServer(connectionString));
builder.Services.AddScoped<IStoreRepository, EFStoreRepository>();
builder.Services.AddScoped<IOrderRepository, EFOrderRepository>();
builder.Services.AddRazorPages();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();
builder.Services.AddScoped<Cart>(sp => SessionCart.GetCart(sp));
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//builder.Services.AddServerSideBlazor();
//builder.Services.AddServerSideBlazor().AddCircuitOptions(o => {
// o.DetailedErrors = _env.IsDevelopment;
//}).AddHubOptions(opt => {
// opt.MaximumReceiveMessageSize = 10 * 1024 * 1024; // 10MB
//});
builder.Services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; });
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseDeveloperExceptionPage();
//app.UseStatusCodePages();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseRouting();
app.UseAuthorization();
//app.UseEndpoints(endpoints => {
// endpoints.MapControllerRoute("catpage",
// "{category}/Page{productPage:int}",
// new { Controller = "Home", action = "Index" });
// endpoints.MapControllerRoute("page", "Page{productPage:int}",
// new { Controller = "Home", action = "Index", productPage = 1 });
// endpoints.MapControllerRoute("category", "{category}",
// new { Controller = "Home", action = "Index", productPage = 1 });
// endpoints.MapControllerRoute("pagination",
// "Products/Page{productPage}",
// new { Controller = "Home", action = "Index", productPage = 1 });
// endpoints.MapDefaultControllerRoute();
// endpoints.MapRazorPages();
// endpoints.MapBlazorHub();
// endpoints.MapFallbackToPage("/admin/{*catchall}", "/Admin/Index");
//});
//app.MapControllerRoute("catpage", "{category}/Page{productPage:int}", new { controller = "Home", action = "index" });
//app.MapControllerRoute("page", "Page{productPage:int}", new { controller = "Home", action = "index", productPage = 1 });
//app.MapControllerRoute("category", "{category}", new { controller = "Home", action = "index", productPage = 1 });
//app.MapControllerRoute("pagination", "Products/Page{productPage}", new { controller = "Home", action = "index", productPage = 1 });
//app.MapControllerRoute(
// name: "default",
// pattern: "{controller=Home}/{action=Index}/{id?}");
//app.MapDefaultControllerRoute();
app.MapRazorPages();
app.MapBlazorHub();
app.MapFallbackToPage("/admin/{*catchall}", "/Admin/Index");
//SeedData.EnsurePopulated(app);
app.Run();
}
}
}
I then update Routed.razor - no duplication of assemblies any more which was the main issue.
<Router AppAssembly="#typeof(Program).Assembly">
<Found>
<RouteView RouteData="#context" DefaultLayout="typeof(AdminLayout)" />
</Found>
<NotFound>
<h4 class="bg-danger text-white text-center p-2">
No Matching Route Found
</h4>
</NotFound>
</Router>
And a quick fix to Index to get the references correct:
#page "/admin"
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>CoinStore Admin</title>
<link href="/lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
<base href="~/" />
</head>
<body>
<component type="typeof(Routed)" render-mode="Server" />
<script src="/_framework/blazor.server.js"></script>
</body>
</html>
And I get:
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.