Error message from DotNetOpenAuth's ReadAuthorizationRequest when passing in HttpRequestWrapper - iis

I have a custom authorization filter for my Authorize method in my OAuth controller. When the authorization filter notes the user is logged, it stuffs the current OAuth request into the session, and ships them off to log in.
After log in, in my /OAuth/Authorize endpoint, I check to see if that request is in the session, instead of immediately failing because there is not an authorization request attached to the current request. Then, I call the authorization server with that request object.
The code in my Authorize action looks like this:
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
[ExternalAppAuthorizeAttribute]
public ActionResult Authorize() {
Object requestObject = this.HttpContext.Session["AuthRequest"];
HttpRequestBase request = null;
if ((requestObject != null) && (requestObject.GetType() == typeof(HttpRequestWrapper)))
{
request = (HttpRequestWrapper)requestObject;
this.HttpContext.Session.Remove("AuthRequest");
}
EndUserAuthorizationRequest pendingRequest = null;
if (request != null)
{
pendingRequest = this.authorizationServer.ReadAuthorizationRequest(request);
} else
{
pendingRequest = this.authorizationServer.ReadAuthorizationRequest();
}
if (pendingRequest == null) {
throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request.");
}
ReadAuthorizationRequest fails, however, when the request was found in the session and restored. The error message is not very helpful: "Value does not fall within the expected range. "
Here is the stacktrace:
[ArgumentException: Value does not fall within the expected range.]
System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo) +0
System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode) +10
System.Web.Util.Misc.ThrowIfFailedHr(Int32 hresult) +9
System.Web.Hosting.IIS7WorkerRequest.GetServerVariableInternal(String name) +36
System.Web.Hosting.IIS7WorkerRequest.GetServerVariable(String name) +49
System.Web.HttpRequest.AddServerVariableToCollection(String name) +22
System.Web.HttpRequest.FillInServerVariablesCollection() +85
System.Web.HttpServerVarsCollection.Populate() +36
System.Web.HttpServerVarsCollection.Get(String name) +42
System.Collections.Specialized.NameValueCollection.get_Item(String name) +10
DotNetOpenAuth.Messaging.MessagingUtilities.GetPublicFacingUrl(HttpRequestBase request, NameValueCollection serverVariables) +61
DotNetOpenAuth.Messaging.MessagingUtilities.GetPublicFacingUrl(HttpRequestBase request) +43
DotNetOpenAuth.Messaging.Channel.ReadFromRequestCore(HttpRequestBase request) +69
I've inspected my request in flight and everything used by oauth in it looks totally fine: headers, URI, etc. I'm at a loss as to what might be causing this.
Does anyone know why this might be the case? Or, if you have alternative recommendations for storing the oauth request while the user is authenticating, I am open to them.

Turns out that ServerVariables on the HttpRequestWrapper was throwing an exception when called (which is now obvious from the stack trace). I believe this is because the request hadn't hit the controller action yet since it was intercepted by the filter. I guess that ServerVariables gets set when the request is processed by the controller action?
I resolved this by creating a new class that implements HttpRequestBase. I passed it my stored oauth request and the actual request, and returned the properties from the oauth request for everything in HttpRequestBase except ServerVariables, which I returned from the current request.

Related

Servicestack Exception Handling: Passing a Status That Came From Web

Let's say I have multiple services. One service is calling another service to get something. That service also gets some information from a third party vendor. Let's say the third party vendor returned a Too Many Requests message with 429 status code. The method that calls the third party vendor is used by lots of other methods and can be called directly via GET Request or within another Http Request.
When I detect the 429 status code, I was throwing a custom exception (let's say TooManyRequestsException) and using the Mapper to send the code to the requester.
Config.MapExceptionToStatusCode.Add(typeof(TooManyRequestsException),429);
It works if the method is called directly. But obviously I forgot the fact that this method was called by many other methods inside the service and all other methods are wrapping this exception with generic System.Exception objects with custom messages inside their catch blocks.
What other options do I have other than changing all the methods that wrap the exception with System.Exception? I tried to go over http://docs.servicestack.net/error-handling but couldn't really find a way that would help me, or couldn't make sense of it.
I recommend looking at Overriding OnExceptionTypeFilter in your AppHost which will let you apply custom logic to inspect the Exception and customize the ResponseStatus returned:
public override void OnExceptionTypeFilter(
Exception ex, ResponseStatus responseStatus)
{
var argEx = ex as ArgumentException;
var isValidationSummaryEx = argEx is ValidationException;
if (argEx != null && !isValidationSummaryEx && argEx.ParamName != null)
{
var paramMsgIndex = argEx.Message.LastIndexOf("Parameter name:");
var errorMsg = paramMsgIndex > 0
? argEx.Message.Substring(0, paramMsgIndex)
: argEx.Message;
responseStatus.Errors.Add(new ResponseError
{
ErrorCode = ex.GetType().Name,
FieldName = argEx.ParamName,
Message = errorMsg,
});
}
}

External Login WebAPI2 MVC5

I need to use a 3rd party token as a way to authenticate. I'm getting the token in the home controller and need to be able to send it on to my WebAPI controllers (It's an SPA application using Backbone). Is there a way of doing this?
EDIT:
Relevant code:
public ActionResult Index(string projectId, int companyId, bool isCompanyAdmin)
{
// if not a valid user return error
var validate = new Validate().ValidateContext(HttpContext,
"Key", "Secret");
if (!validate.IsValidated) return View(Constants.ValidationFailed);
// The info validated, so now I can set it to authorized
// put code here for doing it
//Get the model for the user
try
{
var model = ConvertToVM(_smsRepository.GetCompany(companyId, projectId));
}
catch (ProviderIncompatibleException)
{
// connection string wrong
return View(Constants.ConnectionFailed);
}
catch (Exception e)
{
// catch all
return View(Constants.DatabaseError);
}
//create and send through the view model that determines what view the user will get
return View(model);
}
Ok I put in the index method on the Home Controller. Like I said, we make a call to a third party API passing in the context, the client key, and the client secret to verify our identity. Could I just add a Bearer token in the home controller? Or otherwise pass the http context to OWiN and use some custom logic to add the token if validate.IsValidated is true? It needs to be something that works with WebAPI.

How can I use Scribe with JSF to connect to the LinkedIn API?

Based upon the Scribe example on github, I'm trying to authorize my application to use LinkedIn's api.
Here is my current code that is tied to a button click:
public String generateFromLinkedIn() {
OAuthService service = new ServiceBuilder()
.provider(LinkedInApi.class)
.apiKey(apiKey)
.apiSecret(apiSecret)
.callback("http://localhost:8080/Project/faces/linkedIn.xhtml").build();
ExternalContext externalContext = FacesContext.getCurrentInstance()
.getExternalContext();
Token requestToken = service.getRequestToken();
String authUrl = service.getAuthorizationUrl(requestToken);
try {
externalContext.redirect(authUrl);
} catch (IOException ex) {
Logger.getLogger(LinkedInController.class.getName()).log(Level.SEVERE, null, ex);
}
Map<String, String> parameterMap = (Map<String, String>) externalContext.getRequestParameterMap();
Verifier v = new Verifier(parameterMap.get("oauth_verifier"));
Token accessToken = service.getAccessToken(requestToken, v);
OAuthRequest request = new OAuthRequest(Verb.GET, "https://api.linkedin.com/v1/people/~");
service.signRequest(accessToken, request);
Response response = request.send();
System.err.println(response.getBody());
return "";
}
And in my .xhtml page I have:
<h:commandButton value="Generate" action="#{linkedInController.generateFromLinkedIn()}"></h:commandButton>
Everything works okay until I try to get the Verifier from the parameter map, which does not have any oauth_verifier. I tried splitting it up into multiple methods, but so far I cannot obtain the oauth_verifier from the URL parameters even though it is clearly there after returning from LinkedIn authorization dialog.
Any suggestions on how to get this verifier correctly or how to make Scribe work with JSF?
You seem somehow to expect that the redirected request magically returns to exactly same location in the code and continues from there. This is untrue. It are effectively 2 HTTP requests. You're basically still fiddling around in the parameter map of the current request (the one which called the generateFromLinkedIn() method).
After you call redirect() you should be returning from the method. You should move the remainder of the code into the #PostConstruct or <f:viewAction> of the backing bean tied to linkedIn.xhtml. It's the one who's called with the parameter.

What's the equivalent of HttpContext.Current.User in an HttpListener-hosted service?

I've written a custom attribute for ServiceStack that has the following code in it:
public override void Execute(IHttpRequest request, IHttpResponse response, object requestDto) {
HttpContext.Current.User = GetUserFromOAuth2BearerToken(request);
}
This works beautifully when hosting in IIS, but when using the built-in HttpListener host, HttpContext.Current returns null.
What's the correct syntax to set, and get, the security principal associated with the current request/response when using HttpListener?
I am not sure the 'security principal/System.Security.Principal' is available with the current request/response when using AppHostHttpListenerBase.
You could pull get the User inforamation doing something like...
var sessionId = request.Cookies["ss-id"].ToString();
var user = request.GetCacheClient().Get<AuthUserSession>(sessionId);

how to apply filters in jsf

I have a filter and I get Page not Found error when any client requests a JSF page in my web application. I don't know how to fix this issue.
Here is my filter code:
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession ses = req.getSession(true);
String pageRequested = req.getRequestURL().toString();
if (ses.getAttribute("userDetails") != null) {
fc.doFilter(request,response);
} else {
RequestDispatcher dis = request.getRequestDispatcher(LOGIN_PAGE);
dis.forward(request,response);
}
I have done all the necessary settings in web.xml deployment descriptor.
A 404 simply means that the requested resource is not found. Down to the point this can have 2 causes:
The URL is invalid (i.e. LOGIN_PAGE is invalid).
The resource is not there where you think it is.
To further nail down the cause in this particular problem we need to know 2 things:
What is the absolute URL of the current request? Print request.getRequestURL().
What is the absolute URL with which you can open the login page independently in webbrowser?
Then, you should be able to extract the correct LOGIN_PAGE from this information.

Resources