Wednesday, 15 July 2015

Debugging errors in SharePoint add-in (app) development

If I was to summarize this post in one sentence, I’d say “if your SharePoint app/add-in is giving a generic error such as ‘An error occurred while processing your request’, then you’ve probably got the add-in registration/authentication stuff wrong!”. This applies to provider-hosted SharePoint add-ins rather than SharePoint-hosted add-ins, and also doesn’t apply to apps which use the Office 365 APIs as an alternative to the SharePoint add-in model. But for anyone developing/deploying SharePoint add-ins, this is a really common experience for the modern SharePoint/Office 365 developer – everybody screws this up at some point :) There are numerous forum posts and probably some existing articles about this, but I always seem to fall into this trap (usually with a typo or something) and then spend 1-2 hours debugging and searching the internet until I remember what specifically the problem is. So, I just wanted to take a short diversion from my Challenges in Office 365 development – and ways to address them series to write about this; partly just to have a quick reference for the next time I run into the problem, because I know I definitely will :)

Symptoms of the problem

When you click on your add-in, you are taken to the remote website (e.g. on localhost, or in Azure, IIS, or wherever you published it to) but instead of seeing the default page you simply see a white page with a simple message:
clip_image001
This happens because of this boilerplate code which is found in most SharePoint add-ins – see the 'CanNotRedirect' case in particular:

Uri redirectUrl;

switch (SharePointContextProvider.CheckRedirectionStatus(Context, out redirectUrl)) 
{ 
    case RedirectionStatus.Ok: 
    return;

    case RedirectionStatus.ShouldRedirect: 
        Response.Redirect(redirectUrl.AbsoluteUri, endResponse: true); 
    break;

    case RedirectionStatus.CanNotRedirect: 
        Response.Write("An error occurred while processing your request."); 
        Response.End(); 
    break; 
}


If you perform any debugging and step through the code, you’ll probably end up in the TokenHelper class, looking at this method:

public static string GetContextTokenFromRequest(HttpRequestBase request) 
{ 
    string[] paramNames = { "AppContext", "AppContextToken", "AccessToken", "SPAppToken" }; 
    foreach (string paramName in paramNames) 
    { 
        if (!string.IsNullOrEmpty(request.Form[paramName])) 
        { 
            return request.Form[paramName]; 
        } 
        if (!string.IsNullOrEmpty(request.QueryString[paramName])) 
        { 
            return request.QueryString[paramName]; 
        } 
    }

    return null; 
}

In many cases, what you’ll find is that null is being returned from this method – effectively no value can be found for any of the specified form parameters, either in the URL or the post body. We’ve successfully passed lots of earlier code and a valid context token is passed from SharePoint/Office 365 into the add-in, but we’re failing at this point.

Digging deeper

When you click on an add-in (e.g. in the SharePoint Site Contents page), you are taken to AppRedirect.aspx – this page eventually does a redirect to the redirect URL you should have specified in the app registration. This is a form POST, and in the body of the request is token needed by your remote code. Or at least, it should be. In the case where things aren’t working, you’ll notice that a token is NOT passed (e.g. in Fiddler):
Add-in redirect - Fiddler 1
Add-in redirect - Fiddler 2
We can see two things about the body of the form post from the image above:
  • The SPAppToken parameter is empty
  • The SPRedirectMessage parameter gives us a clue about my specific problem, with a value of “EndpointAuthorityDoesNotMatch
To be clear, the EndpointAuthorityDoesNotMatch message is just one flavor of this problem – you might see something else here depending on precisely how you’ve messed up your app registration/authentication setup :) For example, you might have the add-in registration all correct, but you’re trying to run the application on HTTP rather than HTTPS – in that case, you’ll see:
  • An empty SPAppToken
  • The SPErrorInfo parameter containing something like “The requested operation requires an HTTPS (SSL) channel. Ensure that the target endpoint address supports SSL and try again.”
The message will be encoded so will look more like this (for anyone pasting this into an internet search):
SPErrorInfo=The+requested+operation+requires+an+HTTPS+%28SSL%29+channel.++Ensure+that+the+target+endpoint+address+supports+SSL+and+try+again.
clip_image002
If you’re developing a high-trust add-in (for on-premises SharePoint), another flavor of the problem is that you haven’t lined up the authentication requirements – in particular the token-signing via a certificate aspects. See Package and publish high-trust apps for SharePoint 2013 for more info if this is your scenario.
In my case, EndpointAuthorityDoesNotMatch is telling me that the URL of the remote application does not match what is expected from the app registration for this client ID. I can use the /_layouts/15/AppInv.aspx page to lookup the details I specified when I registered my app with /_layouts/15/AppRegNew.aspx. IMPORTANT: if that last sentence doesn’t mean much to you and/or you’re starting out in add-in development, then your mistake is probably that you just haven’t registered the add-in at all! The next section might help:

Background - key things to know/remember when developing add-ins

Add-in registration

Add-ins need to be registered with SharePoint/Office 365 with a set of required permissions requested (which the person installing the add-in needs to consent to) – without this, you effectively just have some random code outside of SharePoint which is trying to read/write data, and so the security mechanisms are kicking-in and your code is blocked. This can be done via the Seller Dashboard for add-ins that will be widely distributed (even if just amongst your own environments), or using the AppRegNew.aspx page for other types. I suggest reading the following if you need some background on this - Guidelines for registering apps for SharePoint 2013
“Development mode” vs. “packaging for deployment mode”
Visual Studio really tries to help you out when developing add-ins. When I hit F5 to launch and debug my add-in, quite a few things are taken care of so I can get running quickly and see my code - I think of this as “development mode”. Let’s talk about what happens here, and also what you need to think of later on when packaging for deployment to another environment.
Development mode:
Upon pressing F5, the add-in is packaged up and deployed to the SharePoint site specified - this can be local or in SharePoint Online, and any previous installations there are removed. Additionally, the add-in is automatically registered with this SharePoint environment. Assuming the website for the add-in (i.e. the remote ASP.NET site) can indeed be browsed on the URL specified, then the F5 process opens up a browser window where the app is being installed/trusted, and you can then click on the icon and enter the app. Some other points to note:
  • The URL for the remote ASP.NET website is specified in the properties for the add-in web project – by default, a “localhost” URL such as https://localhost:44311/ is used against your local IIS Express instance. It’s possible to change this in the project properties, for example to a named virtual directory URL for use with a full IIS instance.
  • If using IIS Express, you should consider if localhost can be used with HTTPS on your machine. I had it in my head that it was possible somewhere to specify that it was SSL should NOT be used for the remote website – but frankly I can’t now remember if this true or find how it’s possible now, so that could be nonsense :) I always run on the full local IIS instance and have SSL running on localhost, and that works for me.
  • Authentication also needs to be considered. Often you’ll need to ensure “Windows Authentication” is enabled on the web project, so that the current user can be identified and authentication back to SharePoint can take place. If, however, you’re developing an Office 365 add-in which will authenticate to Azure AD, this isn’t the case.
  • In development mode, your app manifest is expected to have a ClientID value of “*”. Visual Studio will replace this at packaging/debug time with the correct value.
Packaging for deployment mode:
When you’re ready to think about deploying the add-in where other people can see it (e.g. Azure, some IIS servers, or somewhere else that isn’t your local dev box), you need to change a couple of things in your files before deploying. For an end-to-end walk through of the deployment process (using Azure Web Apps as the hosting platform), see my Deploy SP2013 provider-hosted apps/Remote Event Receivers to Azure Websites (for Office 365 apps) post – it was written a while back but I’ve updated it since to account for changes in Azure and Visual Studio. But here are the key changes to make from “development mode”:
  • Deal with the add-in registration – go to AppRegNew.aspx in the SharePoint environment the add-in will be used in, and register the add-in (reminder – either see my last link or Guidelines for registering apps for SharePoint 2013 if you’re not clear on this step!) For the app/add-in ID, either generate a new GUID on the page or use the existing ClientId value in your project files – the key thing is that they match, that’s all.
    • Make a note of the ClientId and ClientSecret used/generated on the AppRegNew.aspx page – you’ll need these!
  • Ensure the AppSettings section of your web.config contains the ClientId and ClientSecret from the add-in registration (previous step) – overwrite the existing values if needed. The authentication code baked into every add-in by Visual Studio tools (TokenHelper.cs) will read the values from here.
  • Optionally, find your appmanifest.xml file and replace the “*” in the ClientId value with the add-in ID from the registration – effectively hard-coding the value in there. This step is technically optional, because the process of packaging the app in Visual Studio (next step) will actually replace the “*” with a GUID value you specify anyway, but it can make sense to not rely on this and explicitly specify the GUID in appmanifest.xml instead - for example, if you’re distributing the VS project files, or perhaps aren’t expecting to do much more local development/testing. Otherwise, we’ll deal with things in the next step.
  • Optionally, replace the “~remoteAppUrl” value in appmanifest.xml also – it’s the exact same deal /consideration as the previous point.
  • Publish both elements of the add-in (the add-in remote website and the add-in package) using the Visual Studio “Publish..” mechanism. Right-click on the add-in project and click “Publish..”, and you should see the dialog helping you publish the outputs of both VS projects:
    VS SP add-in publish dialog 1
    You’ll need to press both of these buttons :) The first one will help you publish your web project – if you want to publish to Azure easily, ensure you have the Azure SDK installed. This will allow you to “discover” and publish to Azure Web Apps (websites) within your subscription, without having to separately download the publishing profile used for the connection.

    The second button will package the app – you’ll be asked for some key details which will be baked into the package:

    VS add-in publish dialog 2

    During the packaging process, a couple of things will be baked into the appmanifest.xml in the .app file which is generated (and this is why some of the previous steps are optional):
    • The ~remoteAppUrl token will be replaced with the remote website URL you specify
    • The “*” in the ClientId value will be replaced with the value from the Client ID textbox above

      NOTE - you won’t see any changes in your source appmanifest.xml file. It’s only if you crack open the add-in, by renaming from .app to .zip, and examining the files inside will you see where this has happened.
  • Take the add-in package and deploy it to the SharePoint add-in catalog for your environment (be in Office 365 or on-premises SharePoint). Drag the .app file into the catalog, and add any additional details (e.g. add-in description, screenshots etc.) to the list item for the file.
The add-in is now available to be installed to SharePoint sites.

Back to my problem

Anyway, AppInv.aspx allows me to enter a client ID/app ID (which identifies an add-in/some remote code) and see the details of the registration:
AppInv.aspx
When I click the “Lookup” button, the other textboxes get populated with the details of the registration, as shown above. In my case, I can now see the mistake I made – it’s the “www.” in the app domain/URL. When I consider the URL my remote site is actually running on, it’s actually just https://cob-[foo].azurewebsites.net without the “www.” – as you can see if you look back at the first image in this article.

So, the solution in my case is to re-register the add-in with the correct URL.
It’s not possible to modify an existing add-in registration, so you need to do a new registration with a new App ID (and update the Client ID value in your app manifest etc.) You can tidy up the old app registration by going to AppPrincipals.aspx and deleting the original app principal there.

Summary

There are a few common pitfalls around developing SharePoint add-ins, and even with a reasonable level of experience it’s easy to fall into one of these! Some key things to look for include mistakes in the add-in registration, ensuring you know the difference between “development mode” and “packaging for production mode”, and also the need for HTTPS on the remote web application. If everything is straight, your add-in should work fine however.
Steve Peschka has some more good tips in this area at https://samlman.wordpress.com/2015/03/01/more-troubleshooting-tips-for-high-trust-apps-on-sharepoint-2013/

In the next post, I’ll resume my series on Challenges in Office 365 development – and how to address them.

3 comments:

Christopher Henry said...

Thanks so much for this post! I too become forgetful of the differences between development and deployment configurations. The asterisk for Client ID in development mode I had completely forgotten. Thanks!

Dave Jorgensen said...

Hi Chris. Great article. One comment I have. you said this: " It’s not possible to modify an existing add-in registration, so you need to do a new registration with a new App ID". While you cannot edit directly, I've had success re-registering while using the same ID and SECRET. This in essence edits (well, really it overwrites) but the point being, you can continue to use the same ID and SECRET when you re-register the app.

Eric Blais said...

If SharePoint was Rock n Roll you would be Mick Jagger. Keep up the great work, yet gain you saved my as$.