Sharepoint 2013 MVC 5 provider-hosted app. Fails to authenticate on HttpPost using [SharePointContextFilter]

拟墨画扇 提交于 2019-12-21 12:37:18

问题


I have been banging my head for the past week unable to resolve some issues with proper authentication for sharepoint provider-hosted app.

I am currently developing a sharepoint app for a company's Sharepoint online. I am using Visual Studio 2013. I deploy the app as a Cloud-service on the company's Windows Azure portal. Everything goes smooth up to the point when i need to make a HttpPost, then the app fails to authenticate. The design of the Conroller is as it follows:

    [SharePointContextFilter]
    public ActionResult Index()
    {
        UserSingleton user_temp = UserSingleton.GetInstance();

        User spUser = null;

        SharePointContext spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);

        using (var clientContext = spContext.CreateUserClientContextForSPHost())
        {
            if (clientContext != null)
            {
                spUser = clientContext.Web.CurrentUser;

                clientContext.Load(spUser, user => user.Title, user => user.Email);

                clientContext.ExecuteQuery();

                ....code....

            }
        }

           ....code....

        return View();
    }

Loading the index page goes fine, the the action creates user context and it's all good. The problem comes when i try to submit a HttpPost as it follows:

    [HttpPost]
    [ValidateAntiForgeryToken]
    [SharePointContextFilter]
    public ActionResult GetService(System.Web.Mvc.FormCollection fc)
    {
        PlannedHours ph = this.PopulateModel(fc);

        if (ph == null)
            return View("NoInfoFound");

        ViewData["PlannedHours"] = ph;

        return View("Index");
    }

When I call this via the post button, i get a "Unable to determine your identity. Please try again by launching the app installed on your site." The Shared/Error.cshtml view. The thing is that when i remove the [SharePointContextFilter] then it works, but that means that the request doesn't pass through[SharePointContextFilter] thus it is not properly authenticated? Or is it? Because it fails to validate the user's legitimacy.

One thing that i noticed when i don't remove [SharePointContextFilter] and invoke the post, then the url ends up without the {StandardTokens} query. Is it suppose to be like that - i mean it is smth like hostname.com/Home/GetService, however when i use actionlink the spcontext.js always appends the {StandardTokens} query to the base url - smth like hostname.com/Home/ActionNAme/?SPHostUrl=https%3A%2F%2FSHAREPOINTPAGEURL....

What i notice is that i call hostname.com/Home/ActionNAme/ without appending the query it fails to pass the [SharePointContextFilter].

I am fairly new to sharepoint 2013 and MVC 5 ( Razor ) so please if you know why my HttpPost fails to pass the [SharePointContextFilter] try to explain me or give any suggestion. I have tried using HttpGet However, when I Invoke the HttpGet having the [SharePointContextFilter] and appending the SPHostUrl= token it works. But then i cannot use the [ValidateAntiForgeryToken]. Is [ValidateAntiForgeryToken] even needed in such an app since the [SharePointContextFilter] always checks the legitimacy of the user? I am quire confused now. There is tons of material to read on the net and nothing is close to explain when to append these Standard tokens, when to use the [SharePointContextFilter] etc. The matter of fact is that I am developing a sharepoint app for the first time in my life and i've been researching and coding only for the past 3 weeks. So my knowledge is yet pretty limited, have that in mind when answering. Thanks in advance, I hope that i get some clarification about what is happening!

-----------------------------UPDATE----------------------------------------

Ok, a quick update. I have found out something rather weird. The SharePointContextFilterAttribute.cs

    public class SharePointContextFilterAttribute : ActionFilterAttribute
    {
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        Uri redirectUrl;
        switch (SharePointContextProvider.CheckRedirectionStatus(filterContext.HttpContext, out redirectUrl))
        {
            case RedirectionStatus.Ok:
                return;
            case RedirectionStatus.ShouldRedirect:
                filterContext.Result = new RedirectResult(redirectUrl.AbsoluteUri);
                break;
            case RedirectionStatus.CanNotRedirect:
                filterContext.Result = new ViewResult { ViewName = "Error" };
                break;
        }
    }
}

Always returns the last case ( RedirectionStatus.CanNotRedirect ) because the method SharePointContextProvider.CheckRedirectionStatus(filterContext.HttpContext, out redirectUrl) contains something that I cannot wrap my head around.

First of all:

     Uri spHostUrl = SharePointContext.GetSPHostUrl(httpContext.Request);

        if (spHostUrl == null)
        {
            return RedirectionStatus.CanNotRedirect;
        }

Ok i understand that - if the httpContext.Request does no contain the spHostUrl it will fail to redirect. That for some reason has to be there.

But the following:

    if (StringComparer.OrdinalIgnoreCase.Equals(httpContext.Request.HttpMethod,                    "POST"))
        {
            return RedirectionStatus.CanNotRedirect;
        }

Wait WHAAAT?!? No POST allowed?!!? What is going on here? I really don't know if I am doing something totally wrong or what? Am I even allowed to play around with the SharePointContext.cs ? I really need someone to clarify what exactly is going on... I'd appreciate!


回答1:


Above solution didn't work for me. I had the same problem with post, but for me it was the

return RedirectToAction("Index");

causing the error.

I changed it to:

return RedirectToAction("Index", new {SPHostUrl = SharePointContext.GetSPHostUrl(HttpContext.Request).AbsoluteUri});

and it worked.

I am not sure this is the solution for your problem as you are doing return view, but it may help someone :)




回答2:


I had the same issue and spent a few days to solve that. I don't know why SharePointContextFilter didn't work properly and didn't redirect. So my app was started via POST method and I had resubmission form confirm dialog when refreshed page. I also got "Unable to determine your identity. Please try again by launching the app installed on your site.".

To fix that I used Post/Redirect/Get pattern. I changed my Index action to do RedirectToAction to another IndexGet action. But you should have two IndexGet methods — for post and get.

In the end I got three actions for that. Index action redirected to POST IndexGet Action:

    public ActionResult Index()
    {            
        return RedirectToAction("IndexGet");
    }

Two IndexGet methods for Post and Get have the same code:

    [HttpPost]
    public ActionResult IndexGet(string tmp)
    {
        //your code from Index action
        return View();
    }

    [HttpGet]
    public ActionResult IndexGet()
    {
        //your code from Index action
        return View();
    }

So it works in the such way: on the start the Index action is called via POST. It redirects to POST IndexGet action and in this place SharePointContextFilter works properly and calls IndexGet via GET. This pattern solved my issue.




回答3:


I Agree with Libin/ user24176!

When navigating from one controller's action to another, the SharePoint refers the SPHostUrl to get the context. But, when I found out the issue is due to missing of SPHostUrl, I tried appending SPHostUrl in RedirectToAction method & everything starts working.

Solution: Append SPHostUrl to RedirectToAction method calls It's a common scenario to redirect the user to another Action using Controller's RedirectToAction method and it's overloads.

By default you can easily redirect the user to a "Another" Action using the following line of code

return RedirectToAction("Another");

Assuming that you're executing this call from the HomeController, your browser will be redirected to www.PHapp.com/Home/Another and again the SPHostUrl is missing

To fix that you can easily pass the SPHostUrl to the RedirectToAction method or you can override the method in your BaseController class (which each and every MVC App should have) and change the actual redirect to something like this

return RedirectToAction("Another",new 
{ SPHostUrl = SharePointContext.GetSPHostUrl(HttpContext.Request).AbsoluteUri }); 

This will force your browser to request the Action using the following url www.PHapp.com/Home/Fallback?SPHostUrl=..."

Of course there are other pitfalls when building SharePoint Apps using MVC, but being able to create a SharePoint Context from each and every Controller Method is a kind of critical depending on customer's requirements.

--# Update 2--- Initially I was working in IE & the site loads the controller Action without SPHostURL in IE lower versions, later I have updated _Layouts HTML- header section with force IE to open in latest version:

<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

After this, MVC PH App binds the SPHotsURL in all the Action links.




回答4:


I had the same error. Post request is normal for this case if it doesn't contain Form Data with errors. For example such Post request can contain such error:

SPErrorInfo:The endpoint address '' does not match the app's endpoint ''.

If you see such error in the POST your SharePointContextFilterAttribute class will surely throw the error with POST as you described in your question. But if POST request is a "good" request it will be processed before throwing the error and return redirect OK.

So, the task here is to not change behavior of SharePointContextFilterAttribute, but solve the issue. For example the error above says that I specified wrong domain name for my ClientID. You can generate ClientID in Microsoft Seller Dashboard and it requires to specify domain. If it differs from the domain where provider hosted app is hosted it will throw the error.

It is the same about https... if the redirect URL is not HTTPS it will throw the error also. For some reasons when you start app from Visual Studio it doesn't throw such errors, but if you deploy your app to App Catalog it will throw such errors.

So, I would recommend you to open Fiddler and research your POST requires. Most likely you will see the error, which clearly explains the problem.




回答5:


You have probably lost the Sharepoint Context in the Redirect Action.

Watch this demonstration from SP Conference from 29 minutes. The error you are describing is intentionally produced and a solution (provide the Sharepoint Context in a new RedirectAction is presented)

https://youtu.be/EnT5FtTeWA4?t=28m57s



来源:https://stackoverflow.com/questions/23828750/sharepoint-2013-mvc-5-provider-hosted-app-fails-to-authenticate-on-httppost-usi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!