Send Data Using the WebRequest Class to DotNetOpenAuth website - asp.net

I am trying to send data to DotNetOpenAuth website as described here http://msdn.microsoft.com/en-us/library/debx8sh9.aspx
Sender receive (500) Internal Server Error. The same code for blank website without DotNetOpenAuth works fine. Should I tweak something?
Here is an exception:
System.ArgumentNullException was unhandled by user code
Message="Value cannot be null.\r\nParameter name: key"
Source="mscorlib"
ParamName="key"
StackTrace:
at System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at DotNetOpenAuth.OAuth.ChannelElements.OAuthChannel.ReadFromRequestCore(HttpRequestInfo request) in c:\BuildAgent\work\7ab20c0d948e028f\src\DotNetOpenAuth\OAuth\ChannelElements\OAuthChannel.cs:line 145
at DotNetOpenAuth.Messaging.Channel.ReadFromRequest(HttpRequestInfo httpRequest) in c:\BuildAgent\work\7ab20c0d948e028f\src\DotNetOpenAuth\Messaging\Channel.cs:line 372
at DotNetOpenAuth.OAuth.ServiceProvider.ReadRequest(HttpRequestInfo request) in c:\BuildAgent\work\7ab20c0d948e028f\src\DotNetOpenAuth\OAuth\ServiceProvider.cs:line 222
Exception occurs on last line of the code:
private void context_AuthenticateRequest(object sender, EventArgs e)
{
// Don't read OAuth messages directed at the OAuth controller or else we'll fail nonce checks.
if (this.IsOAuthControllerRequest())
{
return;
}
if (HttpContext.Current.Request.HttpMethod != "HEAD")
{ // workaround: avoid involving OAuth for HEAD requests.
IDirectedProtocolMessage incomingMessage = OAuthServiceProvider.ServiceProvider.ReadRequest(new HttpRequestInfo(this.application.Context.Request));

If you're sending the POST request with a Content-Type of application/x-www-form-urlencoded, but the POST entity contains something other than the normal key1=value1&key2=value2 format, that might explain it. It looks like DotNetOpenAuth can't handle a POST entity that claims to be name=value pairs but only has a value without a key in front of it. Arguably that's a bug in DotNetOpenAuth since normally that's just considered a value of a null key.
If you're not sending key=value pairs at all, I suggest you drop or change the Content-Type header so that you're not claiming to be sending key=value pairs. If you are sending them, but intentionally sending a null key, then hang on while the bug gets fixed.

Related

Handle Unauthorized Request and Return Status Code 404

I am developing a standalone .Net Core API targeting framework .Net Core 2.2.The authentication scheme is JWTBearerTokens connecting to our ADFS Identify server.
When I call an API endpoing decorated with the [Authorize] attribute I am getting a 401 Unauthorized response, which is expected and default behaviour.
What I want to do next is instead of having that same call return a 401, I would like to return the status code to be 404. (I don't want to get into great details of why 404. Simply, I do not want to expose that the endpoint exists if a valid token is not included in request)
In previous .Net Framework WebAPI you could create your own attribute and override the HandleUnauthorizedRequest method and return the status code you want.
I have reviewed the documentation on policy-based authorization, but have not tried the sample or tried implementing it. The policy handler looks more to do with handling (return success or fail) if a policy is not fulfilled. I do not see anywhere where you can return a different status code on failure. So that only would make sense if I start checking against actual Policies.
Any insights?
Returning 404 instead of 401 is bad practice(as mentioned in the comments by #Chris Pratt) and must be avoided. Consider these cases,
You're leaving the project to someone else and they can't figure why 404 is returned
A 404 is returned when you call the homepage/Index page. Poor ideology.
Later on in the project, you decide to allow post requests without authentication. So on and so forth.
Anyways, as part of the community, I'll give you the answer...
Add this to your global.asax
void Application_EndRequest(object source, System.EventArgs args)
{
if (Response.StatusCode == 401)
{
Response.ClearContent();
Response.RedirectToRoute("ErrorH", (RouteTable.Routes["ErrorH"] as Route).Defaults);
}
}
And in routeConfig, create a route for your errorHandler :
routes.MapRoute(
"ErrorH",
"Error/{action}/{errMsg}",
new { controller = "CustomController", action = "Change401To404", errMsg = UrlParameter.Optional }
);
And in your custom controller :
public class CustomController : Controller //or Base
{
public ActionResult Change401To404(){
//Do whatever you want
}
}
PS: This is not the only way, there are many other ways to do it. But at least in this method, you can differentiate real 404 responses from 401 responses.

Check logged user with normal and ajax request

I use interceptor to check if a user is logged in every controller call like this :
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) {
if(request.getSession().getAttribute("user") == null) {
response.sendRedirect("redirect:/login?next="+
URLEncoder.encode(
request.getRequestURL().toString() + "" +
(request.getQueryString() != null ? "?" + request.getQueryString() : "")
,"utf-8");
return false;
}
return true;
}
It work fine for normal request but for ajax request i can't make a response.sendRedirect(..).
How to know if it's a ajax or normal request ?
How can i do it like if i got a ajax error ?
$.ajax({
.....
success : function(data) { ...... },
error : function(){
alert("login error"); // or
document.location = '/path/login' // or something else
}
});
There a other way to handle it rather than using interceptor ?
1. How to know if it's a ajax or normal request ?
You can check inside your interceptor for the existence of the X-Requested-With header. This header is always added to the ajax request by the jQuery library (to my knowing almost all major js libraries add it as well) with the purpose of preventing the Cross-Site request forgery. To figure out if the request is ajax, you can write your preHandle method like
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) {
String requestedWith = request.getHeader("X-Requested-With");
Boolean isAjax = requestedWith != null ? "XMLHttpRequest".equals(requestedWith) : false;
...
}
2. How can i do it like if i got a ajax error ?
As you've already noticed, ajax request don't recognize server side redirects, as the intention of the server side redirects is to be transparent to the client. In the case of an ajax request, don't do redirect rather set some status code to the response e.g. response.setStatus(respCode) or add a custom header e.g. response.setHeader("Location", "/path/login"), and read it through in the jQuery's complete method which is a callback that follows after either success or error, e.g.
$.ajax({
//...
complete: function(xhr, textStatus) {
console.log(xhr.status);
console.log(xhr.getResponseHeader('Location'));
// do something e.g. redirect
}
});
3. There a other way to handle it rather than using interceptor ?
Definitely. Checkout Spring Security. Its a framework, and adds a bit to the learning curve, but its well worth it. It will add much more than a custom solution, e.g. you'll get authorization mechanism on top of the authentication. When your application matures, you'll notice that the straigthforward implementation that you're on to now, has quite a few security flaws that are not hard to exploit e.g. session fixation, where spring security can easily protect you. There's plenty of examples online, and you'll get better support here on the SO in comparison to any custom solution. You can unit test it, an asset I personally value very much
You could simply:
Refuse ajax requests before the user is properly logged in
once the user logs in, set a security token in the session or somewhere
pass that token in the ajax request and use that token to validate on the server side prehandle
in your case you would check the existence of the token before running into the code
Also, the preHandle does not have to apply to every routes, you could also have different routes each with different authorisation, prehandle, code.

Stop authentication at an early pipeline stage- unless going to /Login?

I'm writing a MessageHandler to authenticate a user.
If a request is not containing a special header , I want to block it at the MessageHandler stage.
But if the user wants to go to the Users/Login method, he will probably have no header (because he is not Login yet ).
The problem is that I don't want to block him at the [authorize] controller level.
It's pretty simple :
If he doesn't have the header and he is not on the way to login — BLOCK
If he doesn't have the header and he is on the way to login — only then - ALLOW
Question
1) At the MessaageHandler stage , how can I know that he is on a way to do login ? ( NB : I don't mention the {action} in the route. e.g. :
--
public class User :ApiController
{
[HttpPost]
public bool CheckLogin (....) //i'm not specifying action in the route
{
}
}
2) Looking at the command to read the header :
AuthenticationHeaderValue auth = actionContext.Request.Headers.Authorization;
But - Authorization != Authentication.
So why does web api reference the authorization header as an Authentication ?
The MessageHandler executes before routing has occurred. So at this stage you don't know yet which controller action will be executed.
One possibility would be to check the verb and the path being requested and perform the custom verification based on that:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string path = request.RequestUri.PathAndQuery;
if (request.Method == HttpMethod.Post && path.StartsWith("/api/checklogin", StringComparison.InvariantCultureIgnoreCase))
{
// Do not enforce the presence of the custom header
return base.SendAsync(request, cancellationToken);
}
// Check for the presence of your custom header
}
So why does web api reference the authorization header as an Authentication ?
At HTTP level, the header is called Authorization.
I believe you are trying to reinvent the wheel while it is already there. You have Autorize and AllowAnonymous (for your Login action) and then you could have a custom authentication filter to read the header and set up the Principal for the request lifetime.
The reason for that is that the term authorization header has been always used in the context of HTTP header-based authentication. Someone who used the tern for the first time was probably not aware that authentication header would probably be slightly more appropriate.

ASP.NET Web API removing HttpError from responses

I'm building RESTful service using Microsoft ASP.NET Web API.
My problem concerns HttpErrors that Web API throws back to user when something go wrong (e.g. 400 Bad Request or 404 Not Found).
The problem is, that I don't want to get serialized HttpError in response content, as it sometimes provides too much information, therefore it violates OWASP security rules, for example:
Request:
http://localhost/Service/api/something/555555555555555555555555555555555555555555555555555555555555555555555
As a response, I get 400 of course, but with following content information:
{
"$id": "1",
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'MyNamespaceAndMethodHere(Int32)' in 'Service.Controllers.MyController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
Something like this not only indicates that my WebService is based on ASP.NET WebAPI technology (which isn't that bad), but also it gives some information about my namespaces, method names, parameters, etc.
I tried to set IncludeErrorDetailPolicy in Global.asax
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Never;
Yeah, that did somehow good, now the result doesn't contain MessageDetail section, but still, I don't want to get this HttpError at all.
I also built my custom DelegatingHandler, but it also affects 400s and 404s that I myself generate in controllers, which I don't want to happen.
My question is:
Is there any convinient way to get rid of serialized HttpError from response content? All I want user to get back for his bad requests is response code.
What about using a custom IHttpActionInvoker ?
Basically, you just have to send an empty HttpResponseMessage.
Here is a very basic example :
public class MyApiControllerActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
var result = base.InvokeActionAsync(actionContext, cancellationToken);
if (result.Exception != null)
{
//Log critical error
Debug.WriteLine("unhandled Exception ");
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError));
}
else if (result.Result.StatusCode!= HttpStatusCode.OK)
{
//Log critical error
Debug.WriteLine("invalid response status");
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(result.Result.StatusCode));
}
return result;
}
}
In Global.asax
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionInvoker), new MyApiControllerActionInvoker());
One other important thing you could do, not related to Web Api, is to remove excessive asp.net & IIS HTTP headers. Here is a good explanation.
I believe your approach of using the message handler is correct because regardless of the component in the Web API pipeline that sets the status code to 4xx, message handler can clear out response body. However, you do want to differentiate between the ones you explicitly set versus the ones set by the other components. Here is my suggestion and I admit it is a bit hacky. If you don't get any other better solution, give this a try.
In your ApiController classes, when you throw a HttpResponseException, set a flag in request properties, like so.
Request.Properties["myexception"] = true;
throw new HttpResponseException(...);
In the message handler, check for the property and do not clear the response body, if the property is set.
var response = await base.SendAsync(request, cancellationToken);
if((int)response.StatusCode > 399 && !request.Properties.Any(p => p.Key == "myException"))
response.Content = null;
return response;
You can package this a bit nicely by adding an extension method to HttpRequestMessage so that neither the ApiController nor the message handler knows anything about the hard-coded string "myException" that I use above.

How to redirect to login page from a web service in an AJAX call?

I am using jQuery to call web service (*.asmx) methods. The web service uses FormsAuthentication to determine whether the calling user is authenticated. I am unable to return a redirect status code from the web method, e.g.
[WebMethod(EnableSession=true)]
public Dictionary<string, object> GetArchivedFiles(int pageSize, int page)
{
if(HttpContext.Current.User.Identity.IsAuthenticated && Session["UserId"] != null)
// Do some computing and return a Dictionary.
// Method will fall through here if the user is not authenticated.
HttpContext.Current.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
return null;
}
The redirect does not work, I always get 500 Internal Server Error when doing this. I tried different codes. What would be the recommended way to go here? I need to read the redirect information from JavaScript and load the new page (or maybe display a login control AJAX way).
I actually get a JSON object back, which looks like this:
{"Message":"Authentication failed.","StackTrace":null,"ExceptionType":"System.InvalidOperationException"
}
I tried running the debugger, but it doesn't show that any methods are entered. As you can see the StackTrace is null...
When invoked in Fiddler as a standard POST request (not XMLHttpRequest) it returns an exception actually:
HTTP/1.1 500 Internal Server Error
Server: ASP.NET Development Server/9.0.0.0
Date: Wed, 04 Mar 2009 14:46:02 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Length: 1739
Connection: Close
System.NotSupportedException: The type System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] is not supported because it implements IDictionary.
at System.Xml.Serialization.TypeScope.GetDefaultIndexer(Type type, String memberInfo)
at System.Xml.Serialization.TypeScope.ImportTypeDesc(Type type, MemberInfo memberInfo, Boolean directReference)
at System.Xml.Serialization.TypeScope.GetTypeDesc(Type type, MemberInfo source, Boolean directReference, Boolean throwOnError)
at System.Xml.Serialization.ModelScope.GetTypeModel(Type type, Boolean directReference)
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root, String defaultNamespace)
at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type, XmlRootAttribute root)
at System.Web.Services.Protocols.XmlReturn.GetInitializers(LogicalMethodInfo[] methodInfos)
at System.Web.Services.Protocols.XmlReturnWriter.GetInitializers(LogicalMethodInfo[] methodInfos)
at System.Web.Services.Protocols.MimeFormatter.GetInitializers(Type type, LogicalMethodInfo[] methodInfos)
at System.Web.Services.Protocols.HttpServerType..ctor(Type type)
at System.Web.Services.Protocols.HttpServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)
You should not redirect from the Web service. It should return a value to indicate whether or not you should redirect (This could be a separate Authenticate method that returns a string, if it's null, it means it's authenticated, if it isn't it'll contain the redirection URL) Then in javascript, you could check the return value and redirect appropriately by setting the window.location property.
By the way, access to the Web service should not require authentication.
I don't have experience specifically with asp.net but in general I like to use an ajaxResponse object that has properties such as "success", "content", "errortype". A login required response in this case would have a success of "false" and errortype "login", or whatever you choose. The javascript code then decides how to handle the returned object based on these properties, showing the user a login form or redirecting to a new page.
What I did was to get rid of the status code at all and just return null. A method returns an empty list if there are no files, with a total page count of 0. But if the user is not authenticated, it returns null, in such case I use window.location to redirect the browser to the login page.
Throw an exception in the webservice. Catch this in the caller and redirect as necessary.
In fact, if your post to the webservices (ajax or not) contains a "Content-Type" header with value "application/json; charset=utf-8", then the ASMX stack will send you back a json formated string with an exception of the supposed redirect that usually you expect including status code 401 (unauthorized), but, if you don't send any content type header, you well get a browser redirection to the login page as response with status code OK and using ajax, you will get the login page html as response.
Hope that this help on clarifying.
Regards

Resources