Blazor WASM - Access to fetch at "https://login.microsoftonline.com/...." (redirected from ...) from origin ... has been blocked by "CORS" policy: - azure-active-directory

I am building a WASM hosted app with multiple clients and started to get the following error when I called a WebApi controller from the Client project.
Access to fetch at 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=0e5f2876-c.......-client-SKU=ID_NET6_0&x-client-ver=6.23.1.0'
(redirected from 'https://localhost:5001/WeatherForecast') from origin 'https://localhost:5001'
has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I ended up working out what the problem was and thought I would document it here as I wasn't able to find a writeup of a similar situation.
My WebApi project was configured as follows. (ie. with AddMicrosoftIdentityWebApi and AddMicrosoftIdentityWebApp in the same project.)
Program.cs (Server Project)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
The project worked fine with just AddMicrosoftIdentityWebApi, but started generating the error when I added AddMicrosoftIdentityWebApp.

It turns out that the reason for the error is that the controller that I'm targeting did not know which Authentication Scheme to target. The CORS message was a red-herring (even though it is technically correct in hindsight).
I had to be explicit in my [Authorize] atrribute as per the following.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}

Related

How do I use the old XmlSerializer with Web API, instead of the DataContractXmlSerializer?

I have a Web API site that has, up until now, supported only JSON.
I'm trying to get it to work with XML, and I've run into issues.
There are a couple of things about the DataContract XmlSerializer that bug me. First, it imposes an order on the XML elements, and managing the namespaces is a mess.
It was suggested that I configure my site to use the older XmlSerializer. The code to enable this is supposed to be simple, just set the UseXmlSerializer flag on the XmlFormatter:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
...
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
...
}
}
And it doesn't work.
If I do not set UseXmlSerializer = true:
If I submit a request with "Accept: application/json", I get JSON
If I submit a request with "Accept: application/xml", I get XML
If I do set UseXmlSerializer = true:
If I submit a request with "Accept: application/json", I get JSON
If I submit a request with "Accept: application/xml", I get JSON
Which makes no sense at all.
Something that may be related - we're using Swagger to create API documentation pages:
If I do not set UseXmlSerializer = true:
The Swagger IO Response Content Type dropdown includes "application/xml"
If I do set UseXmlSerializer = true:
The Swagger IO Response Content Type dropdown does not include "application/xml"
Seeing that, I tried explicitly adding support for xml in WebApiConfig.Register:
public static void Register(HttpConfiguration config)
{
config.Formatters.XmlFormatter.UseXmlSerializer = true;
config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
...
}
This makes no difference. The Swagger UI still does not include the xml media types, and requests that ask for XML still return JSON.

404 Not Found error in preflight OPTIONS when executing a PUT method in CakePHP API with axios in React App

So, we have an API with CakePHP 3.7. We are using resources to generate CRUD methods. The API is hosted in a server with apache2 and is accessed through a manager app using React (this app is a microservice). The manager makes the calls through axios and is correctly managing the GET, POST and HEAD requests (simple CORS requests) but we're having problems when it comes to more complex requests such as PUT or DELETE.
When executing PUT or DELETE requests it makes a preflight OPTIONS request and it returns a 404 Not Found error. And some messages in console related with CORS which are:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSPreflightDidNotSucceed?utm_source=devtools&utm_medium=firefox-cors-errors&utm_campaign=default
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSDidNotSucceed?utm_source=devtools&utm_medium=firefox-cors-errors&utm_campaign=default
We already tried several fixes, such as using CakePHP CORS plugin, adding CORS Headers in the response in the beforeRender and beforeFilter methods of AppController and also adding CORS headers in the apache, none of this seams to be working.
private function setCorsHeaders() {
$this->response->cors($this->request)
->allowOrigin(['*'])
->allowMethods(['*'])
->exposeHeaders(['X-Total-Pages'])
->maxAge(800)
->build();
}
public function beforeRender(Event $event)
{
$this->setCorsHeaders();
}
public function beforeFilter(Event $event)
{
if($this->request->is('options')) {
$this->setCorsHeaders();
return $this->response;
}
}
Header set Access-Control-Expose-Headers "X-Total-Pages"
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS"
The expected behaviour is that the PUT and DELETE methods are executed properly (the preflight OPTIONS should pass successfully). Any help is apreciated.
In CakePHP >=3.4, Http\Response objects are treated as immutable by many methods. The method chain called on $this->request->cors() uses the CorsBuilder class to queue the desired headers on an immutable response which is returned when calling build().
Try assigning the return from CorsBuilder::build() with the queued headers to $this->response.
private function setCorsHeaders() {
$this->response = $this->response->cors($this->request)
->allowOrigin(['*'])
->allowMethods(['*'])
->exposeHeaders(['X-Total-Pages'])
->maxAge(800)
->build();
}

Laravel 5.4 / Angular possible mishandled rejection due to cross origin request blocked

I am currently building an application using token based authentication with Angular and Laravel. I initially set things up just to test the API by creating a BookController . At first I was getting a Cross Origin Request Block error when I tried to call this data from Angular. However I managed to resolve this by adding the headers to my routes/web.php file. Here is the whole file. NB: After adding these headers I was succesfully able to use the API even from another domain
<?php
header('Access-Control-Allow-Origin: *');
header( 'Access-Control-Allow-Headers: Authorization, Content-Type' );
//Route::get('/', 'BookController#show');
//Route::resource('book/create', 'BookController#create');
Auth::routes();
Route::get('/', 'HomeController#index');
Route::resource('book', 'BookController');
Route::resource('authenticate', 'AuthenticateController', ['only' => ['index']]);
Route::post('authenticate', 'AuthenticateController#authenticate');
However I am currently following this tutorial to set up token based authentication. https://scotch.io/tutorials/token-based-authentication-for-angularjs-and-laravel-apps
To summarise , my issue is when I submit the form containing username and password I am getting the following errors. Below I will try elaborate a bit more but it is quite difficult as there is alot to it.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://www.example.local/authenticate/.
(Reason: CORS header 'Access-Control-Allow-Origin' missing).
And
Possibly unhandled rejection:
{"data":null,"status":-1,"config":{"method":"POST","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"http://www.example.local/authenticate/","data":{"email":"dasdas#Dasa.com","password":"fsdfd"},"withCredentials":false,"headers":{"Accept":"application/json,
text/plain,
/","Content-Type":"application/json;charset=utf-8"}},"statusText":""}
I am using Angular UI Router V 0.4.2 and satellizer. My Angular version is 1.6.2 It using a different domain than the API. Much like the working example above.
On the laravel side I also followed this tutorial to add middleware to attempt to resolve this but no luck.
http://en.vedovelli.com.br/2015/web-development/Laravel-5-1-enable-CORS/
I will also include my AuthenticateController.php file..
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use App\User;
class AuthenticateController extends Controller
{
public function __construct()
{
// Apply the jwt.auth middleware to all methods in this controller
// except for the authenticate method. We don't want to prevent
// the user from retrieving their token if they don't already have it
$this->middleware('jwt.auth', ['except' => ['authenticate']]);
$this->middleware('cors');
}
public function index()
{
// Retrieve all the users in the database and return them
$users = User::all();
return $users;
}
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
try {
// verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong
return response()->json(['error' => 'could_not_create_token'], 500);
}
// if no errors are encountered we can return a JWT
return response()->json(compact('token'));
}
}
My issue is I do not even know if the "possibly unhandled rejection" is related to the "Cross-Origin Request Blocked" error. But I have to assume it is.
Can you recognise anything from my routes files that may be allowing one and not another?
EDIT:
I have noticed the difference between one request and another is that one is a GET request while another is an OPTIONS request. This may be the cause.
I have since added Header set Access-Control-Allow-Origin "*" to both the virtual hosts config file in Apache and to a .htaccess file in the root of the Laravel project. Still no change.
I am wondering is this related something in Angular
Your server code needs to handle that OPTIONS request by sending a headers-only response to it that includes the Access-Control-Allow-Methods: GET, POST, PUT, DELETE header and Access-Control-Allow-Headers: Authorization, Content-Type header.
Or you can just try using https://github.com/barryvdh/laravel-cors which makes all this easier.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests has general info you might want to read up on.

Restlet + Google App Engine + CORS

I am using Restlet on Google App Engine for developing my sample application.
The front end is Angular 2 App.
The Rest API is working fine with browser.
However, I am getting the following issue when I am trying to hit the URL from Angular app.
XMLHttpRequest cannot load https://1-dot-jda-saas-training-02.appspot.com/rest/projectsBillingInfo/123. The 'Access-Control-Allow-Origin' header contains multiple values 'http://evil.com/, *', but only one is allowed. Origin 'http://localhost:3000' is therefore not allowed access.
So, I thought I will go ahead and add the CORS headers in the response. I used CorsFilter for that as follows but the issue is still there. When I see the header of the Response, I do not see any CORS headers. What am I missing here?
#Override
public Restlet createInboundRoot() {
// Create a router Restlet that routes each call to a
// new instance of HelloWorldResource.
Router router = new Router(getContext());
CorsFilter corsFilter = new CorsFilter(getContext(), router);
corsFilter.setAllowedOrigins(new HashSet<String>(Arrays.asList("*")));
corsFilter.setAllowedCredentials(true);
// Defines only one route
router.attachDefault(AddressServerResource.class);
router.attach("/contact/123",ContactServerResource.class);
router.attach("/projectsBillingInfo/123",ProjectBillingResource.class);
return corsFilter;
}
EDIT
I could get this working. May be I was doing some mistake.
But, I am not able to make this work with the GaeAuthenticator. When I am putting the GaeAuthenticator along with Corsfilter, it skips the authentication part of it. So, either the authentication works or the corsfilter works but not both. Is there any easy way to set/modify HTTP headers in restlet.
Here is the code I am using ..
#Override
public Restlet createInboundRoot() {
// Create a router Restlet that routes each call to a
// new instance of HelloWorldResource.
Router router = new Router(getContext());
// Defines only one route
router.attachDefault(AddressServerResource.class);
router.attach("/contact/123",ContactServerResource.class);
router.attach("/projectsBillingInfo/123",ProjectBillingResource.class);
GaeAuthenticator guard = new GaeAuthenticator(getContext());
guard.setNext(router);
CorsFilter corsFilter = new CorsFilter(getContext(), router);
corsFilter.setAllowedOrigins(new HashSet<String>(Arrays.asList("*")));
corsFilter.setAllowedCredentials(true);
return corsFilter;
First, I think you can use the service instead of the filter:
public MyApplication() {
CorsService corsService = new CorsService();
corsService.setAllowedCredentials(true);
corsService.setSkippingResourceForCorsOptions(true);
getServices().add(corsService);
}
Do you mind to set the "skippingServerResourceForOptions"?
#Override
public Restlet createInboundRoot() {
// Create a router Restlet that routes each call to a
// new instance of HelloWorldResource.
Router router = new Router(getContext());
// Defines only one route
router.attachDefault(AddressServerResource.class);
router.attach("/contact/123",ContactServerResource.class);
router.attach("/projectsBillingInfo/123",ProjectBillingResource.class);
return router;
}
Best regards, Thierry Boileau

WebAPI CORS not allowing Post requests

I have exhausted every resource I can find regarding Cors, and I still get the following error message when sending a request from Angular's $http service (via Chrome):
POST http://localhost:61459/api/LoginAuth/PostLoginByHandle 500 (Internal Server Error)
Get requests work just fine. I have found a hundred variations of similar instructions that seem to work for other people, but I just can't crack this one. I'll post my code.
WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
//jsonFormatter.SerializerSettings = new CamelCasePropertyNamesContractResolver();
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
}
}
From my understanding, this should be enough to allow any Cors request globally. In addition, I mark the controller with:
[EnableCors(origins: "*", headers: "*", methods: "*")]
I've tried modifying my web.config with various things I've found online, but I've read that it's not necessary when doing it programatically. Any idea what I'm doing wrong?
I've tagged this post as angularjs in case I'm doing something wrong there, as I'm very new to it. Here is my call:
$http.post("http://localhost:61459/api/LoginAuth/PostLoginByHandle",this.LoginRequest).success(function(data){
testCtrl.user = data;
console.log("Retrieved: " + data);
});
**Edit: I am able to hit the controller with Postman when I remove the top method. Any idea why these would conflict? Postman gives this error:
"Message": "An error has occurred.",
"ExceptionMessage": "Multiple actions were found that match the request: \r\nLoginByKey on type RecruitingTool.Web.Controllers.LoginAuthController\r\nPostLoginByHandle on type RecruitingTool.Web.Controllers.LoginAuthController"
Here is the controller code. I don't get why these would conflict:
[HttpPost]
public LoginResult LoginByKey(LoginRequest req)
{
LoginResult l = new LoginResult();
if (!string.IsNullOrEmpty(req.Key) &&
HttpContext.Current.Cache["credentials." + req.Username.ToUpper()].ToString() == req.Key)
{
l.Success = true;
}
else
{
l.Success = false;
l.ErrorMessage = "The credentials key is not valid.";
}
return l;
}
[HttpPost]
[EnableCors(origins: "*", headers: "*", methods: "POST")]
public LoginResult PostLoginByHandle(LoginRequest req)
{
LoginResult l = new LoginResult();
if (req.Username.ToUpper() == "TESTUSER" && req.Password == "test")
{
//change to authenticate against DB
l.Success = true;
l.CredentialsKey = Guid.NewGuid().ToString();
l.ErrorMessage = "";
HttpContext.Current.Cache["credentials." + req.Username.ToUpper()] = Guid.NewGuid().ToString();
}
else
{
l.Success = false;
l.ErrorMessage = "The username or password is not correct. Please check your information and try again.";
}
return l;
}
**Edit 2: The problem was a conflict between the two method's default routes. I'm not sure why that would be the case, but as soon as I specify an explicit route for both of them, it is resolved. I'm still interested to know the answer if anyone knows. Thanks all!
If you put a breakpoint in your Web API POST controller action is it not hitting it? HTTP 500 normally indicates some issue with your code (unhandled exception).
If it's not hitting that controller action it must be earlier in the pipeline. Have you tried just posting directly to your API method from something like POSTman? Very useful extension for Chrome..
1- Your method parameters are missing the [FromBody] attribute, so it should be like
public LoginResult PostLoginByHandle([FromBody]LoginRequest req)
2- Also, both methods have the exact same signature, in terms of the number of parameters and the data type of req parameters.
hope that helps.
WebAPI supports Convention based routing:
To find the action, Web API looks at the HTTP method, and then looks for an action whose name begins with that HTTP method name. For example, with a GET request, Web API looks for an action that starts with "Get...", such as "GetContact" or "GetAllContacts". This convention applies only to GET, POST, PUT, and DELETE methods. You can enable other HTTP methods by using attributes on your controller. We’ll see an example of that later.
Are you sure you don't have two methods in there, one called PostXXX and one called XXX? Or perhaps the one named POSTxxx is triggering convention-based routing. I've seen mention of "the conventions can result in conflicts in the routing table, matching incorrect actions."
Try renaming your methods to something other than Postxxx, Getxxx, ...
[PS Attribute-routing is much better]
I encountered same problem today. As it turns out the problem was in pre-flight OPTIONS request. I used this solution: Handling CORS Preflight requests to ASP.NET MVC actions
Additionally I added headers into response and removed Microsoft.AspNet.WebApi.Cors from project:
protected void Application_BeginRequest()
{
//CORS
if (Request.Headers.AllKeys.Contains("Origin"))
{
Response.Headers.Add("Access-Control-Allow-Origin", string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["CORS_ORIGIN"]) ? "*" : ConfigurationManager.AppSettings["CORS_ORIGIN"]);
Response.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, DELETE");
Response.Headers.Add("Access-Control-Allow-Headers", "Access-Control-Allow-Methods, Access-Control-Allow-Origin, Content-Type, Accept, X-Requested-With, Session");
//handle CORS pre-flight requests
if (Request.HttpMethod == "OPTIONS")
Response.Flush();
}
}

Resources