37

I have been working a lot with DotNetOpenAuth. First we used 5.0.0-alpha1 but we switched over to v4.0.30319 because we couldn't find what was causing our problems.

We are building a C# Web API project on .NET 4.5.1 RC with MVC 5 RC in Visual Studio 2013. We have implemented IAuthorizationServerHost, INonceStore, and ICryptoKeyStore.

The problem that we have are around the following case:

public class TokensController : Controller
{
    private readonly AuthorizationServer authorizationServer = new AuthorizationServer(new MyAuthorizationServer());

    /// <summary>
    /// This action will handle all token requests. 
    /// </summary>
    /// <returns>The action result that will output the token response.</returns>
    [HttpPost]
    public ActionResult Index()
    {
        var outgoingWebResponse = this.authorizationServer.HandleTokenRequest(this.Request);
        return outgoingWebResponse.AsActionResult();
    }
}

return outgoingWebResponse.AsActionResult(); a method with origins in DotNetOpenAuth.Messaging and the MessagingUtilities static class. The DotNetOpenAuth.Core (which contains this code) references MVC 4.0 and the HttpResponseMessageActionResult class inherits from ActionResult.

This means the current version of DotNetOpenAuth in not compatible with MVC 5. Compiling and trying to run this will just case 500 errors.

Does anyone have any ideas how this could be easily fixed (or maybe not)?

I didn't notice that the DotNetOpenAuth Nuget package wrote over my packages for 5.0. So after reinstalling the packages and adding the assemblyBinding again:

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
    <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
    <bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="5.0.0.0" />
  </dependentAssembly>
</assemblyBinding>

This got us a little further. Now the error comes down to:

Attempt by security transparent method 'DotNetOpenAuth.Messaging.MessagingUtilities.AsActionResult(DotNetOpenAuth.Messaging.OutgoingWebResponse)' to access security critical type 'System.Web.Mvc.ActionResult' failed.

Jeroen
  • 53,290
  • 30
  • 172
  • 279
jens
  • 466
  • 4
  • 8

5 Answers5

51

Fix available.

Install NuGet package DotNetOpenAuth.Mvc5 and change all uses of AsActionResult() to AsActionResultMvc5()

Andrew Arnott
  • 74,820
  • 24
  • 127
  • 163
6

After further debugging and talking to the people at DotNetOpenAuth at GitHub https://github.com/DotNetOpenAuth/DotNetOpenAuth/issues/307 the conclusion is that MVC 5 has a new security model.

Binding redirect will therefore not be enough. Until further there are two choices:

1) Grab the DotNetOpenAuth source code and removing the [assembly: AllowPartiallyTrustedCallers] from all projects. Recompile and member to disable strong name verfication sn -Vr *. After this code cannot be run on Medium Trust environments.

2) Grab the DotNetOpenAuth source code and recompiling it against MVC 5.

According to the discussion on GitHub the best future solution would be moving out all related MVC stuff to a separate assembly.

jens
  • 466
  • 4
  • 8
  • I've just upgraded to MVC5 and come crashing into this issue. Looking around I've been amazed to find out that AA has dropped the project. I can't really tell of DNOA is still being actively developed as there hasn't been a release for a very long time. What is the state of the project? Are you still using DNOA? – Jammer Jun 18 '14 at 09:06
3

A workaround (can use with current beta nuget package) for this case:

  • Create a ActionResult class wraps HttpResponseMessage

    public class WrapperHttpResponseMessageResult : ActionResult
    {
        private readonly HttpResponseMessage _response;
    
        public WrapperHttpResponseMessageResult(HttpResponseMessage response)
        {
            _response = response;
        }
    
        public override void ExecuteResult(ControllerContext context)
        {
            HttpResponseBase responseContext = context.RequestContext.HttpContext.Response;
            responseContext.StatusCode = (int)_response.StatusCode;
            responseContext.StatusDescription = _response.ReasonPhrase;
            foreach (KeyValuePair<string, IEnumerable<string>> keyValuePair in (HttpHeaders)_response.Headers)
            {
                foreach (string str in keyValuePair.Value)
                    responseContext.AddHeader(keyValuePair.Key, str);
            }
    
            if (_response.Content != null)
            {
                _response.Content.CopyToAsync(responseContext.OutputStream).Wait();
            }
        }
    }
    
  • Change return outgoingWebResponse.AsActionResult(); to new WrapperHttpResponseMessageResult(outgoingWebResponse);

Code of WrapperHttpResponseMessageResult is copied from AsActionResult, so they do the same function.

langtu
  • 1,118
  • 1
  • 10
  • 23
2

use this to ensure that the authorizer gets passed correctly.

  public class MvcAuthorizer : WebAuthorizer
{
    public ActionResult BeginAuthorization()
    {
        return new MvcOAuthActionResult(this);
    }

    public new ActionResult BeginAuthorization(Uri callback)
    {
        this.Callback = callback;
        return new MvcOAuthActionResult(this);
    }
}

' then retrieve it correctly

public class MvcOAuthActionResult : ActionResult
{
    private readonly WebAuthorizer webAuth;

    public MvcOAuthActionResult(WebAuthorizer webAuth)
    {
        this.webAuth = webAuth;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        webAuth.PerformRedirect = authUrl =>
        {
            HttpContext.Current.Response.Redirect(authUrl);
        };

        Uri callback =
            webAuth.Callback == null ?
                HttpContext.Current.Request.Url :
                webAuth.Callback;

        webAuth.BeginAuthorization(callback);
    }
}
0

If using it with OutgoingWebresponse (did not upgrade dotnetOpenAuth but mvc yes to 5).

Add this class (hacked from langtu's response):

 public class WrapperHttpResponseMessageResult : ActionResult
{
    private readonly OutgoingWebResponse _response;

    public WrapperHttpResponseMessageResult(OutgoingWebResponse response)
    {
        _response = response;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        HttpResponseBase responseContext = context.RequestContext.HttpContext.Response;
        responseContext.StatusCode = (int)_response.Status;
        responseContext.StatusDescription = _response.Status.ToString();
        foreach (string key in _response.Headers.Keys)
        {
            responseContext.AddHeader(key, _response.Headers[key]);
        }

        if (_response.Body != null)
        {
            StreamWriter escritor = new StreamWriter(responseContext.OutputStream);
            escritor.WriteAsync(_response.Body).Wait();
        }
    }
}

And then replace :

return response.AsActionResult();

with

return new WrapperHttpResponseMessageResult(response);

Community
  • 1
  • 1
  • The easiest and cleanest is to install the DotNetOpenAuth.Mvc5 nuget package as Andrew Arnott mentioned below. – decocijo Apr 14 '15 at 07:38