Unable to consume .NET Core web API in React JS After Single Sign on using Azure Ad - reactjs

I have successfully implemented Single Sign on with Azure Ad and fetched profile using MS Graph API but when I try to consume my dot net web API it is showing me error Unauthorized(401)
May be I am missing something in configuration,
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "example.onmicrosoft.com",
"ClientId": "392xxxx2-bxx4-4xxf-axxc-505bd9c6d8b4",
"TenantId": "06xxx2xbe-9xxe-4xx8-bxxd-e1a6ebxxxxd",
"scopes": "api://3xxxxe52-bxx4-4xxf-axx2c-505bxxxxb4/User.Read"
}
here is my Startup.cs code
using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using TMS.API.Configuration;
using TMS.DAL.Configuration;
using TMS.DAL.Mapper;
using Microsoft.Identity.Web;
namespace TMS.API
{
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.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(Configuration, "AzureAd");
services.AddControllers();
services.AddSwaggerGen(s =>
{
s.SwaggerDoc("v1", new OpenApiInfo() { Title = "TMS API", Version = "V1" });
});
services.AddAutoMapper(typeof(Startup));
services.Configure<Setting>(Configuration.GetSection("Settings"));
services.RegisterEngineServices();
services.RegisterRepositories();
}
// 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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithHeaders()
.WithExposedHeaders());
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "TMS v1");
});
}
private static void AddAutoMapper(IServiceCollection services)
{
var mapperConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new BSRMapperClass());
});
IMapper mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
}
}
}
Controller:
[Route("api/[controller]")]
[ApiController]
[RequiredScope(RequiredScopesConfigurationKey ="AzureAd:scopes")]
public class GeneralController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IGeneralService _generalService;
public GeneralController(IGeneralService generalService, IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
{
_generalService = generalService;
_configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
[Authorize(Roles ="Admin")]
[Route("[action]")]
[HttpGet]
public async Task<DataTransfer<IEnumerable<MonthResponseModel>>> GetMonthList()
{
return await _generalService.GetMonthList();
}
React config file:
export const msalConfig = {
auth: {
clientId: "ddxxxx8-xxf-4xxd-bxx2-a4xxxxxd6c",
authority: "https://login.microsoftonline.com/061f82be-9xxe-4xx8-bdad-e1xxxxxb6d", // This is a URL (e.g. https://login.microsoftonline.com/{your tenant ID})
redirectUri: "http://localhost:3001",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
}
};
export const apiConfig = {
uri: "https://example.azurewebsites.net/api", // e.g. http://localhost:5000/api
scopes: ["api://3xxxx2-bxx4-4xxf-a72c-505xxxxx8b4/User.Read"] // e.g. ["scp1", "scp2"]
};
// Add scopes here for ID token to be used at Microsoft identity platform endpoints.
export const loginRequest = {
scopes: ["User.Read"]
};
// Add the endpoints here for Microsoft Graph API services you'd like to use.
export const graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
};
Here we have:
Client(React) App Essentials
Server(.net web API) App Essentials
I have exposed my API and added scope and authorized client Application:
Exposed API
I need help as I have been stuck in this issue from a couple of days and Kindly do let me know where to add Users in Client App or in Server app?

Issue Fixed!
In appsetting I changed "scopes": "api://3xxx52-bxx4-40xf-axxc-505xxxx8b4/User.Read" to "scopes": "User.Read"and reorder the middleware in Configure Method (Startup file):
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Loreal TMS v1");
});
app.UseHttpsRedirection();
//app.UseStaticFiles();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

Related

.NET 5.0 + React + Google Auth

I am struggling to build a secure React web app with Net 5.0 backend (all in the same project) and would like some advice.
All within the same Visual Studio project.
Eg:
Project
ClientApp (React)
Controllers (C# endpoints eg /api/data, api/filtereddata)
Program.cs
Startup.cs
(Implementation #1)
I can make the front-end login using the 'react-google-login' npm package. That works well but it doesn't protect the endpoints in the controllers (/api/data).
<div>
<GoogleLogin
clientId={clientId}
buttonText="Login with Google"
onSuccess={onSuccess}
onFailure={onFailure}
cookiePolicy={'single_host_origin'}
style={{ marginTop: '100px' }}
isSignedIn={true}
/>
</div>
I have also discovered I can verify that google token on the server by using something akin to:
const onSuccess = (res) => {
const tokenBlob = { tokenId: res.tokenId };
axios.post(`/api/auth/google`, tokenBlob)
.then(res => {
})
.catch(function (error) {
console.log("axois error", error);
})
};
[Route("/api/[controller]")]
public class AuthController : Controller
{
[AllowAnonymous]
[HttpPost("google")]
public IActionResult Google([FromBody] GoogleToken t)
{
var payload = GoogleJsonWebSignature.ValidateAsync(t.tokenId, new GoogleJsonWebSignature.ValidationSettings()).Result;
}
}
But it seems awkward to do this for every API call that the UI makes.
(Implementation #2)
I have tried doing this all in the backend (Microsoft.AspNetCore.Authentication.Google), that sort of worked with AddGoogle & AddCookie, that would re-direct to a Google login page when I tried to get data from the backend (via [Authorize]) - but I could not get React to notice that it was logged in/out.
// ConfigureServices
services.AddCors(options =>
{
options.AddDefaultPolicy(builder => {
builder.WithOrigins("https://localhost","https://accounts.google.com")
.AllowAnyHeader().AllowAnyMethod();
});
});
services.AddControllers().AddNewtonsoftJson();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o =>
{
o.LoginPath = "/signin";
o.LogoutPath = "/signout"; // ??
o.ExpireTimeSpan = TimeSpan.FromHours(1);
})
.AddGoogle(options =>
{
options.ClientId = "";
options.ClientSecret = "";
options.SaveTokens = true;
options.CallbackPath = "/signin-google";
});
// Configure
app.UseCors();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
app.UseSpa(spa => {
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) {
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
// Home controller
public class HomeController : Controller {
[Route("/signin")]
public IActionResult SignIn() {
var authProperties = new AuthenticationProperties { RedirectUri = "/"
};
return new ChallengeResult(GoogleDefaults.AuthenticationScheme, authProperties);
}
[Authorize]
[Route("/signout")]
[HttpPost]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return Ok("Logged out");
}
}
// Api controller
[Authorize]
[Route("api/[controller]")]
public class DataController : Controller
{
private readonly ILogger<DataController> _logger;
public MatchListController(ILogger<DataController> logger)
{
_logger = logger;
}
[HttpGet]
public ResponseViewModel Get([AllowNull] DateTime? d) => new ResponseViewModel(matchDate ?? DateTime.UtcNow)?.Result;
}
The second implementation will re-direct to Google and require the login but React doesn't know the page is logged in. So how can it get the logged info to display the username etc?
So what is the best practice? Am I close? I feel close!
What I'd love to see would be an example of the WeatherForecast React template in visual studio with a working Google login that uses [Authorize] on the API data controller.
Any suggestions welcome.
Edit: Added some code

Call Api from blazor and pass Auth token

I have a blazor webassembly project that gets created from https://github.com/Azure-Samples/ms-identity-blazor-wasm/tree/main/WebApp-graph-user/Call-MSGraph.
Its basically the project that gets created when you use the .net core template for a Blazor application that uses authentication via AD B2B
dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" -o {APP NAME} --tenant-id "{TENANT ID}"
I was then able to call graph.api when the user logged in. I then tried to call my own Api with that authentication as described in https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-3.1.
I used
builder.Services.AddHttpClient<ITestDataService, TestDataService>(
client => client.BaseAddress = new Uri("https://localhost:44342/"))
.AddHttpMessageHandler(x =>
{
var handler = x.GetRequiredService<AuthorizationMessageHandler>()
.ConfigureHandler(new[] { "https://localhost:44342/" },
scopes: new[] { "https://graph.microsoft.com/User.Read" });
return handler;
});
I can see that a token is attached when calling the Api but authentication fails (401). The api is generated from Visual Studio templates for B2B AD and uses the configuration that is also used for the Blazor application.
This is its Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(policy =>
policy.WithOrigins("http://localhost:5000",
"https://localhost:5001")
.AllowAnyMethod()
.WithHeaders(HeaderNames.ContentType,
HeaderNames.Authorization,
"x-custom-header")
.AllowCredentials());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Do you have any idea what is missing?
The complete source is available at github https://github.com/mathiasfritsch/blazor-calls-api
If you want to call Microsoft graph and your custom API in one blazor webassembly project, we can implement it by creating different HTTP client to call different API
For example
Register a server API app
Register an AAD app for the Server API app
Expose an API
Register a client app
Register a client app
Enable Implicit grant flow
Add API permissions. (Graph API permissions and API app permissions)
Configure API app
Please add the following code in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority += "/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[] {
$"https://sts.windows.net/{Configuration["AzureAD:TenantId"]}/",
$"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/v2.0"
},
RoleClaimType = "roles",
// The web API accepts as audiences both the Client ID (options.Audience) and api://{ClientID}.
ValidAudiences = new[]
{
options.Audience,
$"api://{options.Audience}"
}
};
});
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthScopeSeparator(" ");
c.OAuthAppName("Protected Api");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Configure Client APP
Create custom AuthorizationMessageHandler for Graph API and custom API
// custom API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:44300/" },
scopes: new[] { "the API app scope" });
}
}
//Graph API
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
public GraphAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://graph.microsoft.com/" },
scopes: new[] { "https://graph.microsoft.com/User.Read" });
}
}
Add the following code to the program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped<GraphAuthorizationMessageHandler>();
// register HTTP client to call our own api
builder.Services.AddHttpClient("MyAPI", client => client.BaseAddress = new Uri("https://localhost:44300/"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
// register HTTP client to call graph api
builder.Services.AddHttpClient("GraphAPI", client => client.BaseAddress = new Uri("https://graph.microsoft.com/"))
.AddHttpMessageHandler<GraphAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the API app scope>");
options.ProviderOptions.AdditionalScopesToConsent.Add("https://graph.microsoft.com/User.Read");
});
await builder.Build().RunAsync();
}
}
Call the api
#inject IHttpClientFactory _clientFactory
var httpClient = _clientFactory.CreateClient("<the client name you register>");
await apiClient.GetStringAsync("path");

Pass JWT token from angular HttpClient to access secure .NET Core Web API

I need to call secure Web API from Angular 9 application by presenting the token. I am using Angular with .NET CORE 3.1 Web API. I have managed to generate Azure B2C token but stuck to call secure web api as I got CORS error.
Angular component calling Web API end-point
testAPI1(){
console.log("calling test API ...");
const myheaders = new HttpHeaders({
'Content-Type': 'application/json; charset=utf-8',
'Authorization': this.authService.accessToken
});
this.http.get('https://localhost:5001/txn/v1/Dashboard/GetMessage', {headers: myheaders})
.subscribe((data)=>{
console.warn(data);
})
}
Auth Service
#Injectable()
export class AuthService implements OnInit{
constructor(
private oauthService: OAuthService,
private router: Router
){// other code}
public get accessToken() {
return this.oauthService.getAccessToken();
}
Web API controller & endpoint
[Authorize]
[Route("txn/v1/[controller]/[action]")]
[EnableCors("CorsPolicy")]
[ApiController]
public class DashboardController : ControllerBase
{
[HttpGet]
public ActionResult<HelloMessage> GetMessage()
{
var result = new HelloMessage()
{
GivenName = "james",
ReturnMessage = "Dashboard# Hello, Welcome to Digital tech"
};
return result;
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//JWT Authentication
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(jwtConfig =>
{
jwtConfig.Audience = Configuration["AzureAdB2C:ResourceId"];
jwtConfig.Authority = $"{Configuration["AzureAdB2C:Instance"]}{Configuration["AzureAdB2C:TanantId"]}";
jwtConfig.RequireHttpsMetadata = false;
jwtConfig.SaveToken = true;
jwtConfig.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer =true,
ValidateAudience = true,
ValidateLifetime = true
};
});
//CORS policy
services.AddCors(options =>
options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin()));
error
Policies for CORS can be a bit finicky. So I would recommend maybe trying for a pretty open CORS policy (Which isn't too dangerous given you are using header authentication and not a cookie).
So your configure services method should look like so :
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
And then your Configure method should be something like :
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors( options => options.WithOrigins("http://example.com").AllowAnyMethod() );
app.UseMvc();
}
Note that the order inside the Configure method is important. The call to CORS must be relatively early on, if not the first middleware in your pipeline.
If that works, then work backwards to slowly add policies and see which one breaks. CORS can be really finicky so it works better to allow everything in a basic example and then add thing slowly in.
More reading here : https://dotnetcoretutorials.com/2017/01/03/enabling-cors-asp-net-core/

Azure Internal Server Error on ASP.NET Core Web API Controller

I've created a ASP.NET Core Web API Controller with React Js App and it on Azure. After sever try I am able to upload on Azure and now I'm getting error on my API. When I click on Customer it does not give me error but there is no data from SQL Database.
Can someone guide me on How to connect DB to my ASP.NET Core Web API or suggest me where am I doing wrong?
I tried post/add data to customer table but I am getting Internal server Error
Here is sql connection string in my appsetting.json
"ConnectionStrings": {
"DevConnection": "jdbc:sqlserver://aspapireact.database.windows.net:1433;database=ReactTask;user=*****;password=*****;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;"}
The Startup.cs
namespace RahulTask1
{
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.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("https://aspapireact.azurewebsites.net")
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DevConnection")));
}
// 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();
}
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.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
}
}
And this the API I am trying to call
https://aspapireact.azurewebsites.net/api/Customers
You can see my code on GitHub
https://github.com/rlbrs/ASPAPIReact
In this project you will see the local server connection string but I've updated with above one and same with appserver.json
you can use the configuration builder to the configure services method. To build the key value pair from the appsettings.json on any environment based appsettings file, add following code to the ConfigureServices method (This is not mandatory)
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false , reloadOnChange : true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
And then you read the value for the connection string as
Configuration["ConnectionStrings:DevConnection"]
PS: Any particular reason you are using jdbc connection? why not use the standard dot net based connection string?

Access HangFire endpoint from Visual Studio 2019 React template project

Following the guide at https://medium.com/#jamesdale1993/asp-net-core-2-with-signalr-and-react-redux-a-simple-example-c25ea6b19dbe I created a project with Visual Studio 2019 and the react-redux template (core 3.0 project).
I got the SignalR up and running as per article, react frontend connected to SignalR endpoint in backend code.
I then installed HangFire (https://www.hangfire.io/), set it up, and the recurring job set up for testing is working correctly. I can see (in database) the recurring job firing every minute as it should.
My problem:
I cannot access HangFire Dashboard! By default it should be http://localhost:55663/hangfire, but I see only header of template project.
I guess it is some kind of routing issue as I can only access what is served in ClientApp/build folder (react).
Any help would be greatly appreciated.
I've tried to change the default /hangfire endpoint by setting
app.UseHangfireDashboard("/jobs"); in Startup.cs Configure section, same result.
In Startup.cs code below, search for "Hangfire" for relevant sections.
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.AddSignalR();
services.AddControllersWithViews();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
// Database connection
services.AddTransient<IDbConnection>((sp) =>
new SqlConnection(Configuration.GetConnectionString("TestProjectConnection"))
);
// Hangfire
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("TestProjectConnection")));
services.AddHangfireServer();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
[System.Obsolete]
public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
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.UseCors("MyPolicy");
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
//Hangfire endpoint
app.UseHangfireDashboard("/jobs");
//backgroundJobs.Enqueue(() => Debug.WriteLine("Hello world from Hangfire!"));
RecurringJob.AddOrUpdate<ITestClass>("TestMethod", x => x.WriteMessage("Testing"), Cron.MinuteInterval(1));
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSignalR(routes =>
{
routes.MapHub<SignalRCounter>("/signalrcounter");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp/build";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
It was a matter of order of the routes.
Working with:
public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
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.UseCors("MyPolicy");
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSignalR(routes =>
{
routes.MapHub<SignalRCounter>("/signalrcounter");
});
app.UseHangfireDashboard("/hangfire");
app.UseRewriter();
app.UseAuthentication();
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp/build";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
app.UseHangfireServer();
//backgroundJobs.Enqueue(() => Debug.WriteLine("Hello world from Hangfire!"));
RecurringJob.AddOrUpdate<ITestClass>("TestMethod", x => x.WriteMessage("Testing"), Cron.MinuteInterval(1));
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
}

Resources