Adding the WWW-Authenticate header gives an error - c#

I'm trying to configure basic access authorization on my .NET HttpListener but I keep running into the same error. I've tried all the solutions that can be found on this site and many others but with no success.
I need to use admin/admin as username/password for basic authentication. The wikipedi page shows how the header should look, which I followed.
I keep getting the error "The header WWW-Authenticate must be changed with the correct method, parameter:name" there is however no parameter called "name" that must be added, like shown on the wikipedia page. I've ran out of options unfortunately and hope that somebody can help.
My code is as follows
private void WebRequestCallback(IAsyncResult result)
{
if (httpListener == null)
{
return;
}
HttpListenerContext context = httpListener.EndGetContext(result);
if (basicAccessAuth)
{
HttpListenerRequest Request = context.Request;
HttpListenerResponse Response = context.Response;
httpListener.AuthenticationSchemes = AuthenticationSchemes.Basic;
NameValueCollection nvCol = new NameValueCollection();
nvCol.Add("Authorization", "admin:admin");
httpListener.Realm = "Overflow";
Request.Headers.Add(nvCol); // error gets thrown here, missing "name" parameter
Response.Headers.Add("WWW-Authenticate: Basic YWRtaW46YWRtaW4=");
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
MessageBox.Show(identity.Name);
}
httpListener.BeginGetContext(new AsyncCallback(WebRequestCallback), httpListener);
if (ReceiveWebRequest != null)
{
ReceiveWebRequest(context);
}
ProcessRequest(context);
}

I have managed to figure out my issue. I added the header in the wrong way, it should be Response.AddHeader instead of Response.Headers.Add

Related

MVC 4 RedirectToAction does not see Custom Header

If you start a new Web Project, and create a new MVC4 application (with sub-kind as "WebApi", you can paste the below code in (overwriting HomeController.cs) to get the code to work.
I have a MVC4 application (with WebApi).
I am trying to set a custom-header in a MVC controller method and then do a RedirectToAction. The custom-header is not seen in the second mvc-controller-method.
I am able to set a cookie in the first mvc-controller-method and see it in the second mvc-controller-method (after a RedirectToAction).
Is there a way to see the custom-header I set in the second mvc-controller-method after a RedirectToAction ?
Thanks.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace MyMvc4WebApiProjectNamespace.Controllers
{
public class HomeController : Controller
{
private const string CustomCookieName = "CustomCookieName";
private const string CustomHeaderName = "X-CustomHeaderName";
private const string IISExpressRootUrl = "http://localhost:55937/"; /* open up the project properties and go to the web tab and find the iis-express area to get the correct value for your environment */
public ActionResult Index()
{
IEnumerable<string> webApiValues = null;
string value1 = null;
string value2 = null;
HttpClientHandler handler = new HttpClientHandler
{
UseDefaultCredentials = true,
PreAuthenticate = true
};
using (var client = new HttpClient(handler))
{
string valuesUri = IISExpressRootUrl + "api/Values";
webApiValues = client
.GetAsync(valuesUri)
.Result
.Content.ReadAsAsync<IEnumerable<string>>().Result;
if (null != webApiValues)
{
value1 = webApiValues.ElementAt(0);
value2 = webApiValues.ElementAt(1);
}
else
{
throw new ArgumentOutOfRangeException("WebApi call failed");
}
}
HttpCookie customCookie = new HttpCookie(CustomCookieName, "CustomCookieValue_ThisShowsUpIn_MyHomeControllerAlternateActionResult_Method");
Response.Cookies.Add(customCookie);
HttpContext.Response.AppendHeader(CustomHeaderName, "CustomHeaderValue_This_Does_Not_Show_Up_In_MyHomeControllerAlternateActionResult_Method");
//Response.AppendHeader(CustomHeaderName, value2);
return RedirectToAction("MyHomeControllerAlternateActionResult");
}
public ActionResult MyHomeControllerAlternateActionResult()
{
IEnumerable<string> webApiReturnValues = null;
CookieContainer cookieContainer = new CookieContainer();
foreach (string cookiename in Request.Cookies)
{
if (cookiename.Equals(CustomCookieName, StringComparison.OrdinalIgnoreCase))
{
var cookie = Request.Cookies[cookiename];
cookieContainer.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, "localhost"));
}
}
if (cookieContainer.Count < 1)
{
throw new ArgumentOutOfRangeException("CookieContainer did not find the cookie I was looking for");
}
else
{
Console.WriteLine("This is what actually happens. It finds the cookie.");
}
HttpClientHandler handler = new HttpClientHandler
{
UseCookies = true,
UseDefaultCredentials = true,
PreAuthenticate = true,
CookieContainer = cookieContainer
};
using (var client = new HttpClient(handler))
{
bool customHeaderWasFound = false;
if (null != this.Request.Headers)
{
if (null != this.Request.Headers[CustomHeaderName])
{
IEnumerable<string> headerValues = this.Request.Headers.GetValues(CustomHeaderName);
client.DefaultRequestHeaders.Add(CustomHeaderName, headerValues);
customHeaderWasFound = true;
}
}
/*I wouldn't expect it to be in the below, but I looked for it just in case */
if (null != this.Response.Headers)//
{
if (null != this.Response.Headers[CustomHeaderName])
{
IEnumerable<string> headerValues = this.Response.Headers.GetValues(CustomHeaderName);
client.DefaultRequestHeaders.Add(CustomHeaderName, headerValues);
customHeaderWasFound = true;
}
}
if (!customHeaderWasFound)
{
Console.WriteLine("This is what actually happens. No custom-header found. :( ");
}
string valuesUri = IISExpressRootUrl + "api/Values";
webApiReturnValues = client
.GetAsync(valuesUri)
.Result
.Content.ReadAsAsync<IEnumerable<string>>().Result;
if (null == webApiReturnValues)
{
throw new ArgumentOutOfRangeException("WebApi call failed");
}
}
return View(); /* this will throw a "The view 'MyHomeControllerAlternateActionResult' or its master was not found or no view engine supports the searched locations" error, but that's not the point of this demo. */
}
}
}
Response headers are never copied automatically to requests - so setting any custom headers on response will not impact next request issued to handle 302 redirect.
Note that it is the case even with cookies: response comes with "set this cookie" header, and all subsequent request will get "current cookies" header.
If you have your own client you may be able to handle 302 manually (not possible if you are using browser as client).
As another answer stated, response headers are about this response, not the next one. Redirecting is not a server-side action. A redirect instructs the client to perform a completely new request, and of course in a new request, the response headers for the old request are not present. So return RedirectToAction("MyHomeControllerAlternateActionResult"); is guaranteed to not have this response's headers when the browser initiates the new request.
In trying to solve this problem, one might think of trying to persist the data to the next request server-side, such as through a cookie or in an explicit session variable, or implicitly via use of ViewBag/ViewData/TempData. However, I don't recommend this as using session state heavily has performance implications in large/high-usage web sites, plus there are other negative and subtle side-effects that you may run into down the road. For example, if a person has two browser windows open to the same web site, they can't be doing different actions reliably, as the session data for one window can end up being served to the other one. Avoid session usage as much as possible in your web site design—I promise this will benefit you down the road.
A slightly better way, though still with its problems, is to redirect to a URL with querystring parameters containing a payload. And, instead of the whole set of data, you can provide a key that can be pulled from the session (as long as it's also bound to their IP address and is large like a GUID or two together). However, relying on session state is still not ideal as stated before.
Instead, consider using server-side redirection such as child actions. If you find that hard because what you want to call is a main controller you have a few options:
If you're using dependency injection, add a parameter to the current controller (saving it from the constructor and using it in the request method) that is the desired controller you want to "redirect" to. You can then call that controller directly. This may not be ideal (as all calls to this controller also have to new up a copy of that one), but it does work. Trying to new up the other controller manually can also work, but for reasons I don't fully remember, I think this can give some additional problems. In any case, this method can give issues accessing the HttpRequest context and other context objects correctly, though this can be worked around.
Rearchitect your application so that controllers are not the place where full pages are rendered. Instead, use them as "smart routers" that call child actions to perform the real work. Then, you can call the same child actions from any controller. But this still has problems.
Perhaps the best way is to add custom routing logic through action filters or other means (search the web!) so that the correct controller is hit in the first place! This may not always be possible, but sometimes the need to redirect to another controller mid-procedure actually points to a larger design problem. Focusing on how to cause the knowledge of which controller to hit to be available earlier in the pipeline (such as during routing) can reveal architecture problems, and can reveal likely solutions to them.
There may be other options that I haven't thought of, but at least you have a few alternatives to the simple "no way to do that."
I was able to do something similar like what the user is requesting in the following (rudimentary) way:
In the redirect, add a custom query string parameter
Create a custom Module that checks for that parameter and appends the custom header (read http://dotnetlionet.blogspot.com/2015/06/how-to-add-httpmodule-in-mvc5.html on how to do your own module)
In this way I was able to get my custom headers to be picked up

page.CheckOut() throws SPException: URL is invalid even though page exists

I'm writing an event receiver for a SharePoint site, and I want this receiver to edit the contents of a basic page after it is created. Here is the function that is giving me issues:
public void FillPage(SPSite site, SPItemEventProperties properties, string pageName)
{
using (site)
{
// Wait until the page has been generated
while (!PageExists(properties.BeforeUrl))
{
Thread.Sleep(10000);
}
Thread.Sleep(30000); // Added so I can check that the URL exists in my browser
SPWeb web = site.RootWeb;
SPFile page = web.GetFile(properties.BeforeUrl);
page.CheckOut(); // Throws SPException: 'URL is invalid'.
...
}
}
The PageExists function simply uses an HttpWebRequest pointed to the page that was just generated:
public bool PageExists(string url_ending)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri((the root site URL) + url_ending));
request.Timeout = 15000;
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return true;
}
catch (WebException we)
{
if (we.Message.Contains("Unauthorized"))
{
return true; // If it's an authorization error, the page exists but access was denied
}
return false;
}
}
The CheckOut function returns: "SPException: The URL '...' is invalid. It may refer to a nonexistent file or folder, or refer to a valid file or folder that is not in the current Web." In addition, I added a breakpoint at the line containing 'page.Checkout()' and examined that page variable, and found that all of its members throw a 'System.IO.FileNotFoundException' or a 'System.IndexOutOfRangeException', even though it's pointed to the correct URL. I've also checked that the HttpWebRequest is pointed to the correct URL, which it is, and as mentioned in the comments I check to see that the page exists in my browser before the code can attempt to check it out.
From what searching I did, I found out that this error is often thrown when the database logs are filling up. But from what I found, in that case this error would also occur when attempting to check out documents from the SharePoint site itself, and I have not had that issue; I only get this error when I attempt to check out a page from the event receiver. Any idea what's going on?
I found this article
http://blog.mastykarz.nl/inconvenient-spwebgetfilestring/
which explains that GetFile can have unexpected results.
There is a workaround provided:
using (SPSite site = new SPSite("http://moss"))
{
using (SPWeb web = site.RootWeb)
{
object o = web.GetFileOrFolderObject("/site/subsite1/Pages/default.aspx");
if (o is SPFile)
{
SPFile f = (SPFile)o;
}
}
}
you should give it a try !

How can I use existingResponse="Auto" successfully?

So I am returning detailed 400 error responses from my MVC web app. Setting existingResponse="PassThrough" works, but that's not what I want. I don't want to expose all failures, I only want to expose them when I have custom responses.
Auto, is set by default, but I deliberately set it. However, the documentation says "SetStatus" flag must be set, but I have no idea how to do such a thing. I wrote the following four controller methods in order to test it, and only BadRequestD works. The others set the status code and the status just fine, but the body content is "Bad Request".
public ActionResult BadRequestA()
{
Response.StatusCode = 400;
return Content("weeeeee");
}
public ActionResult BadRequestB()
{
Response.Status = "400 U DUN MESSED UP";
return Content("weeeeee");
}
public ActionResult BadRequestC()
{
Response.Status = "400 U DUN MESSED UP";
Response.StatusCode = 400;
return Content("weeeeee");
}
public ActionResult BadRequestD()
{
Response.StatusCode = 400;
Response.TrySkipIisCustomErrors = true;
return Content("weeeeee");
}
However, the documentation says "SetStatus" flag must be set, but I have no idea how to do such a thing
It's actually talking about the fTrySkipCustomErrors flag/argument to the IHttpResponse::SetStatus method in the IIS C++ SDK (see note I added to bottom of documentation here). But in ASP.NET the flag is exposed as Response.TrySkipIisCustomErrors. So according to:
http://www.iis.net/configreference/system.webserver/httperrors
Auto = Leaves the response untouched only if the SetStatus flag is set
I would expect to see IIS replace the response with its own html error page content (you can configure what that content is) by default unless you set:
Response.TrySkipIisCustomErrors = true;
Which is what you're seeing.
Additional related info, in MVC5 it seems to act as if that flag is true even if it's false for uncaught exceptions which I don't see in WebForms. As a workaround in Global.asax I'm:
protected void Application_Error()
{
var error = Server.GetLastError();
Server.ClearError();
//code to log error here
var httpException = error as HttpException;
Response.StatusCode = httpException != null ? httpException.GetHttpCode() : (int)HttpStatusCode.InternalServerError;
}
If you need to have custom responses with 4xx http statuses and still want to use Custom Error Pages here's what you should do:
set existingResponse="Auto" in web.config;
set TrySkipIisCustomErrors = true in your action (one that returns 4xx status and a content);
clear server error in global.asax (in Application_Error() - Server.ClearError()) and re-set the status code (Reponse.StatusCode = ((HttpException)Server.GetLastError()).GetHttpCode())
It's weird that IIS team didn't implement existingResponse attribute for specific status codes, so it's impossible to use existingResponse="PassThrough" just for one (or few) codes.

Issuing HEAD request with IRestClient in ServiceStack

The context: I've built a REST service which handles 'Profile' objects. Each profile is required to have a unique name. One of the operations that clients will need to do for validation purposes is check to make sure that a profile with the given name does not already exist.
Rather than build a RPC-style 'ProfileExists' method, I would prefer to stay within REST design principles and issue a HEAD request to the Profile with the given name and then return the appropriate response code depending on whether the profile already exists or not (200, 404, respectively), no response body needed.
Following the conventions with the newer ServiceStack API, I've set up a method to accept Head requests and tested it successfully for both cases using Fiddler:
public object Head(GetProfile request)
{
ValidateRequest(request);
HttpStatusCode responseCode;
using (var scope = new UnitOfWorkScope())
{
responseCode = _profileService.ProfileExists(request.Name) ? HttpStatusCode.OK : HttpStatusCode.NotFound;
scope.Commit();
}
return new HttpResult { StatusCode = responseCode };
}
The trouble is on the client-side. Issuing the HEAD request through ServiceStack's IRestClient interface is proving difficult. While there are methods for Get, Post, Put, and Delete, there is no method for Head. From there I assumed I could use CustomMethod to specify the HEAD verb explicitly as a parameter:
public bool ProfileExists(string profileName)
{
try
{
var response = _restClient.CustomMethod<IHttpResult>(HttpMethods.Head, new GetProfile { Name = profileName });
return response.StatusCode == HttpStatusCode.OK;
}
catch (WebServiceException ex)
{
if (ex.StatusCode == 404)
return false;
}
// Return false for any other reason right now.
return false;
}
However, the underlying implementation (ServiceClientBase) throws an exception when validating the HttpVerb parameter:
if (HttpMethods.AllVerbs.Contains(httpVerb.ToUpper()))
throw new NotSupportedException("Unknown HTTP Method is not supported: " + httpVerb);
The set HttpMethods.AllVerbs contains all of the usual verbs for RFC 2616 and more. Unless this behavior is a bug, throwing an exception for any of the known HTTP verbs suggests that the author's intent for CustomMethod did not include being able to issue requests for a known HTTP verb.
Which leads me to my question: How do I issue a HEAD request on the client side in ServiceStack?
This was a bug:
if (HttpMethods.AllVerbs.Contains(httpVerb.ToUpper()))
throw new NotSupportedException("Unknown HTTP Method is not supported: " + httpVerb);
That I've just fixed in this commit. This fix will be available on the next release of ServiceStack (v3.9.33+) due this weekend.

ProcessRequest from RequestInterceptor never ends [WCF]

So, I'm using the Developer's Guide to the WCF REST Starter Kit and having a problem with the RequestInterceptor. I have the exact code the guide is showing but for some reason the method never ends.
This is my code:
public override void ProcessRequest(ref RequestContext requestContext)
{
GenerateErrorResponse(requestContext, HttpStatusCode.Forbidden, "shit happens!");
}
public void GenerateErrorResponse(RequestContext context, HttpStatusCode statusCode, string errorMessage)
{
XElement response = XElement.Load(new StringReader(string.Format(ERROR_HTML, errorMessage)));
Message reply = Message.CreateMessage(MessageVersion.None, "action", response);
HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty()
{
StatusCode = statusCode,
//StatusDescription = errorMessage
};
responseProp.Headers[HttpRequestHeader.ContentType] = "text/html";
reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
context.Reply(reply);
context = null;
}
My call gets stuck at context.Reply(reply); I have no idea what I'm doing wrong... any heads up?
thanks
Ok, my bad... it was a simple/stupid issue.
First of all there was an Exception taking place that I was not seeing... once I added a try catch and tracing I discovered I was getting the following error: "System.InvalidOperationException: This collection holds request headers and cannot contain the specified response header".
After looking a little closer I noticed I was adding a HttpRequestHeader instead of the HttpResponseHeader... my bad :(
responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";

Categories

Resources