Enable CORS in Azure Mobile Services - OPTIONS not authorized - c#

I am creating .Net web services using Azure Mobile Services. The services themselves work fine, but I want to enable CORS.
My Global.asax contains:
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod != "OPTIONS") return;
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET,POST");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers",
"Authorization, Origin, Content-Type, Accept, X-Requested-With,x-zumo-application,x-zumo-installation-id");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
My WebAPIConfig.cs contains:
public static void Register()
{
ConfigOptions options = new ConfigOptions();
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
var cors = new EnableCorsAttribute("*", "*", "*","*");
config.EnableCors(cors);
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
}
My request/response:
OPTIONS http://********.azure-mobile.net/API/MyLogin?username=username&password=password&email=testtest%40example.com&_=140191793307 HTTP/1.1
Host: ********.azure-mobile.net
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: null
Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-type,x-zumo-application,x-zumo-installation-id
Connection: keep-alive
Cache-Control: max-age=0
HTTP/1.1 401 Unauthorized
Content-Length: 81
Content-Type: application/xml; charset=utf-8
Server: Microsoft-IIS/8.0
WWW-Authenticate: Basic realm="Service"
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=50b9234b61ec5f663e817ec57c430ca7b921bbcd842719dfc2bdc27374adea87;Path=/;Domain=********.azure-mobile.net
Date: Wed, 04 Jun 2014 21:38:56 GMT
<Error><Message>Authorization has been denied for this request.</Message></Error>

There is a workaround for enabling CORS in Mobile Services here:
https://gist.github.com/HenrikFrystykNielsen/6c934be6c6c8fa9e4bc8
You don't need the Application_BeginRequest part -- requests/responses don't go through that code path -- they go through the OWIN pipeline. Good thing is that you only need the gist above to get going.
Hope this helps!
Henrik

Related

Asp.Net MVC WebApi CORS Request fails - no proposed solutions affecting the outcome

I have an Asp.Net NVC5 web application (running under Visual Studio 2017) on localhost:59569 (SiteApi). I have a second website (also running under Visual Studio 2017) on localhost:61527 (SiteClient) where a page once loaded makes the following api call to SiteApi:
$http({
url: 'http://localhost:59569/api/V2/' + alias,
method: 'POST',
data: pm,
xhrFields: { withCredentials: true },
headers: { 'Content-Type': 'application/json; charset=utf-8' }
})
.then(th, ex);
NOTE: I have tried this with and without the xhrFields + withCredentials information using Microsoft IE, Microsoft Edge and Chrome.
Back on SiteApi the resulting preflight call for OPTIONS is intercepted by the following code in Global.asax which executes exactly as written and I can trace through the if statement when an inbound call for OPTIONS triggers it.
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
Response.Clear();
Response.Headers.Add("Access-Control-Allow-Origin", "*");
Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, Session");
Response.Flush();
Response.End();
}
}
The intention being to send the desired headers back to the client to allow CORS to function properly - however immediately after this code is executed the web page back on SiteClient reports that the request has been blocked due to missing 'Access-Control-Allow-Origin' header is missing and I can see that none of the headers I have specified have made it back to the client.
In an attempt to have CORS work I have the following nuget packages installed on the SiteAPI project.
Microsoft.AspNet.Cors
Microsoft.AspNet.WebApi.Cors
I adjusted the WebApiConfig.Register() method to include:
// Web API configuration and services
config.EnableCors();
I have tried many variations of adding the filter attributes to my controller like so:
[EnableCors("*", "*", "*", SupportsCredentials = true)]
I have tried adding my own custom ActionFilterAttribute from solutions found in other CORS related questions on stackoverflow - for example (among various others):
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
base.OnActionExecuting(filterContext);
I have the following to my web.config file:
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
I have ALL these solutions live in my project and in spite of this I still get CORS errors on the client side.
So the caveat here is that I also have a custom filter that looks up security on each API call - which works fine with ALL calls made from pages running on SiteApi. In the case of calls from SiteClient (CORS calls) the security filter never fires at all though I am getting 401 errors reported on the client in addition to the errors due to the missing CORS related headers.
I have cleared the caches of all browsers and the server itself. This is my first time working with CORS and I'm already exhausted from working with what really should be a simple solution. Looking for solutions here and would appreciate some help from those in the know.
Request Headers:
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 2
Content-Type: application/json; charset=UTF-8
Host: localhost:59569
Origin: http://localhost:61527
Referer: http://localhost:61527/Home/Index
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36 Edg/80.0.361.54
Response Headers:
Cache-Control: private
Content-Length: 6115
Content-Type: text/html; charset=utf-8
Date: Wed, 19 Feb 2020 00:46:06 GMT
Server: Microsoft-IIS/10.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcbngyMDA4MjZcRGV2XFRlY2hJVFxFQVNJV2ViQXBwXGFwaVxWMlxHZXRDb21tYW5kcw==?=
This is the flow I use. Sometimes browsers don't like "*". Other times, browsers don't like localhost, either. This is the logic I use (modify the allow headers as you see fit). It could be the fact that you aren't allowing the access control headers in your allowed headers, too:
[Add this to Global.asax]
protected void Application_BeginRequest(object sender, EventArgs e)
{
var originKey =
Request.Headers.AllKeys.FirstOrDefault(
a => a.Equals("origin", StringComparison.InvariantCultureIgnoreCase));
if (originKey != null && Request.HttpMethod == "OPTIONS"))
{
// Optional Whitelist check here can return without the headers below to reject CORS
Response.Headers.Add("Access-Control-Allow-Origin", Request.Headers[originKey]);
Response.Headers.Add("Access-Control-Allow-Credentials", "true");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUSH, DELETE, OPTIONS");
Response.Headers.Add("Access-Control-Allow-Headers",
"Authorization, Content-Type, Access-Control-Allow-Headers, X-Requested-With, Access-Control-Allow-Method, Accept");
Response.Flush();
Response.End();
return;
}
}

POST http request C# - Request Payload

I'm trying to POST HTTP request on C# .NET app, i need some help
because i have no idea if that is possible using the request data from google chrome developer tools.
General :
Request URL: https://my.domain.ae/data/verify/Proc
Request Method: POST
Status Code: 200
Remote Address: ip:443
Request Payload :
------WebKitFormBoundaryWVKBQMeG4O3GcfXR
Content-Disposition: form-data; name="Id"
016
------WebKitFormBoundaryWVKBQMeG4O3GcfXR
Content-Disposition: form-data; name="Lvl"
10
------WebKitFormBoundaryWVKBQMeG4O3GcfXR
Content-Disposition: form-data; name="Name"
Test
------WebKitFormBoundaryWVKBQMeG4O3GcfXR
Content-Disposition: form-data; name="Score"
23
------WebKitFormBoundaryWVKBQMeG4O3GcfXR
Content-Disposition: form-data; name="Coins"
100
------WebKitFormBoundaryWVKBQMeG4O3GcfXR--
Request Headers :
:authority: my.domain.ae
:method: POST
:path: /data/verify/Proc
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
content-length: 617
content-type: multipart/form-data; boundary=----WebKitFormBoundaryWVKBQMeG4O3GcfXR
cookie: user_language=ar; ****
origin: https://my.domain.ae
referer: https://my.domain.ae/index
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
x-requested-with: XMLHttpRequest

Invalid / expired Token - 401 - Unauthorized (Authorization Required) using Spring.Social.Twitter

I am using the Spring.Net Framework (spring.rest, spring.social.core and spring.social.twitter (2.x - oauth 1.0a) with c#.
The idea of the application will be for an event, people can link their twitter account to the event and as results are received for that person. The persons result it posted to their twitter account.
It will be made up of a website where users will register and give access. Then a console application which will process results (using the saved token set to post to registered users.
I have a web mvc application that asks a user to sign in and authorise an application so that it can post on behalf of the user. This information returned (token etc) is encrypted and kept in a db linked to the user. e.g. Our website - redirect to callback url - twitter - sign in +permission - back to callback url - store token info in db encrypted.
I then have a console application (eventually a service) which looks at results, when we have a result for a person with twitter account associated and permission given. We use that information to post to their account (their result).
The sign up process works fine, and i can post to the signed up person. saying they've registered. (website)
The console application initially worked to be able to post up results.
However, suddenly I am getting ( i think this is just a time frame (approx an hour) - works soon as a they register.)
"POST request for 'https://api.twitter.com/oauth/access_token' resulted in 401 - Unauthorized (Authorization Required)."
Linked to a user (in our system) - i am encrypting and storing verifier (from callback), secret and value. I then am using this to exchange for a access token to get secret and value to be able to post.
This is currently all a demo (going to 127.0.0.1) for a proof of concept.
So far only tried all this with the Spring.Social framework (c#)
When it works via console application -- RAW -
REQUEST
POST https://api.twitter.com/oauth/access_token HTTP/1.1
Accept: application/x-www-form-urlencoded,multipart/form-data,/
Authorization: OAuth oauth_consumer_key="xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, oauth_signature_method="HMAC-SHA1"
, oauth_timestamp="1417007918"
, oauth_nonce="1784014115"
, oauth_version="1.0"
, oauth_token="yyyyyyyyyyyyyyyyyyyyyyyyyyy"
, oauth_verifier="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
, oauth_signature="vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"
Content-Type: application/x-www-form-urlencoded
Host: api.twitter.com
Content-Length: 0
Connection: Keep-Alive
RESPONSE
HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0
content-length: 167
content-security-policy: default-src https:; connect-src https:; font-src https: data:; frame-src https:; img-src https: data:; media-src https:; object-src https:; script-src 'unsafe-inline' 'unsafe-eval' https:; style-src 'unsafe-inline' https:; report-uri https://twitter.com/i/csp_report?a=NVXW433SMFUWY%3D%3D%3D&ro=false;
content-type: text/html; charset=utf-8
date: Wed, 26 Nov 2014 13:18:40 UTC
etag: "fbf12c0103c8a9a4e85476ebc4a721fb"
expires: Tue, 31 Mar 1981 05:00:00 GMT
last-modified: Wed, 26 Nov 2014 13:18:39 GMT
pragma: no-cache
server: tsa_b
set-cookie: twittersess=BAh7BzoPY3JlYXRlZF9hdGwrCAnzQOxJAToHaWQiJTZjODM4ZWMwZDg4ZjY0%250ANGFjYWE0N2M1YWU0MmMzNmZl--af2ffe955256e30da84bc52b585d7c30b6926284; domain=.twitter.com; path=/; secure; HttpOnly
set-cookie: guest_id=v1%3A141700791973784560; Domain=.twitter.com; Path=/; Expires=Fri, 25-Nov-2016 13:18:40 UTC
status: 200 OK
strict-transport-security: max-age=631138519
vary: Accept-Encoding
x-connection-hash: bd73d7f87abb9c16caef67f675d8641b
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-mid: 4e5118e4376219e001674f1292c123965885210f
x-runtime: 0.10480
x-transaction: d24c3b12ea39be37
x-ua-compatible: IE=edge,chrome=1
x-xss-protection: 1; mode=block
oauth_token=xxxxx-xxxxxxxxxxxxxxxxxxxxxx&oauth_token_secret=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&user_id=2xxxxxxxxxxxxxx&screen_name=naxxxxxx
After about an hour(couple of hours in this instance) same code will fail.... with HTTP/1.1 401 Authorization Required - Invalid / expired Token.
REQUEST
POST https://api.twitter.com/oauth/access_token HTTP/1.1
Accept: application/x-www-form-urlencoded,multipart/form-data,*/*
Authorization: OAuth oauth_consumer_key="xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
, oauth_signature_method="HMAC-SHA1"
, oauth_timestamp="1417018992"
, oauth_nonce="2294192392"
, oauth_version="1.0"
, oauth_token="yyyyyyyyyyyyyyyyyyyyyyyyyyy"
, oauth_verifier="zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
, oauth_signature="vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"
Content-Type: application/x-www-form-urlencoded
Host: api.twitter.com
Content-Length: 0
Connection: Keep-Alive
RESPONSE
HTTP/1.1 401 Authorization Required
cache-control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0
content-length: 136
content-security-policy: default-src https:; connect-src https:; font-src https: data:; frame-src https:; img-src https: data:; media-src https:; object-src https:; script-src 'unsafe-inline' 'unsafe-eval' https:; style-src 'unsafe-inline' https:; report-uri https://twitter.com/i/csp_report?a=NVXW433SMFUWY%3D%3D%3D&ro=false;
content-type: text/html; charset=utf-8
date: Wed, 26 Nov 2014 16:23:13 UTC
expires: Tue, 31 Mar 1981 05:00:00 GMT
last-modified: Wed, 26 Nov 2014 16:23:13 GMT
pragma: no-cache
server: tsa_b
set-cookie: _twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCEzs6exJAToHaWQiJWI5MDgzMzk0Y2FhMGY2%250AMGNlNmEyYzQzZjk5OGEyNjAyIgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVy%250AOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA--86562a75e3ddabab3c688e726e6c42fe37a067ea; domain=.twitter.com; path=/; secure; HttpOnly
set-cookie: guest_id=v1%3A141701899357688419; Domain=.twitter.com; Path=/; Expires=Fri, 25-Nov-2016 16:23:13 UTC
status: 401 Unauthorized
strict-transport-security: max-age=631138519
vary: Accept-Encoding
www-authenticate: OAuth realm="https://api.twitter.com"
x-connection-hash: 24d849ec3bead25133b581794d34c74f
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-mid: aed281dc4b2e3fb483737ccd772ce1d8d3f6064d
x-transaction: d41ff14981c7a0ec
x-ua-compatible: IE=edge,chrome=1
x-xss-protection: 1; mode=block
<?xml version="1.0" encoding="UTF-8"?>
<hash>
<error>Invalid / expired Token</error>
<request>/oauth/access_token</request>
</hash>
Can anyone help or advice on what I should be persisting to be able to post on behalf of a user - once they've given us permission from a service or console application.
Thanks in advance.
I managed to solve issue. I removed Spring.Social from equation and wrote some quick code to talk to Twitter API without a framework, this gave me a better understanding of what the steps and process were.
Using this I then re-added the framework and I think I use it correctly now. As it all works.
My issue was that once I got my request token+verifier - supplying callback, consumer etc. I then used that to get an access token to perform posts etc.
However, every time I attempted to post I tried to exchange request token value + secret and verifier to get the access token. This eventually fails.
I changed code to save returned access token and just use that directly, works fine now.

When authenticating WCF service on IIS AuthenticateRequest is called only first time

I have a working implementation of OData WCF service which now need to be published in IIS with basic custom authentication.
Implementation is based on Microsoft OData example and works perfectly fine under the IIS Express. When I publish it to IIS 7.5 with only Basic Authentication enabled, AuthenticateRequest handler is only called on initial request, which returns status code 401 and asks to authenticate.
AuthenticateRequest is no longer called on subsequent requests. When debugging the service on IIS, BeginRequest is definitely called, it's just AuthenticateRequest not being present in the pipeline? Both are called every request in IIS Express.
IIS Authentication configuration:
IHttpModule code:
public class BasicAuthModule: IHttpModule
{
// based on http://msdn.microsoft.com/en-gb/data/gg192997.aspx
public void Init(HttpApplication app)
{
app.AuthenticateRequest += AuthenticateRequest;
app.BeginRequest += BeginRequest;
}
private void BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if(app.Context == null)
{
throw new Exception("Will not happen");
}
}
private void AuthenticateRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if(!app.Request.Headers.AllKeys.Contains("Authorization"))
{
CreateNotAuthorizedResponse(app, 401, 1, "Please provide Authorization headers with your request.");
app.CompleteRequest();
}
else if(!BasicAuthProvider.Authenticate(app.Context))
{
CreateNotAuthorizedResponse(app, 401, 1, "Logon failed.");
app.CompleteRequest();
}
}
private static void CreateNotAuthorizedResponse(HttpApplication app, int code, int subCode, string description)
{
var response = app.Context.Response;
// response.Status = "401 Unauthorized";
response.StatusCode = code;
response.SubStatusCode = subCode;
response.StatusDescription = description;
// response.AppendHeader("WWW-Authenticate", "Basic");
// response.End();
}
public void Dispose()
{
}
}
Web.config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="BasicAuthModule" type="WcfTestService.BasicAuthModule"/>
</modules>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
And the question: why authentication works in Visual Studio 2012 debug server but not in IIS 7.5?
Complete test project can be downloaded from here.
Edit:
I commented out excessive testing code in the CreateNotAuthorizedResponse function and response.End() which caused an exception (I added it last minute before posting).
When inspecting requests it looks like everything should work except that Cookie may be causing IIS to skip authentication for some reason. Below first 2 raw request - reply pairs:
Request 1:
GET http://localhost:8080/test/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788
Reply 1: (created by CreateNotAuthorizedResponse method)
HTTP/1.1 401 Please provide Authorization headers with your request.
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="localhost"
X-Powered-By: ASP.NET
Date: Mon, 20 Oct 2014 13:03:14 GMT
Content-Length: 6607
<!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 7.5 Detailed Error - 401.1 - Please provide Authorization headers with your request.</title>
Request 2 (when entered test:test - dGVzdDp0ZXN0):
GET http://localhost:8080/test/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Authorization: Basic dGVzdDp0ZXN0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,pl;q=0.6
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788
Response 2 (BeginRequest called but not AuthenticateRequest):
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="localhost"
X-Powered-By: ASP.NET
Date: Mon, 20 Oct 2014 13:03:17 GMT
Content-Length: 6531
<!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 7.5 Detailed Error - 401.1 - Unauthorized</title>
I think you are mixing up IIS built-in basic authentication with your own custom authentication module. The short answer is to disable Basic Authentication in IIS and enable Anonymous. This will pass all the auth work onto asp.net.
If your are testing in VS I'm assuming you are doing so via a browser that is auto launched when you hit F5.
With basic auth turned on IIS initially responds with a 401 which causes the browser to display the login form.
The credentials you enter there have to be valid windows credentials, which IIS validates against your windows accounts. Once IIS has validated these credentials it will pass the request along to your code.
If you enter valid windows credentials the event is raised but your code will reject it because the credentials are not test/test and return a 401.1
If you enter test/test then IIS is rejecting the credentials and sends back a 401 so your event is never called.
Final word: You should be testing your web service using an http client (e.g. unit test with System.Net.WebClient), or use a chrome plugin (postman/devhttp) to test at the http level. If you are already doing this then forgive my assumption.

Cannot Return Custom HTTP Error Details Remotely

This is a strange one. I'm running MVC 3 and have a custom action result that wraps exceptions and returns a message along with the standard HTTP error.
public class ExceptionResult : ActionResult
{
private readonly Exception _exception;
public ExceptionResult(Exception exception)
{
_exception = exception;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ClearHeaders();
response.Cache.SetNoStore();
response.ContentType = ContentType.Json;
var baseEx = _exception as BaseException ?? new ServerException(_exception);
var result = baseEx.GetResult();
var json = result.ToJSON();
response.Write(json);
response.StatusCode = (int)result.Status.Code;
}
}
When I run this locally I get exactly what I expect:
HTTP/1.1 400 Bad Request
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Thu, 01 Dec 2011 19:00:03 GMT
Content-Length: 81
{"error":"invalid_request","error_description":"Parameter grant_type is missing"}
But when I try to connect from a different machine I get the standard IIS error message instead:
HTTP/1.1 400 Bad Request
Cache-Control: no-store
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Thu, 01 Dec 2011 19:02:33 GMT
Content-Length: 11
Bad Request
UPDATE
There must be some http module somewhere in the IIS pipeline that is swallowing the response and rewriting the content. I wrote a module to log the request and response and it's returning exactly what I expect however what actually makes it to the browser is wrong.
2011-12-02 15:39:00,518 - ======== Request ========
2011-12-02 15:39:00,518 - GET /oauth/2/token HTTP/1.1
2011-12-02 15:39:00,519 - Cache-Control: max-age=0
2011-12-02 15:39:00,519 - Connection: keep-alive
2011-12-02 15:39:00,519 - Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
2011-12-02 15:39:00,519 - Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
2011-12-02 15:39:00,519 - Accept-Encoding: gzip,deflate,sdch
2011-12-02 15:39:00,519 - Accept-Language: en-US,en;q=0.8
2011-12-02 15:39:00,519 - Host: micah-pc:8095
2011-12-02 15:39:00,519 - User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2
2011-12-02 15:39:00,519 - =========================
2011-12-02 15:39:00,519 - OAuth exception occurred.
BoomTown.OAuth.OAuthException: Parameter grant_type is missing
at BoomTown.OAuth.Request.TokenRequest.GetRequestValidator() in C:\code\BoomTown\Api\BoomTown.OAuth\Request\TokenRequest.cs:line 19
at BoomTown.OAuth.Request.OAuthRequestBase.Validate() in C:\code\BoomTown\Api\BoomTown.OAuth\Request\OAuthRequestBase.cs:line 33
at BoomTown.OAuth.Request.OAuthRequestBase..ctor(HttpRequestBase request, IOAuthServiceLocator serviceLocator) in C:\code\BoomTown\Api\BoomTown.OAuth\Request\OAuthRequestBase.cs:line 28
at BoomTown.OAuth.Request.TokenRequest..ctor(HttpRequestBase request, IOAuthServiceLocator serviceLocator) in C:\code\BoomTown\Api\BoomTown.OAuth\Request\TokenRequest.cs:line 13
at BoomTown.Api.Web.Controllers.OAuth.V2.OAuthController.Token() in C:\code\BoomTown\Api\BoomTown.Api.Web\Controllers\OAuth\V2\OAuthController.cs:line 26
2011-12-02 15:39:00,520 - ======= Response =======
2011-12-02 15:39:00,520 - HTTP/1.1 400 Bad Request
2011-12-02 15:39:00,520 - Cache-Control: no-store
2011-12-02 15:39:00,520 - X-AspNet-Version: 4.0.30319
2011-12-02 15:39:00,520 - Content-Type: application/json; charset=utf-8
2011-12-02 15:39:00,520 - {"error":"invalid_request","error_description":"Parameter grant_type is missing"}
SOLUTION
Thanks to a little sleuthing I was able to figure it out. I setup IIS tracing which confirmed my suspicions that it was related to the customerrormodule which was intercepting my requests and overwriting my error messages. I kept monkeying with the
<system.web>
<customErrors />
<system.web>
settings but to no avail. I was on the right track, but since it's IIS 7 that I'm running I needed to change the correct web.config section like this:
<system.webServer>
<httpErrors errorMode="Detailed" />
</system.webServer>
Now all my custom JSON messages come through perfectly. Big thanks to Jason Finneyfrock for the tag team on this one.
In your web.config, do you have httpErrors defined to only be DetailedLocalOnly? I'm not sure whether or not the content would be removed in this situation.
http://www.iis.net/ConfigReference/system.webServer/httpErrors
I came across this, not sure if it will help:
context.HttpContext.Response.TrySkipIisCustomErrors = true;

Categories

Resources