SCENARIO
I am trying to provide authentication for my web application APIs with AWS-Cognito and JWTBearer.
PROBLEM
Currently, my client performs a GET call and is met with a 401 (Unauthorized) message. This prompts the client to redirect to Cognito UI for log-in and the token. Once performed, we're turned back to the web application.
When the token has been provided, I attach it to a header and perform the request again. This doesn't get validated by the server, and we end up in a loop.
It feels as though JWTBearer is not doing anything.
SERVER .NET 5.0
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://{domain}.{location}.amazoncognito.com/{PoolId}";
options.Audience = "http://localhost:5000";
});
...
}
public void Configure(iApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseAuthentication();
app.UseAuthorization();
...
}
Controller
[HttpGet]
[Authorize]
public async Task<IActionResult> GetItems(ICollection<Data> collection){...}
CLIENT React
//Token = "Bearer AccessToken"
fetch(config.getResourceUrl, {headers: {Authentication: Token}})
.then(res => {
if (res.status > 400) {
window.location.replace("{Cognito UI}");
} else {
return res.json();
})
Solved the problem:
fetch(config.getResourceUrl, {headers: {Authentication: Token}})
The header should have read Authorization instead.
fetch(config.getResourceUrl, {headers: {Authorization: Token}})
Related
I am doing this project, but I am having the issue of having a CORS error
Access to XMLHttpRequest at 'https://localhost:44309/api/auth/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I am doing everything by the book and enabling CORS inside the startup.cs file
services.AddAutoMapper(typeof(GroupFourLibraryContext));
services.AddScoped<IUserService, UserService>();
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.SetIsOriginAllowed(isOriginAllowed: _ => true).AllowAnyHeader().AllowAnyMethod().AllowCredentials();
});
});
}
// 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", "GroupFourLibrary v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(MyAllowSpecificOrigins);
app.UseForwardedHeaders();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Axios code.
Code returns a response and a token to login.
const response = await axios.post(authApiAddress + '/login',loginObject).then((res)=>{
}
);
if(response.status ===200){
localStorage.setItem(storageLocationConstant, response.data.message);
}
return response;
Controller code.
Controller handles the login. Calls a a ASP.Identity method to authenticate
[HttpPost]
[Route("login")]
[EnableCors("AllowOrigin")]
public async Task<IActionResult> login(LoginUserViewModel loginViewMdl)
{
if (!ModelState.IsValid)
{
return BadRequest("Login Model is not Valid or missing parameters");
}
var result = await this.usrService.LoginUser(loginViewMdl);
if (result.IsSuccess == false)
{
return BadRequest(result.Message);
}
return Ok(result);
}
I have a web project: .Net core API as a backend and React app as a frontend and everything is working fine with every browser except Mozilla private tab. When I run my react app in Mozilla private window this error occurs:
Cross-Origin Request Blocked: ... Reason: CORS request did not succeed
but if my cors policy is not correct how can other browsers connect my backend without any problem? (p.s Mozilla normal tab works fine as well)
Question: what is the problem and how can I fix it?
Here's a lot of same question :)
I'm facing same problem now.
After hundreds attempts i have semi-solition:
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
//some other code here...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//some code...
app.UseRouting();
app.UseCors(x => x
.SetIsOriginAllowed(origin => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.Build());
app.UseAuthorization();
//other code
}
In react application:
export const loginUser = createAsyncThunk(
'user/loginUser',
async function(loginCredentials, {rejectWithValue}) {
try {
const response = await fetch(`https://localhost:5001/usr/loginuser`, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'Accept': 'text/plain',
},
body: JSON.stringify({loginCredentials})
});
if (!response.ok) {
throw new Error('Server Error!');
}
//other code
With this code i have situation when most of request are failed (seems like 'fetch canceled' in browser) but about 30% request are successfully. That's why i called it "semi"-solution.
Have no idea what's happening, because failed and normal requests are identical (headers and body).
I am trying to send data from React App to .NET Core Web App using SignalR.
The .NET Core Web App has Windows Authentication enabled. With Windows Authentication enabled, I am getting the CORS error when my React App tries to send message to .NET Core App via SignalR.
It works fine if I disable windows authentication and enable anonymous authentication.
Can you please help me with your valuable inputs to make the connection work?
React JS app code looks like below:
const hubConnection = new HubConnectionBuilder()
.withUrl(window.config.APP_URL, options)
.withAutomaticReconnect()
.build();
this.setState({ hubConnection }, () => {
this.state.hubConnection
.start()
.then(() => console.log("SignalR Connection started!"))
.catch((err) =>
console.log("SignalR Error while establishing connection :(", err)
);
});
}
sendMessage = () => {
console.log("sendMessage() Properties: ", this.props);
const signalRMessage = {
UserName: this.props.userName,
};
this.state.hubConnection
.invoke("SendMessage", signalRMessage)
.catch((err) => console.error(err));
};
I tried to explicitly add the ‘Access-Control-Allow-Origin’ header as shown below. But still I see the CORS error.
componentDidMount() {
let options = {
httpClient: {
post: (url, httpOptions) => {
// httpOptions.headers = {
// ...httpOptions.headers,
// "Access-Control-Allow-Origin": window.config.CORS_ALLOW_ORIGIN_URL,
// //"Access-Control-Allow-Methods": "POST, GET, HEAD",
// };
// httpOptions.method = "POST";
// httpOptions.url = url;
// return httpOptions;
const headers = {
...httpOptions.headers,
"Access-Control-Allow-Origin": window.config.CORS_ALLOW_ORIGIN_URL,
"Access-Control-Allow-Methods": "POST, GET, HEAD",
};
let newResponse = {};
return axios.post(url, {}, { headers }).then(response => {
return (newResponse = {
statusCode: response.status,
statusText: response.statusText,
content: JSON.stringify(response.data)
});
});
}
}
};
Below is the CORS error that I see in console logs at React App side:
enter image description here
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddRazorPages();
services.AddCors(options =>
{
options.AddPolicy("ClientPermissionPolicy", policy =>
{
policy.WithOrigins("https://xxxx.com")
.AllowAnyHeader()
.AllowAnyMethod().AllowCredentials();
});
});
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("ClientPermissionPolicy");
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<TapHub>("/hubs/tap");
});
}
Went through many posts here in stackoverflow and tried below but in vain.
In Configure():
app.UseCors(builder => builder.WithOrigins("https://xxxx.com").AllowAnyMethod().AllowAnyHeader().AllowCredentials());
In ConfigureServices():
services.AddCors(options =>
{
options.AddPolicy("ClientPermissionPolicy", policy =>
{
policy.WithOrigins("https://xxxxx.com")
.AllowAnyHeader()
.AllowAnyMethod().AllowCredentials()
.SetIsOriginAllowed((host) => true);
});
});
Tried AllowAnyOrigin()
Tried removing AllowCredentials()
Like I said above, it works fine if I disable windows authentication and enable anonymous authentication. The React App successfully connects to the hub endpoint in case of anonymous authentication. The CORS error comes into picture only when I enable windows authentication. I need Windows Authentication enabled for my requirement. Requesting you to help fix the issue.
Thanks!
A bit late, however I faced similar problem right now. Even with enabled CORS, it still rejected me. In the end, I found out that problem is in the
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
When I included CORS into this part, it started to work ... finally
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireCors("ClientPermissionPolicy");
});
You have to place UseCors between UseRouting and UseAutorization
app.UseRouting();
app.UseCors("ClientPermissionPolicy");
app.UseAuthorization();
and maybe you can try to move AddCors to the top of ConfigureServices method
Just the test pourposes I would use this
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("ClientPermissionPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
.....
}
only if it works, I would try to use specific origins.
For those using .NET Core 3.1, here is a COMPLETE solution (front-end to back-end):
My problem: When I enabled the windows authentication on my web API, I could not do fetch calls from my react app to my .NET Core 3.1 web API, CORS was freaking out. With Anonymous authentication it worked, but not when windows authentication is enabled.
1.launchSettings.json
this will be used only for your dev environnment, make sure windows auth is also enabled in IIS on your prod server.
{
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": false,
"iisExpress": {
"applicationUrl": "http://localhost:58747",
"sslPort": 0
}
},
{... more settings if any}
}
2.Startup.cs:
CORS policy is enabled here. The order of methods is important here. Also, you don't need to set those in a web.config
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", //give it the name you want
builder =>
{
builder.WithOrigins( "http://localhost:3000", //dev site
"production web site"
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
//database services here
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
// global policy same name as in the ConfigureServices()
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
3.Controller(s):
using Microsoft.AspNetCore.Cors;
... your other usings
namespace ProjectTest.Controllers
{
[ApiController]
[EnableCors("CorsPolicy")] //THIS HERE needs to be the same name as set in your startup.cs
[Route("[controller]")]
public class FooController:Controller
{
[HttpGet("getTest")]
public JsonResult GetTest()
{
return Json("bar");
}
}
}
4.React Component fetch call example:
The "credential: 'include'" is the secret
await fetch('http://localhost:3000/Foo/getTest', {
method: 'GET',
credentials: 'include'
}).then(resp => resp.json());
As mentioned here, the problem takes place because:
IIS's Windows Auth happens before ASP.NET Core runs, the ASP.NET Core
CORS implementation isn't able to process the OPTIONS request (because
the browser won't send your Windows credentials with it). The only
real solution here is to use the IIS CORS module since it runs before
Windows Authentication and can process the unauthenticated CORS
request.
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/
I'm trying to get my ReactJS app (on an AWS S3 machine) PUT request working with my Server API (on an AWS Windows EC2 machine). Seems I am being tripped up by the preflight message that is being sent out. I've been searching on how to handle this and came across these two stackoverflow posts:
Enable OPTIONS header for CORS on .NET Core Web API
How to handle OPTION header in dot net core web api
I've ensured IIS accepts the OPTIONS verb and have added the middleware described. I can see the OPTIONS preflight handling being called through the logging but for some reason I am still getting the CORS error. Listed the main sections of the code below, any help would be really appreciated.
ReactJS PUT request
var myHeaders = new Headers();
myHeaders.append('Accept', 'application/json');
myHeaders.append('Content-Type', 'application/json-patch+json');
var rawObject = {
Name: this.state.recipeEdit.name,
Type: this.state.recipeTypeEdit,
Description: this.state.recipeEdit.description,
Ingredients: this.state.recipeIngredients,
Steps: this.state.recipeSteps,
};
var requestOptions = {
method: 'PUT',
headers: myHeaders,
body: JSON.stringify(rawObject),
redirect: 'follow',
};
fetch(this.state.url, requestOptions)
.then((response) => response.json())
.then((data) => {
this.setState({ recipeDetail: data });
});
Middleware Class
public class OptionsMiddleware
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly RequestDelegate _next;
public OptionsMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
return BeginInvoke(context);
}
private Task BeginInvoke(HttpContext context)
{
if (context.Request.Method == "OPTIONS")
{
log.Error("Handling the OPTIONS preflight message");
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { (string)context.Request.Headers["Origin"] });
context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept" });
context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, POST, PUT, DELETE, OPTIONS" });
context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
context.Response.StatusCode = 200;
return context.Response.WriteAsync("OK");
}
log.Error("Invoking message");
return _next.Invoke(context);
}
}
public static class OptionsMiddlewareExtentions
{
public static IApplicationBuilder UseOptions(this IApplicationBuilder builder)
{
return builder.UseMiddleware<OptionsMiddleware>();
}
}
CORS Configuration in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
log.Error("Entered ConfigureServices");
try
{
#if DEBUG
services.AddCors();
#else
services.AddCors(o => o.AddPolicy("MyCorsPolicy", builder =>
{
builder.SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));
#endif
services.AddControllersWithViews().AddNewtonsoftJson();
services.AddControllersWithViews(options =>
{
options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
});
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddMvc(options => options.Filters.Add(typeof(homebakeExceptionFilter)));
#if USE_SQLITE
log.Error("Using SQLITE");
services.AddDbContext<SqliteDbContext>(options =>
{
options.UseSqlite("Data Source=./homebake.db");
});
#else
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("homebakeapp-api-in-memory");
});
#endif
log.Error("Adding services");
services.AddScoped<IIngredientRepository, IngredientRepository>();
services.AddScoped<IRecipeStepRepository, RecipeStepRepository>();
services.AddScoped<IRecipeRepository, RecipeRepository>();
services.AddScoped<IIngredientService, IngredientService>();
services.AddScoped<IRecipeStepService, RecipeStepService>();
services.AddScoped<IRecipeService, RecipeService>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
log.Error("Adding auto mapper");
services.AddAutoMapper(typeof(Startup));
}
catch (System.Exception ex)
{
log.Error(ex.Message);
if (ex.InnerException != null )
log.Error(ex.InnerException);
}
}
private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
var builder = new ServiceCollection()
.AddLogging()
.AddMvc()
.AddNewtonsoftJson()
.Services.BuildServiceProvider();
return builder
.GetRequiredService<IOptions<MvcOptions>>()
.Value
.InputFormatters
.OfType<NewtonsoftJsonPatchInputFormatter>()
.First();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddLog4Net();
log.Error("Entered Configure");
app.UseOptions();
#if DEBUG
app.UseCors(options => options.WithOrigins("http://localhost:3000").AllowAnyMethod().AllowAnyHeader());
#else
log.Error("Using cors policy");
app.UseCors("MyCorsPolicy");
#endif
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
//app.use
app.UseHttpsRedirection();
log.Error("Using MVC");
app.UseMvc();
}
I have seen this error when the server-side CORS settings are set using both the web.config and in the code, like in your middleware, which at runtime results in duplicates and cause this type of behavior. Also, you may want to add the following to your web.config and see if it helps. This will ensure your CORS settings are only set by the code.
<httpProtocol>
<customHeaders>
<remove name="Access-Control-Allow-Headers" />
<remove name="Access-Control-Allow-Methods" />
<remove name="Access-Control-Allow-Origin" />
</customHeaders>
</httpProtocol>
In the end the issue was to do with IIS configuration. After more searching I found the solution here:
How do I enable HTTP PUT and DELETE for ASP.NET MVC in IIS?
Essentially I had to update the ExtensionlessUrlHandler-Integrated-4.0 setting to accept the PUT and DELETE verbs (access it from the Handler Mappings option in IIS) and also disable both the WebDav module and handler. After that the requests went through and were processed correctly. I'm also still running the Middleware code as detailed above in case anyone else ever comes across this issue.
What made me look at the IIS configuration was that I got the multiple entries for Access-Control-Allow-Origin if I added it to my web.config file, if so, how could it be missing when not included there. Big thanks to #Masoud Safi for al the help he gave on this too.