RestSharp async post throws unknown exception - how to get to the underlying issue? - restsharp

I have an API that accepts JSON messages, which works perfectly fine when testing on localhost.
But on the production server it does not, I get this error message:
RestSharp.RestClient Extensions.d__16`1.MoveNext() --- End of stacktracing from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
The production server has an IP filter, so only certain IP addresses can access it, but I have tested with Postman from the same server below code runs on and in Postman it works without any issue.
This is the code that calls the API from the server that has access:
public async Task<string> SubmitOrder(Order order)
{
try
{
var request = new RestRequest("SaveWebsiteOrder", Method.Post)
.AddJsonBody(order)
.AddHeader("Authorization", ConfigReadSystem.ApiPassword);
return await _RestClient.PostAsync<string>(request);
}
catch(Exception ex)
{
return $"{ex.Message}. {ex.StackTrace} {ex.InnerException?.StackTrace}";
}
}
So catching the exception does not provide a lot of details, how can I get more information to investigate the actual issue?

Found out it was related to SSL, certificate seems to be invalid.
Could solve it via this post

Related

Centralized Exception Handling not working

I'm testing centralized exception handling in my ASPNetCore application and wanted to see if an unexpected exception is thrown it is going to be correctly handled by app.UseExceptionHandler() middleware and logged to a specific logging target. I disconnected the SQL database expecting to see Database.EnsureCreated() method in the DbContext class constructor throwing System.Data.SqlClient.SqlException. The problem is that it does throw such an exception, but it appears locally instead of being handled by centrelized error handler. The final result is that a client never gets a response message explaining what happened with the status code 500.
It seems to be strange as the handler works correctly with an exception which I throw inside controllers.
Here is my centralized exception handler configuration:
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
var errorFeature = context.Features.Get<IExceptionHandlerFeature>();
if (errorFeature != null)
{
var exception = errorFeature.Error;
logger.LogError(exception.ToString());
await context.Response.WriteAsync("An unexpected error occurred! Try again later");
}
});
});
Could anyone tell me give me a hint on what I might be doing wrong? Has anyone come across a similar problem?
Your code looks fine to me. What you are experiencing is a First-Chance-Exception. This means that an exception has been thrown that might eventually get handled. During runtime your ExceptionHandler should perfectly handle your exception.
At debugging time however, Visual Studio breaks for that exception. The behaviour of Visual Studio can be configured in the Exception Settings (Debug > Windows > Exception Settings). See Microsoft Docs for more information on that.
So what you basically have to do is to tell Visual Studio to continue debugging on a SqlException
You can use own middleware to handle exception as first-person
public class ExceptionFilter: IExceptionFilter
{
public void OnException(ExceptionContext context)
{
String message = String.Empty;
Type exceptionType = context.Exception.GetType();
if (exceptionType == typeof(NotImplementedException))
{
message = "A server error occurred.";
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.NotImplemented;
context.Result = new RedirectResult("/Home/Index");
}
else if (exceptionType == typeof(AppException))
{
message = context.Exception.ToString();
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Result = new RedirectResult("/Home/Index");
}
//HttpResponse response = context.HttpContext.Response;
//response.StatusCode = (int)status;
//context.Result = new RedirectResult("/Home/Index");
}
}
And in your Startup.cs
app.UseMiddleware(typeof(ExceptionFilter));

Invalid Token when confirming email address - Asp.Net Core

I'm occasionally getting an "Invalid Token" error from my call to
userManager.ConfirmEmailAsync(user, token) . I've narrowed down the problem to the fact that my 2 web servers are sitting behind a load balancer, and the web server that generated the token isn't always the web server that is attempting to confirm the token. I had a similar problem with anti-forgery tokens in a different web site, which I fixed by persisting the data protection key to disk and sharing it between the web servers, so I tried a similar approach here.
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(#"c:\temp\API"));
I then copied the key to the same folder in my other web server but still wasn't successful. Debugging through the AspNetCore.Identity code I can see an exception thrown on the call to
var unprotectedData = Protector.Unprotect(Convert.FromBase64String(token))
in the DataProtectorTokenProvider class. the catch block for the exception in Microsoft's code is simply
catch
{
// Do not leak exception
}
so I decided to inject an IDataProtector into my own Controller and try making that call myself.
public UserController(Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager, Microsoft.AspNetCore.Identity.RoleManager<IdentityRole> roleManager,
ILoggerFactory loggerFactory,
IDataProtectionProvider dataProtectionProvider)
{
Protector = dataProtectionProvider.CreateProtector("DataProtectorTokenProvider");
}
try
{
var unconverted = Convert.FromBase64String(request.EmailConfirmationToken);
var unprotectedData = Protector.Unprotect(unconverted);
}
catch (Exception e)
{
}
I can now catch the exception thrown on the Unprotect call and it's:
The payload was invalid
Microsoft.AspNetCore.DataProtection.Cng.CbcAuthenticatedEncryptor.DecryptImpl(Byte* pbCiphertext, UInt32 cbCiphertext, Byte* pbAdditionalAuthenticatedData, UInt32 cbAdditionalAuthenticatedData)\r\n at Microsoft.AspNetCore.DataProtection.Cng.Internal.CngAuthenticatedEncryptorBase.Decrypt(ArraySegment1 ciphertext, ArraySegment1 additionalAuthenticatedData)\r\n at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, Boolean allowOperationsOnRevokedKeys, UnprotectStatus& status)\r\n at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.DangerousUnprotect(Byte[] protectedData, Boolean ignoreRevocationErrors, Boolean& requiresMigration, Boolean& wasRevoked)\r\n at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData)\r\n at VTR.API.Controllers.UserController.d__16.MoveNext() in C:\Projects\Brewster.Travel\src\cres\trunk\VTR.API\src\VTR.API\Controllers\UserController.cs:line 409
If I make that call with a token generated on the same server then it gets unprotected successfully. I obviously have some problem with how I'm attempting to share my data protection keys, if anyone could shed some light on my problem I would appreciate it.
I managed to get this working thanks to the documentation here: https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview
I needed to add a call to SetApplicationName() in ConfigureServices:
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(#"c:\someDirectory"))
.SetApplicationName("myApplicationName");

Exception thrown when WebAuthenticationBroker receives an OAuth2 callback

The WebAuthenticationBroker doesn't seem to be able to handle navigation to my ms-app://. Just throws this ugly error as you will see below.
Steps
Call AuthenticateAsync(), including callback uri obtained at runtime: WebAuthenticationBroker.GetCurrentApplicationCallbackUri()
Go through authorize process, hit Allow.
Instead of returning, the broker shows the page Can't connect to service. We can't connect to the service you need right now. Unable to do anything, so I hit the Back button visible.
Debugger breaks on catch: "The specified protocol is unknown. (Exception from HRESULT: 0x800C000D)"
The callback for WebAuthenticationBroker.AuthenticateAsync() is received (according to Fiddler4 & the Event Viewer) but it throws the aforementioned exception as if it doesn't know how to interpret the ms-app:// protocol.
All examples imply my code should work but I think there's something less obvious causing an issue.
Code
private static string authorizeString =
"https://api.imgur.com/oauth2/authorize?client_id=---------&response_type=token";
private Uri startUri = new Uri(authorizeString);
public async void RequestToken() {
try {
var war = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.UseTitle
, startUri);
// Imgur knows my redirect URI, so I am not passing it through here
if (war.ResponseStatus == WebAuthenticationStatus.Success) {
var token = war.ResponseData;
}
} catch (Exception e) { throw e; }
}
Event Viewer log excerpts (chronological order)
For information on how I obtained this, read the following MSDN: Web authentication problems (Windows). Unfortunately this is the only search result when querying authhost.exe navigation error.
Information: AuthHost redirected to URL: <ms-app://s-1-15-2-504558873-2277781482-774653033-676865894-877042302-1411577334-1137525427/#access_token=------&expires_in=3600&token_type=bearer&refresh_token=------&account_username=------> from URL: <https://api.imgur.com/oauth2/authorize?client_id=------&response_type=token> with HttpStatusCode: 302.
Error: AuthHost encountered a navigation error at URL: <https://api.imgur.com/oauth2/authorize?client_id=------&response_type=token> with StatusCode: 0x800C000D.
Information: AuthHost encountered Meta Tag: mswebdialog-title with content: <Can't connect to the service>.
Thanks for reading, Stack. Don't fail me now!
Afaik, you need to pass the end URL to AuthenticateAsync even if you assume that the remote service knows it.
The way WebAuthenticationBroker works is like the following: you specify an "endpoint" URL and when it encounters a link that starts with this URL, it will consider the authentication process complete and doesn't even try navigating to this URL anymore.
So if you specify "foo://bar" as callback URI, navigating to "foo://bar" will finish the authentication, as will "foo://barbaz", but not "foo://baz".
Resolved! #ma_il helped me understand how the broker actually evaluates the redirect callback and it led me back to square one where I realized I assumed WebAuthenticationOptions.UseTitle was the proper usage. Not so. Up against Imgur's API using a token, it requires WebAuthenticationOptions.None and it worked immediately.
As an example to future answer-seekers, here's my code.
private const string clientId = "---------";
private static Uri endUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
private static string authorizeString = "https://api.imgur.com/oauth2/authorize?"
+ "client_id="
+ clientId
+ "&response_type=token"
+ "&state=somestateyouwant"
+ "&redirect_uri="
+ endUri;
private Uri startUri = new Uri(authorizeString);
public async void RequestToken() {
try {
WebAuthenticationResult webAuthenticationResult =
await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None
, startUri
, endUri);
if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success) {
string token = webAuthenticationResult.ResponseData;
// now you have the token
}
} catch { throw; }
}

Silverlight fault propagation and UserNamePasswordValidator

Scenario is a Silverlight client using Wcf service & custom authentication. To mitigate the 500/200 status code problem (avoid EndPointNotFound exception) I've applied the SilverLightFaultBehaviour. However, this does not work with UserNamePasswordValidator - When a FaultException is thrown from Validate(), it is not caught by the SilverLightFaultMessageInspector's implementation of BeforeSendReply.
So far, the only workaround I've found is using the alternative client stack instead ( WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);), but there are complications with using it which can no longer be ignored as a lot of our clients are on virtual machines, the silverlight client keeps crashing ( Silverlight 5 - Debugging npctrl.dll crash , http://communities.vmware.com/thread/394306?tstart=0 ).
My primary motivation is that I want to be able to distinguish a failed login from a connection error (the following code is from a client-side async callback method, and only works with the Client stack):
if (e.Error is MessageSecurityException)
{
this.HasLoginFailed.Value = Captions.Login_FailedLogin;
}
else
{
this.HasLoginFailed.Value = Captions.Login_FailedConnection;
}
Is there any other way of modifying the message sent when throwing a FaultException from UserNamePasswordValidator? Or any conceptually different way of doing custom authentication rather than what I am using which enables me to modify the message status or to keep it 200, or just to be able to distinguish a connection failure from bad credentials?
my server-side code for usernamepassword reg:
var serviceCredential = host.Description.Behaviors.Find<ServiceCredentials>();
serviceCredential.UserNameAuthentication.UserNamePasswordValidationMode =
UserNamePasswordValidationMode.Custom;
serviceCredential.UserNameAuthentication.CustomUserNamePasswordValidator =
new MyValidator();
When you throw a FaultException from MyValidator, it is wrapped as the InnerException of a MessageSecurityException, that's probably why you weren't able to catch it directly as a FaultException.
To add some information to the fault you are throwing, what you can do is adding a FaultCode:
throw new FaultException(
"Invalid user name or bad password.",
new FaultCode("BadUserNameOrPassword")
);
Then, catch the exception client-side and retrieve your FaultCode:
try { ... }
catch (MessageSecurityException e)
{
FaultException fault = (FaultException) e.InnerException;
String faultCode = fault.Code.Name;
// you can now display a meaningful error with the faultCode
}
I hope it will help!

how do i get an exception out of a web service?

I have a web service that runs perfectly when i reference it from within the project solution. As soon as i upload it to the remote server, it starts blowing up. Unfortunately, the only error message I get is on the client side "faultexception was unhandled by user code". Inside of the web service, I have exceptions handled in all of the methods, so I'm pretty sure it's getting caught somewhere, but I don't know how to see it. I suspect that the problem is permissions related, but I can't see where it's happening.
I tried placing an error message into object returns, but it's still not making it out; something like this:
public bool SetDirectReports(ADUser user)
{
try
{
var adEntry = new DirectoryEntry(string.Format("LDAP://<GUID={0}>", user.Guid), "administrator", "S3cur1ty");
if (adEntry.Properties["directReports"].Count > 0)
{
user.DirectReports = new List<ADUser>();
foreach (string directReport in adEntry.Properties["directReports"]) //is being returned as full distinguished name
{
var dr = new DirectoryEntry(string.Format("LDAP://{0}", directReport), "administrator", "S3cur1ty");
user.DirectReports.Add(GetUserByGuid(dr.NativeGuid));
}
return true;
}
else
{
user.DirectReports = new List<ADUser>();
return false;
}
}
catch (Exception ex)
{
user.HasError = true;
user.ErrorMessage = "Error setting direct reports: " + ex.Message;
return false;
}
}
but its' still not catching. I was hoping for a better approach. I'm not sure if I could add something that would output the exception to the console or what. Any help would be appreciated. TIA
P.S. this isn't necessarily the method thats crashing, there's a web of them in the service.
You should dump all of your exceptions to a log file on the server side; exposing error information to the client is a potential security risk, which is why it's turned off by default.
If you really want to send exception information to the client, you can turn it on. If you are using a WCF service you should set the "includeExceptionDetailsInFaults" property on for the service behavior, as described in this MSDN article on dealing with unhandled exceptions in WCF. Once you do so, you will have a property on the FaultException called Detail that should itself be a type of Exception.
For better error handling you should also take a look at typed faults using the FaultContract and FaultException<> class; these have the benefit that they don't throw the channel into a faulted state and can be handled correctly:
try
{
// do stuff here
}
catch (Exception ex)
{
var detail = new CustomFaultDetail
{
Message = "Error setting direct reports: " + ex.Message
};
throw new FaultException<CustomFaultDetail>(detail);
}
If you are using an ASP.NET Web Service, you should set the customErrors mode to "Off" in your web.config. This will send back the entire exception detail as HTML, which the client should receive as part of the SOAP exception that it receives.
The error your are seeing ("faultexception was unhandled by user code") is happening because this is a remote exception and it is standard behavior to only display exceptions on the local computer by default. In order to make it work how you intend, you need to change the customErrors section of the web.config and set it to Off
UPDATE: I found a related question: c# exception not captured correctly by jquery ajax
(Three years later..)
Here's the solution I came up with, along with some sample WCF code, and Angular code to catch, and display the exception message:
Catching exceptions from WPF web services
Basically, you just need to wrap your WCF service in a try..catch, and when something goes wrong, set a OutgoingWebResponseContext value.
For example, in this web service, I've slipped in an Exception, which will make my catch code set the OutgoingWebResponseContext value.
It looks odd... as I then return null, but this works fine.
public List<string> GetAllCustomerNames()
{
// Get a list of unique Customer names.
//
try
{
throw new Exception("Oh heck, something went wrong !");
NorthwindDataContext dc = new NorthwindDataContext();
var results = (from cust in dc.Customers select cust.CompanyName).Distinct().OrderBy(s => s).ToList();
return results;
}
catch (Exception ex)
{
OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
response.StatusCode = System.Net.HttpStatusCode.Forbidden;
response.StatusDescription = ex.Message.Replace("\r\n", "");
return null;
}
}
What is brilliant about this try..catch is that, with minimal changes to your code, it'll add the error text to the HTTP Status, as you can see here in Google Chrome:
If you didn't have this try..catch code, you'd just get an HTTP Status Error of 400, which means "Bad Request".
So now, with our try..catch in place, I can call my web service from my Angular controller, and look out for such error messages coming back.
$http.get('http://localhost:15021/Service1.svc/getAllCustomerNames')
.then(function (data) {
// We successfully loaded the list of Customer names.
$scope.ListOfCustomerNames = data.GetAllCustomerNamesResult;
}, function (errorResponse) {
// The WCF Web Service returned an error
var HTTPErrorNumber = errorResponse.status;
var HTTPErrorStatusText = errorResponse.statusText;
alert("An error occurred whilst fetching Customer Names\r\nHTTP status code: " + HTTPErrorNumber + "\r\nError: " + HTTPErrorStatusText);
});
Cool, hey ?
Incredibly simple, generic, and easy to add to your services.
Shame some readers thought it was worth voting down. Sorry about that.
You have several options:
1) If you are using WCF, throw a FaultException on the server and catch it on the client. You could, for instance, implement a FaultContract on your service, and wrap the exception in a FaultException. Some guidance to this here.
2) You could use the Windows Server AppFabric which would give you more details to the exception within IIS. (requires some fiddling to get it working, though)
3) Why not implement some sort of server-side logging for the exceptions? Even if to a file, it would be invaluable to you to decipher what is really happening. It is not a good practice (especially for security reasons) to rely on the client to convey the inner workings of the server.