-2

PLEASE READ THROUGH EDIT 4+ AS THAT IS THE LATEST ON THIS ISSUE

So I've got an API in C# which has auth set up in the web.config. It redirects to another project which handles the authentication. When a request is made it checks if the user is logged in within Login.aspx. I've set up the CORS policy to allow requests by changing the WebApiConfig.cs file to:

        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            var cors = new EnableCorsAttribute("http://localhost:1337", "*", "*");
            config.EnableCors(cors);

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

I've also allowed all pre-flight requests to be ignored by putting this into the Global.asax.cs. I was getting an error before which is why I had added this:

        protected void Application_BeginRequest()
        {
            if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) &&
                Request.HttpMethod == "OPTIONS")
            {
                Response.Flush();
            }
        }

Now when I make a request from my React app to the API I get the following error:

Access to fetch at 'http://localhost:9074/Login.aspx' (redirected from 'http://localhost:9074/Customer/GetAll/') from origin 'http://localhost:1337' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

I don't understand why the pre-flight request isn't being flushed. I want it to go to the login.aspx page on the API realise it's not logged in and see that the origin header isn't the same as the API uri so it'll send back a json response which will include the url for the user to use to authenticate. Then on the react app I can just redirect the user to that url.

If someone was accessing from the API directly (ie they were to visit http://localhost:9074/Customer/GetAll/) then it would just redirect the user instead to the login page to be authenticated. That way react shouldn't complain about a fetch request being redirected to an external page since it's actually getting a value returned (the url).

I can see that it hits the global.asax.cs twice once with a http Method Options which gets flushed and then the second with a Get http Method. When I click on Step Over it doesn't run anything else (I've got a breakpoint on login.aspx) and just gives the error above.

EDIT 1

Changing the Content-Type as suggested here also doesn't help. Regardless of the Content-Type it still sends a pre-flight request. Whenever I get information directly from the API ie I visit (localhost:9072/Customer/GetAll) I don't hit the breakpoint in global.asax.cs

EDIT 2

I've also come across this (Check the second section). Although changing the Content-Type from application/json to something else didn't work I'm assuming it still sends the pre-flight request since it's got Authorization. Since the login.aspx page doesn't expect a GET request is that why it returns a http error 405 Method Not Allowed? My login.aspx page is what checks whether the user is authenticated and redirects them to the login page (a separate external project). Is there a way to ignore the pre-flight request while still keeping authentication enabled as in the solution linked they advised removing it so that you wouldn't get the error.

NETWORK TAB

network tab

EDIT 3

When I had opened the terminal and ran curl -X OPTIONS http://localhost:9074/login.aspx -i I had gotten:

HTTP/1.1 405 Method Not Allowed
Cache-Control: private
Allow: GET, HEAD, OPTIONS, TRACE
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcbW5hemlyXERlc2t0b3BcQWZ0b25cUnVsZXMgRW5naW5lXFJ1bGVFbmdpbmVBUEkgLSBDb3B5XFJ1bGVFbmdpbmVBUElcbG9naW4uYXNweA==?=
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: GET,POST,OPTIONS,DELETE,PUT
Date: Tue, 25 Oct 2022 13:44:56 GMT
Content-Length: 5288

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IIS 10.0 Detailed Error - 405.0 - Method Not Allowed</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;}
code{margin:0;color:#006600;font-size:1.1em;font-weight:bold;}
.config_source code{font-size:.8em;color:#000000;}
pre{margin:0;font-size:1.4em;word-wrap:break-word;}
ul,ol{margin:10px 0 10px 5px;}
ul.first,ol.first{margin-top:5px;}
fieldset{padding:0 15px 10px 15px;word-break:break-all;}
.summary-container fieldset{padding-bottom:5px;margin-top:4px;}
legend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;}
legend{color:#333333;;margin:4px 0 8px -12px;_margin-top:0px;
font-weight:bold;font-size:1em;}
a:link,a:visited{color:#007EFF;font-weight:bold;}
a:hover{text-decoration:none;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.4em;margin:10px 0 0 0;color:#CC0000;}
h4{font-size:1.2em;margin:10px 0 5px 0;
}#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS",Verdana,sans-serif;
 color:#FFF;background-color:#5C87B2;
}#content{margin:0 0 0 2%;position:relative;}
.summary-container,.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
.content-container p{margin:0 0 10px 0;
}#details-left{width:35%;float:left;margin-right:2%;
}#details-right{width:63%;float:left;overflow:hidden;
}#server_version{width:96%;_height:1px;min-height:1px;margin:0 0 5px 0;padding:11px 2% 8px 2%;color:#FFFFFF;
 background-color:#5A7FA5;border-bottom:1px solid #C1CFDD;border-top:1px solid #4A6C8E;font-weight:normal;
 font-size:1em;color:#FFF;text-align:right;
}#server_version p{margin:5px 0;}
table{margin:4px 0 4px 0;width:100%;border:none;}
td,th{vertical-align:top;padding:3px 0;text-align:left;font-weight:normal;border:none;}
th{width:30%;text-align:right;padding-right:2%;font-weight:bold;}
thead th{background-color:#ebebeb;width:25%;
}#details-right th{width:20%;}
table tr.alt td,table tr.alt th{}
.highlight-code{color:#CC0000;font-weight:bold;font-style:italic;}
.clear{clear:both;}
.preferred{padding:0 5px 2px 5px;font-weight:normal;background:#006633;color:#FFF;font-size:.8em;}
-->
</style>

</head>
<body>
<div id="content">
<div class="content-container">
  <h3>HTTP Error 405.0 - Method Not Allowed</h3>
  <h4>The page you are looking for cannot be displayed because an invalid method (HTTP verb) is being used.</h4>
</div>
<div class="content-container">
 <fieldset><h4>Most likely causes:</h4>
  <ul>  <li>The request sent to the Web server used an HTTP verb that is not allowed by the module configured to handle the request.</li>       <li>A request was sent to the server that contained an invalid HTTP verb.</li>  <li>The request is for static content and contains an HTTP verb other than GET or HEAD.</li>    <li>A request was sent to a virtual directory using the HTTP verb POST and the default document is a static file that does not support HTTP verbs other than GET or HEAD.</li> </ul>
 </fieldset>
</div>
<div class="content-container">
 <fieldset><h4>Things you can try:</h4>
  <ul>  <li>Verify the list of verbs enabled for the module handler this request was sent to, and ensure that this verb should be allowed for the Web site.</li>        <li>Check the IIS log file to see which verb is not allowed for the request.</li>       <li>Check the failed request tracing logs for additional information about this error. For more information, click <a href="http://go.microsoft.com/fwlink/?LinkID=66439">here</a>. </li> </ul>
 </fieldset>
</div>

<div class="content-container">
 <fieldset><h4>Detailed Error Information:</h4>
  <div id="details-left">
   <table border="0" cellpadding="0" cellspacing="0">
    <tr class="alt"><th>Module</th><td>&nbsp;&nbsp;&nbsp;StaticFileModule</td></tr>
    <tr><th>Notification</th><td>&nbsp;&nbsp;&nbsp;ExecuteRequestHandler</td></tr>
    <tr class="alt"><th>Handler</th><td>&nbsp;&nbsp;&nbsp;StaticFile</td></tr>
    <tr><th>Error Code</th><td>&nbsp;&nbsp;&nbsp;0x80070001</td></tr>

   </table>
  </div>
  <div id="details-right">
   <table border="0" cellpadding="0" cellspacing="0">
    <tr class="alt"><th>Requested URL</th><td>&nbsp;&nbsp;&nbsp;http://localhost:9074/login.aspx</td></tr>
    <tr><th>Physical Path</th><td>&nbsp;&nbsp;&nbsp;C:\Users\...\API\login.aspx</td></tr>
    <tr class="alt"><th>Logon Method</th><td>&nbsp;&nbsp;&nbsp;Anonymous</td></tr>
    <tr><th>Logon User</th><td>&nbsp;&nbsp;&nbsp;Anonymous</td></tr>

   </table>
   <div class="clear"></div>
  </div>
 </fieldset>
</div>

<div class="content-container">
 <fieldset><h4>More Information:</h4>
  This error means that the request sent to the Web server contained an HTTP verb that is not allowed by the configured module handler for the request.
  <p><a href="https://go.microsoft.com/fwlink/?LinkID=62293&amp;IIS70Error=405,0,0x80070001,19044">View more information &raquo;</a></p>

 </fieldset>
</div>
</div>
</body>
</html>

however when I ran curl -X GET http://localhost:9074/login.aspx -i I got what I was expecting ie the login.aspx page had ran. I feel like I'm getting closer to the solution now :D

EDIT 4

What I think the issue is, is that the API can't handle http OPTIONS methods. I had put into global.asax.cs:

        protected void Application_BeginRequest()
        {
            if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) &&
                Request.HttpMethod == "OPTIONS")
            {
                Response.Flush();
            }
        }

so when I first go to /GetAll it sends first a OPTIONS request where the response gets flushed and then a GET request which tries to retrieve the data. Without authentication this work fine. Since I've got authentication before retrieving the data from \GetAll it redirects the page to Login.aspx to see if the user is authenticated because of the redirect it tries to send the OPTIONS request again. Since it's not a new request it doesn't run Application_BeginRequest() so the OPTIONS request doesn't get flushed. Flushing the OPTIONS request was a work around for the issue of it returning HTTP Statuses 405 (this was before I had implemented the authentication) but as it's a redirect then it doesn't flush the OPTIONS request and just returns 405 so the GET request doesn't get sent since it's expecting a http status 2XX. How do I properly handle the OPTIONS request? Is there something like Application_BeginRedirect where I can flush the OPTIONS request like I did properly? Would this be inappropriate?

Manib
  • 171
  • 15
  • Should'nt be your Api should return 401 status code and your react app will handle that status to show the login page? About the cors issue, did you try to disable the Auth check in your Api and make React do a request if returns success? – rjs123431 Oct 25 '22 at 11:52
  • The authentication is handled by a separate project. What my API does is it checks for authentication from the incoming request. If there's no auth then it will redirect to the login page (a different project, so it's an external url). This is if the domain is the same as the API ie http://localhost:9074. What I would like the API to do is that when it sees the **FETCH** request from React coming in instead of redirecting it it sends it the link which the react app will then redirect. Why do this? Because the fetch request is expecting data back. It's not expecting to be redirected. – Manib Oct 25 '22 at 13:01
  • I don't quite understand what your second suggestion is. Could you clarify please? If you're asking whether React will work without the authentication in the API then the answer is yes. I'm getting http status 405 on the pre-flight request for the login.aspx page. – Manib Oct 25 '22 at 13:04
  • `What I would like the API to do is that when it sees the FETCH request from React coming in instead of redirecting it it sends it the link which the react app will then redirect.` is this working already? It seems that your Api is handling same as the user is put the /getall url in the browser – rjs123431 Oct 25 '22 at 16:14
  • If your Api returns the data without authentication, then that is correct. What seems to be the issue, from what I understand, is you let your Api redirect to the login page as you mentioned here: `If someone was accessing from the API directly (ie they were to visit http://localhost:9074/Customer/GetAll/) then it would just redirect the user instead to the login page to be authenticated.` An Api should not act like this, it should just return 401 status if someone access it without auth token, etc. – rjs123431 Oct 25 '22 at 16:18
  • ```is this working already?``` - I had set up the backend and was testing it out by printing the link from FETCH in React before I would implement the front end. That's when I had realized the issue. – Manib Oct 27 '22 at 07:44
  • ```What seems to be the issue, from what I understand, is you let your Api redirect to the login page as you mentioned here:...``` It has to redirect to login.aspx that's where it checks if the user is authenticated or not. Please read through **EDIT 4** that's the latest on this issue. – Manib Oct 27 '22 at 07:46
  • Good that you solved the issue. Anyways, I would suggest you take a look at other ways of designing the Api, as what I said above, Api should only return a response and not redirect. You may replace your login.aspx with attribute that checks for authentication. Good luck. – rjs123431 Oct 27 '22 at 08:23

1 Answers1

-1

I had found the solution here. It turns out that the OPTIONS request handler was removed from the Web.Config file. By commenting that out it started working.

Manib
  • 171
  • 15