I have a blazor WASM Hosted project using IdentityServer4 ( default from VS templates ). When I launch my application, however, I get the following error. Debugging shows that the options.Value.SigningCredential is null so the .Key is causing the NullReferenceException. So I am missing something, somewhere.
Here is the Client Program.cs
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
//builder.RootComponents.Add<App>("#app");
builder.Services.AddHttpClient("BBQFriend.API", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BBQFriend.API"));
builder.Services.AddApiAuthorization();
var baseAddress = new Uri("https://localhost:44395/api/");
void RegisterTypedClient<TClient, TImplementation>(Uri apiBaseUrl)
where TClient : class where TImplementation : class, TClient
{
builder.Services.AddHttpClient<TClient, TImplementation>(client =>
{
client.BaseAddress = apiBaseUrl;
});
}
RegisterTypedClient<ICountryService, CountryService>(baseAddress);
await builder.Build().RunAsync();
}
Here is the Server Startup.cs
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)
{
//Register EntityFramework Core Datacontext for Dependency Injection
services.AddDbContext<DataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//Add common Identity Screens
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<DataContext>();
//Set up IdentityServer
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, DataContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
//Register Services for DirectNavigation
services.AddScoped<ICountryService, CountryService>();
//Register Repositories for Dependency Injection
services.AddScoped<ICountryRepository, CountryRepository>();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddControllersWithViews();
services.AddRazorPages();
}
// 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.UseWebAssemblyDebugging();
}
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.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
//endpoints.MapFallbackToFile("index.html");
endpoints.MapFallbackToPage("/_Host");
});
}
And here is the applications DataContext.cs
public class DataContext : ApiAuthorizationDbContext<ApplicationUser>
{
public DataContext(DbContextOptions options, IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}
public DbSet<Country> Countries { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new CountryConfiguration());
base.OnModelCreating(modelBuilder);
}
}
Issue is that you are missing the client config values.
As you are using builder.Services.AddApiAuthorization(); it tries to load configuration from default:
By default, configuration for the app is loaded by convention from _configuration/{client-id}. By convention, the client ID is set to the app's assembly name. This URL can be changed to point to a separate endpoint by calling the overload with options.
Related
It is my react code to hit the API on submit button
I update the question and add a new class httpresponemessage post
const handleOnPreview = (e) => {
e.preventDefault();
setsubmittext(text);
const ROOT_URL='https://localhost:7113/';
axios.post(`${ROOT_URL}/api/demo-text`, text, {
headers: { 'Content-type':'application/json'}
})
}
This is my controller in ASP.NET Core MVC:
public class HomeController: Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
[HttpPost]
[Route("/api/demo-text")]
[EnableCors("AllowOrigin")]
public HttpResponseMessage Post([FromForm] text text)
{
return new HttpResponseMessage()
{
Content = new StringContent("POST: Test message")
};
}
public IActionResult Privacy()
{
return View();
}
}
Startup.cs
this is the middleware file of my project
MiddleWare
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
last error I get
I want the HTTP post method to post the data.
If you have not yet, you need to define the cors policy in your asp.net core.
In Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(options.DefaultPolicyName,
policy => policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
}
and in your configuration of the HTTP request pipeline method
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... other middlewares
app.UseCors();
// ... other middlewares
}
Attention: this allows for any origin and may cause security problems. Just do that for checking and testing. Not suggested in production.
I have an app using Azure App Services with SQL server and SQL database that are connect to my web app on asp MVC. I've used Distributed Sql Server Cache as a table on my database and so far everything is working well and connected to each other.
Now I want to do two things:
Add entity framework to my app (I already have the database and
connection string)
Run migration – after I've published my app (If I've added for a
example new line or new
table, now I have new version)
I'm not sure how to do those things , I've looked up on many guides and couldn't find an answer. I found a post similar to mine – but using azure functions - here
. I would appreciate it if someone can help me with the steps that I need to follow (like they did in that post) to get entity framework and the migration.
Here is my code:
Program.cs-
using Microsoft.Extensions.Azure;
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
if(!builder.Environment.IsDevelopment())
builder.Configuration.AddAzureKeyVault(new Uri(Environment.GetEnvironmentVariable("VaultUri")), new DefaultAzureCredential());
builder.Services.AddControllersWithViews();
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddBlobServiceClient(builder.Configuration["storage:blob"], preferMsi: true);
clientBuilder.AddQueueServiceClient(builder.Configuration["storage:queue"], preferMsi: true);
});
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("db");
options.SchemaName = "dbo";
options.TableName = "_Cache";
});
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();
Home Controller:
namespace WebAppAzure.Controllers
{
public class HomeController : Controller
{
private readonly BlobServiceClient storage;
private readonly ILogger<HomeController> logger;
private readonly IDistributedCache cache;
public HomeController(BlobServiceClient storage, ILogger<HomeController> logger,
IDistributedCache cache)
{
this.storage = storage;
this.logger = logger;
this.cache = cache;
}
public IActionResult Index()
{
var containerClient = storage.GetBlobContainerClient("public");
var blob = containerClient.GetBlobClient("image.jpeg");
var model = blob.Uri.ToString();
return View(model: model);
}
public IActionResult Privacy()
{
var stringModel = DateTime.Now.ToString();
cache.SetString("name", stringModel);
return View(model: $"SET: {stringModel}");
}
public IActionResult About()
{
var stringModel = cache.GetString("name");
return View(model: $"GET: {stringModel}");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Add entity framework to my app (I already have the database and connection string)
Use below code for add Entity framework and upload to azure app service and run migration command to migrate database.
DBcontext file in project.
using Microsoft.EntityFrameworkCore;
using WebApplication_72783922.Entity;
namespace WebApplication_72783922
{
public class DbConnectionEntity : DbContext
{
public DbConnectionEntity()
{
}
//string connectionString = Environment.GetEnvironmentVariable("ConnectionStrings:dbcon").ToString();
public DbConnectionEntity(DbContextOptions<DbConnectionEntity> options)
: base(options)
{
}
public virtual DbSet<Users> users { get; set; }
public virtual DbSet<department> Departments { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Server=xxxx;Initial Catalog=database;Persist Security Info=False;User ID=adminserver72783922;Password=xxxx;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
}
Program.cs File code.
using Microsoft.Extensions.Azure;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
var builder = WebApplication.CreateBuilder(args);
if (!builder.Environment.IsDevelopment())
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = "Server=xxxx;Initial Catalog=database;Persist Security Info=False;User ID=adminserver72783922;Password=xxxx;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
options.SchemaName = "dbo";
options.TableName = "_Cache";
});
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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Run migration – after I’ve published my app
Enable Migration using this command on Package Manager Console enable-migrations
Then add-migration InitialCreate
Then create migrationadd-migration test-v1
update database update-database -verbose
I'm using the .NET 5.0 React project template. When the app is deployed to my dev environment, some requests to the API controllers are being redirected to index.html. This does not occur when it's running locally.
Most routes continue to work when deployed however, and I cannot see a difference in how the ApiControllers behind them are configured vs the routes that get redirected to index.html.
Here's a controller whose routes are being redirected to index.html:
namespace DonationMembership.Api.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class RedirectController : ControllerBase
{
private readonly IConfigurationRoot configuration;
public RedirectController(IConfiguration configuration)
{
this.configuration = (IConfigurationRoot)configuration;
}
[HttpGet]
public ActionResult OlcUrl()
{
var olcUrl = configuration[Constants.Settings.OlcSite];
return Ok(olcUrl);
}
}
}
And here's one whose routes continue to work when deployed:
namespace DonationMembership.Api.Controllers
{
[ApiController]
[Route("[controller]")]
public class DonationLevelController : ControllerBase
{
private readonly ILogger<DonationLevelController> _logger;
private readonly IDonationLevelService donationLevelService;
private readonly IConfigurationRoot configuration;
public DonationLevelController(ILogger<DonationLevelController> logger,
IDonationLevelService donationLevelService,
IConfiguration configuration)
{
_logger = logger;
this.donationLevelService = donationLevelService;
this.configuration = (IConfigurationRoot)configuration;
}
[HttpGet]
public ActionResult<ODataResult<DonationLevelContract>> Get()
{
return Ok(donationLevelService.GetDonationLevels());
}
}
}
Any insight would be appreciated, thanks! And yes I am using the '/api' prefix when making requests to my redirect route :)
EDIT:
My Startup.cs:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
ConfigureDeps(services);
ConfigureSwagger(services);
ConfigureJwt(services);
services.AddControllers().AddNewtonsoftJson();
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = Constants.Settings.ClientPath;
});
}
// 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", "MyApp v1"));
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseCertificateForwarding();
// UseAuthentication() must come before UseAuthorization() for JWT config to work
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
I haven't included the contents of my three Configure*() helpers. They aren't doing anything related to controller or route configurations.
I created a react project with the react template in asp.net core with individual user accounts. I have looked around and found out I needed to add roles in the startup file and add a profile service, which seems to have half worked.
I can see the roles in my authorization token like this:
"role": [
"admin",
"bookkeeping" ],
But when I add the [Authorize(Roles = "admin")] tag to my controller the requests are now forbidden, even when i can see my token includes the role "admin".
What am I missing or doing wrong here?
This is the Profile service:
public class ProfileService : IProfileService
{
protected UserManager<ApplicationUser> mUserManager;
public ProfileService(UserManager<ApplicationUser> userManager)
{
mUserManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
ApplicationUser user = await mUserManager.GetUserAsync(context.Subject);
IList<string> roles = await mUserManager.GetRolesAsync(user);
IList<Claim> roleClaims = new List<Claim>();
foreach (string role in roles)
{
roleClaims.Add(new Claim(JwtClaimTypes.Role, role));
}
context.IssuedClaims.Add(new Claim(JwtClaimTypes.Name, user.UserName));
context.IssuedClaims.AddRange(roleClaims);
}
public Task IsActiveAsync(IsActiveContext context)
{
return Task.CompletedTask;
}
}
and this is my startup file:
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.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("admin"));
});
services.AddTransient<IProfileService, ProfileService>();
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.AddRazorPages();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// 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.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.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
}
A small version of my API controller:
[Authorize(Roles = "admin")]
[Route("api/[controller]")]
public class SampleDataController : ControllerBase
{
private readonly ApplicationDbContext _db;
public SampleDataController(ApplicationDbContext db)
{
_db = db;
}
[HttpGet("[action]")]
public IEnumerable<Order> GetOrderList()
{
return _db.Order.ToList();
}
}
And my fetch method
async populateOrderList() {
const token = await authService.getAccessToken();
const response = await fetch('api/SampleData/GetOrderList', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.setState({ orderList: data });
}
As you can see in my startup file I have also tried using a policy, with the tag [Authorize(Policy = "RequireAdministratorRole")] instead. Also it works fine when i just use [Authorize]
Edit: My api controllers are in the same projekt as my identity server.
Thanks for the help in advance.
change your default claimtype for role in startup like this:
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.RoleClaimType = JwtClaimTypes.Role;
});
My situation is my frontend is built with reactjs and my backend built with asp.net core 2.2
-frontend: https://example.com/sub-directory -with valid certificate
-backend: https://198.38.x.x:5001 -windows server without a valid certificate
how can I communicate into my backend if my frontend requiring SSL I always got network error?
Startup.cs
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder
.WithOrigins("https://example.gov.ph", "http://example.gov.ph")
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
app.UseCors(MyAllowSpecificOrigins);
config.json my frontend code
{
"apiUrl": "https://198.38.x.x:5001/api",
"url": "https://198.38.x.x:5001",
"profilePictureUrl": "https://198.38.x.x:5001/Public/Employees/Photos",
"selectOptionLimit": 10,
"fileSizeLimit": 10,
"pageSize": 20,
"gridSize": { "col": 4, "row": 3 }
}
When browsers make cross domain calls using XHR, they request CORS
headers to decide whether the target server allows access to the
source domain.
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins("https://example.com/sub-directory")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddMvc();
// ...
}
public void Configure(IApplicationBuilder app)
{
// ...
app.UseCors("CorsPolicy");
// ...
}
You should be able to add that in your Startup class configuration
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("https://example.com/sub-directory");
});
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors(MyAllowSpecificOrigins);
app.UseHttpsRedirection();
app.UseMvc();
}
}