How to customize error message of OAuthAuthorizationServerProvider? - c#

We are using the OAuthAuthorizationServerProvider class to do authorization in our ASP.NET Web Api app.
If the provided username and password is invalid in GrantResourceOwnerCredentials, the call
context.SetError( "invalid_grant", "The user name or password is incorrect." );
Produces the following Json result:
{
"error": "invalid_grant",
"error_description": "The user name or password is incorrect."
}
Is there any way to customize this error result?
I would like to make it consistent with default error message format used in other parts of the API:
{
"message": "Some error occurred."
}
Is this possible to achieve with the OAuthAuthorizationServerProvider?

This is how I did it.
string jsonString = "{\"message\": \"Some error occurred.\"}";
// This is just a work around to overcome an unknown internal bug.
// In future releases of Owin, you may remove this.
context.SetError(new string(' ',jsonString.Length-12));
context.Response.StatusCode = 400;
context.Response.Write(jsonString);

+1 for Dasun's answer. Here is how I extended it a bit further.
public class ErrorMessage
{
public ErrorMessage(string message)
{
Message = message;
}
public string Message { get; private set; }
}
public static class ContextHelper
{
public static void SetCustomError(this OAuthGrantResourceOwnerCredentialsContext context, string errorMessage)
{
var json = new ErrorMessage(errorMessage).ToJsonString();
context.SetError(json);
context.Response.Write(json);
}
}
The .ToJsonString() is another extension method that uses the Newtonsoft.Json library.
public static string ToJsonString(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
Usage:
context.SetCustomError("something went wrong");

1+ again for "user2325333" and "Dasun's" answer his solution, your answers are good but still there is an issue .
The Josn Tag still return {error:""}, thus I replace the context.Response.Body with empty MemoryStream
and here the work example
public static class ContextHelper
{
public static void SetCustomError(this OAuthGrantResourceOwnerCredentialsContext context,string error, string errorMessage)
{
var json = new ResponseMessage
{ Data = errorMessage, Message = error, IsError = true }.ToJsonString();
context.SetError(json);
context.Response.Write(json);
Invoke(context);
}
public static string ToJsonString(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
static async Task Invoke(OAuthGrantResourceOwnerCredentialsContext context)
{
var owinResponseStream = new MemoryStream();
var customResponseBody = new System.Net.Http.StringContent(JsonConvert.SerializeObject(new ResponseMessage()));
var customResponseStream = await customResponseBody.ReadAsStreamAsync();
await customResponseStream.CopyToAsync(owinResponseStream);
context.Response.ContentType = "application/json";
context.Response.ContentLength = customResponseStream.Length;
context.Response.Body = owinResponseStream;
}
}
public class ResponseMessage
{
public bool IsError { get; set; }
public string Data { get; set; }
public string Message { get; set; }
}
for usage of this context
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (!context.Match.Passcode)
{
context.SetCustomError("invalid_grant", "Passcode is invalid.");
return;
}
}
The Result will be as

Related

SignalR hub not answering to object parameter

I cannot find any resource, that would say I cannot do that.
I have all setup hub/client and tested when parameter is decimal, but once I use generic class, then the server wont react.
SignalRMessage:
public class SignalRMessage<T>
{
public SignalRMessage(T value, string text)
{
Value = value;
Text = text ?? string.Empty;
}
public T Value { get; set; }
public string Text { get; set; }
}
Hub (OnConnected gets a hit):
public class JobHeaderHub : Hub
{
public override Task OnConnectedAsync()
{
Debug.WriteLine(Clients.Caller);
return base.OnConnectedAsync();
}
public async Task JobHeaderUpdated(SignalRMessage<decimal> message)
{
await Clients.Others.SendAsync("ReceiveJobHeaderUpdated", message);
}
public async Task JobHeaderCreated(SignalRMessage<decimal> message)
{
await Clients.Others.SendAsync("ReceiveJobHeaderCreated", message);
}
}
Client:
public class JobHeaderSingalRClient
{
private HubConnection connection;
public JobHeaderSingalRClient()
{
// connection = new HubConnectionBuilder().WithUrl(#"").WithAutomaticReconnect().Build();
connection = new HubConnectionBuilder().WithUrl(#"http://localhost:5000/jobheader").WithAutomaticReconnect().Build();
connection.On<SignalRMessage<decimal>>("ReceiveJobHeaderUpdated", message => JobHeaderUpdated?.Invoke(message));
connection.On<SignalRMessage<decimal>>("ReceiveJobHeaderCreated", message => JobHeaderCreated?.Invoke(message));
}
public static async Task<JobHeaderSingalRClient> CreateConnectedClient()
{
var client = new JobHeaderSingalRClient();
await client.ConnectAsync();
return client;
}
public async Task<JobHeaderSingalRClient> ConnectAsync()
{
await connection.StartAsync();
return this;
}
public event Action<SignalRMessage<decimal>> JobHeaderUpdated;
public async Task SendJobHeaderUpdated(decimal id, string message = null)
{
await connection.SendAsync("JobHeaderUpdated", new SignalRMessage<decimal>(id, message));
}
public event Action<SignalRMessage<decimal>> JobHeaderCreated;
public async Task SendJobHeaderCreated(decimal id, string message = null)
{
await connection.SendAsync("JobHeaderCreated", new SignalRMessage<decimal>(id, message));
}
}
I have no idea why when parameter is SignalRMessage<decimal> then the methods on server are not getting hit. Anyone knows? Thanks.
I had this sort of issues too when I was using constructors with parameters. All of them disappeared after adding a default parameterless constructor.
This is most probably not related to signalR, but to the underlying JSON serialization.
The type has to be specified in order to be able to serialize the objects.
I had similar issues when using objects of type object as parameters.
To troubleshoot turn on verbose error messages in signalR and see if there are any errors logged.
services.AddSignalR(options =>
{
options.Hubs.EnableDetailedErrors = true;
});

NancyFx: how to pass an error message to a custom error page?

We've implemented static custom error pages for 404/500 HTTP status codes using Razor and the IStatusCodeHandler implementation documented here:
https://blog.tommyparnell.com/custom-error-pages-in-nancy/
That works, and now we've been trying to pass an error message to the Razor template. The RenderView method runs without exception, however the output is totally blank.
In the Handle method, we construct an ErrorModel object and set its ErrorMessage property, then pass the object to RenderView:
var response = viewRenderer.RenderView(context, "views/Errors/" + (int)statusCode + ".cshtml", errorModel);
The CustomStatusCode.cs code is:
public class CustomStatusCode : IStatusCodeHandler
{
private readonly ILog _log = LogManager.GetLogger<CustomStatusCode>();
private static IEnumerable<int> _checks = new List<int>();
public static IEnumerable<int> Checks { get { return _checks; } }
private IViewRenderer viewRenderer;
public CustomStatusCode(IViewRenderer viewRenderer)
{
this.viewRenderer = viewRenderer;
}
public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context)
{
return (_checks.Any(x => x == (int)statusCode));
}
public static void AddCode(int code)
{
AddCode(new List<int>() { code });
}
public static void AddCode(IEnumerable<int> code)
{
_checks = _checks.Union(code);
}
public static void RemoveCode(int code)
{
RemoveCode(new List<int>() { code });
}
public static void RemoveCode(IEnumerable<int> code)
{
_checks = _checks.Except(code);
}
public static void Disable()
{
_checks = new List<int>();
}
public void Handle(HttpStatusCode statusCode, NancyContext context)
{
var errorMessage = string.Empty;
var exception = context.GetException();
if (exception != null)
{
errorMessage = exception.Message;
}
var errorModel = new ErrorModel(errorMessage);
try
{
var response = viewRenderer.RenderView(context, "views/Errors/" + (int)statusCode + ".cshtml", errorModel);
response.StatusCode = HttpStatusCode.OK;
context.Response = response;
}
catch (Exception ex)
{
_log.Debug($"CustomStatusCodes caught exception: {ex.Message}");
RemoveCode((int)statusCode);
context.Response.StatusCode = statusCode;
}
}
}
The ErrorModel.cs class is:
public class ErrorModel
{
public ErrorModel(string message)
{
ErrorMessage = message;
}
string ErrorMessage { get; set; }
}
One of the Razor templates:
#using WebService.Models
#Model ErrorModel
<html>
<body>
<b>500 Internal server error.</b>
<p>Error message was: #Model.ErrorMessage</p>
</body>
</html>
We've tried setting response.StatusCode to the original statusCode parameter value, as well as HttpStatusCode.OK, both result in a blank page.
Any suggestions as to what we're missing?

ServiceStack returning typed response in XML format

So I have modified the todo service that gives me a response.
When I deliberately throw an unhandled error to test ServiceExceptionHandler code below, if I use the default /Backbone.Todos/todo/1?format=json, it is fine.
But if I use /Backbone.Todos/todo/1?format=xml, it says:
XML Parsing Error: not well-formed Location:
/Backbone.Todos/todo/1 Line Number 1, Column 2:
Here is my code:
public class AppHost : AppHostBase {
public override void Configure(Funq.Container container) {
//Unhandled errors
this.ServiceExceptionHandler = (req, ex) => {
var res = new { //i return a dynamic object here ... and it doesn't work
Result = null as object,
ResponseStatus = new ResponseStatus() {
ErrorCode = "Error",
Message = "Not available",
StackTrace = "Not available"
}
};
return res;
};
//...
}
//....
}
//My normal request/response dtos look like this:
//-----------------------------------------
[Route("/todo/{id}", "GET")] //display req
public class GetTodo : IReturn<TodoResponse> {
public long Id { get; set; }
}
public class TodoResponse : IHasResponseStatus {
public IList<Todo> Result { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
The thing is, when error occured I can't construct a correct type of response without knowing what type it is. I only get incoming Request and Exception as paramaters, but not Response type.
I suspect the error is because XML cannot automatically serialize dynamic objects in .net.
I not entirely sure I know what you are trying to do but you could try using the built in utilities to handle the exception then modify it as you want.
this.ServiceExceptionHandler = (req, ex) =>
{
var responseStatus = ex.ToResponseStatus();
var errorResponse = ServiceStack.ServiceHost.DtoUtils.CreateErrorResponse(req, ex, responseStatus);
return errorResponse;
};

Amazon Product Advertising API ItemSearch Returning Null

I have spent entire day trying to fix problems with the Amazon ECS(Ecommerce Services) API.
I have downloaded the example on their site for sending SOAP Requests with .NET 4.0 and WCF
http://aws.amazon.com/code/Product-Advertising-API/3941
I have not changed anything in the code from the example, except for the AccessKeyID and SecretyKeyID in the config file.
The Calling code looks like the following:
// Instantiate Amazon ProductAdvertisingAPI client
AWSECommerceServicePortTypeClient amazonClient = new AWSECommerceServicePortTypeClient();
// prepare an ItemSearch request
ItemSearchRequest request = new ItemSearchRequest();
request.SearchIndex = "Books";
request.Title = "WCF";
request.ResponseGroup = new string[] { "Medium"};
ItemSearch itemSearch = new ItemSearch();
itemSearch.Request = new ItemSearchRequest[] { request };
request.Condition = Condition.All;
itemSearch.AssociateTag = "";
itemSearch.AWSAccessKeyId = ConfigurationManager.AppSettings["accessKeyId"];
// send the ItemSearch request
ItemSearchResponse response = amazonClient.ItemSearch(itemSearch);
if (response != null)
{
// write out the results from the ItemSearch request
foreach (var item in response.Items[0].Item)
{
Console.WriteLine(item.ItemAttributes.Title);
}
}
Console.WriteLine("done...enter any key to continue>");
Console.ReadLine();
the Call to the ItemSearch() is returning a null object. I looked further into this and saw that in the AmazongSigningMessageInspector Class, the AfterReceiveReply() method is showing that a proper SOAP XML response is being returned with results, so I know that it is making the call to the service and returning correctly. For some reason though I am left with a NULL ItemSearch object.
The code for my classes are the following:
class AmazonSigningBehaviorExtensionElement : BehaviorExtensionElement
{
public AmazonSigningBehaviorExtensionElement()
{
}
public override Type BehaviorType
{
get
{
return typeof(AmazonSigningEndpointBehavior);
}
}
protected override object CreateBehavior()
{
return new AmazonSigningEndpointBehavior(AccessKeyId, SecretKey);
}
[ConfigurationProperty("accessKeyId", IsRequired = true)]
public string AccessKeyId
{
get { return (string)base["accessKeyId"]; }
set { base["accessKeyId"] = value; }
}
[ConfigurationProperty("secretKey", IsRequired = true)]
public string SecretKey
{
get { return (string)base["secretKey"]; }
set { base["secretKey"] = value; }
}
}
public class AmazonSigningEndpointBehavior : IEndpointBehavior {
private string _accessKeyId = "";
private string _secretKey = "";
public AmazonSigningEndpointBehavior()
{
this._accessKeyId = ConfigurationManager.AppSettings["accessKeyId"];
this._secretKey = ConfigurationManager.AppSettings["secretKey"];
}
public AmazonSigningEndpointBehavior(string accessKeyId, string secretKey) {
this._accessKeyId = accessKeyId;
this._secretKey = secretKey;
}
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime) {
clientRuntime.MessageInspectors.Add(new AmazonSigningMessageInspector(_accessKeyId, _secretKey));
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { return; }
public void Validate(ServiceEndpoint serviceEndpoint) { return; }
public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters) { return; }
}
public class AmazonSigningMessageInspector : IClientMessageInspector {
private string _accessKeyId = "";
private string _secretKey = "";
public AmazonSigningMessageInspector(string accessKeyId, string secretKey) {
this._accessKeyId = accessKeyId;
this._secretKey = secretKey;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel) {
// prepare the data to sign
string operation = Regex.Match(request.Headers.Action, "[^/]+$").ToString();
DateTime now = DateTime.UtcNow;
string timestamp = now.ToString("yyyy-MM-ddTHH:mm:ssZ");
string signMe = operation + timestamp;
byte[] bytesToSign = Encoding.UTF8.GetBytes(signMe);
// sign the data
byte[] secretKeyBytes = Encoding.UTF8.GetBytes(_secretKey);
HMAC hmacSha256 = new HMACSHA256(secretKeyBytes);
byte[] hashBytes = hmacSha256.ComputeHash(bytesToSign);
string signature = Convert.ToBase64String(hashBytes);
// add the signature information to the request headers
request.Headers.Add(new AmazonHeader("AWSAccessKeyId", _accessKeyId));
request.Headers.Add(new AmazonHeader("Timestamp", timestamp));
request.Headers.Add(new AmazonHeader("Signature", signature));
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
}
I have seen this problem everywhere, but nobody has posted a fix for it anywhere. Somebody please help me with this.
My issue was that I was missing the associate tag.
itemSearch.AssociateTag = "213";
There are definitely problems with the generated code, ItemSearchResponse contains an error collection which is not exposed by code. It was through looking at the raw message in the inspector that pointed me in the right direction.
The answer regarding missing associate tag help me, but I also had to ensure the WSDL URL and endpoint address match the Amazon site you've registered under. I registered under the UK site so I need to use.
WSDL
Endpoint Address
A current project to this assignment you can found here on Nager.AmazonProductAdvertising github. The library is also available over nuget
nuget
PM> Install-Package Nager.AmazonProductAdvertising
Example
var authentication = new AmazonAuthentication();
authentication.AccessKey = "accesskey";
authentication.SecretKey = "secretkey";
var wrapper = new AmazonWrapper(authentication, AmazonEndpoint.US);
var result = wrapper.Search("canon eos", AmazonSearchIndex.Electronics, AmazonResponseGroup.Large);
I compiled and posted a fix for this sample. Please go here:
https://forums.aws.amazon.com/message.jspa?messageID=440527#440527

Rhino Mock Expect

Why is the response below always null in my test?
SSO.cs
public class SSO : ISSO
{
const string SSO_URL = "http://localhost";
const string SSO_PROFILE_URL = "http://localhost";
public AuthenticateResponse Authenticate(string userName, string password)
{
return GetResponse(SSO_URL);
}
public void GetProfile(string key)
{
throw new NotImplementedException();
}
public virtual AuthenticateResponse GetResponse(string url)
{
return new AuthenticateResponse();
}
}
public class AuthenticateResponse
{
public bool Expired { get; set; }
}
SSOTest.cs
[TestMethod()]
public void Authenticate_Expired_ReturnTrue()
{
var target = MockRepository.GenerateStub<SSO>();
AuthenticateResponse authResponse = new AuthenticateResponse() { Expired = true };
target.Expect(t => t.GetResponse("")).Return(authResponse);
target.Replay();
var response = target.Authenticate("mflynn", "password");
Assert.IsTrue(response.Expired);
}
Your expectation is not correct. You defined that you expect an empty string as parameter on GetResponse, but you pass in the value SSO_URL. So the expectation is not meet and null is returned instead.
You have two options to correct this
One way is to set IgnoreArguments() on the expectation
target.Expect(t => t.GetResponse("")).IgnoreArguments().Return(authResponse);
and the other way is to pass in your SSO_URL as parameter to the GetResponse method like this
target.Expect(t => t.GetResponse("http://localhost")).Return(authResponse);

Categories

Resources