Skip to main content

Building a Custom Web API for use with SharePoint Online

IMPORTANT UPDATE – PLEASE READ: It looks like as of May 1, 2018 Microsoft made changes that invalidate the techniques shown in this post. Attempting to exchange the access token received by the user interface with an access token that can be used to interact with SharePoint now fails with the following error message: “AADSTS240002: Input id_token cannot be used as ‘urn:ietf:params:oauth:grant-type:jwt-bearer’ grant”. I am going to investigate this issue and post my findings here. I will update this post or I will write a new post when I find a viable alternative solution.

I recently answered a Question on SharePoint Stack Exchange. I’ve spent quite a bit of time looking at this problem over the last week or two so I thought I’d share what I’ve found in a blog post.
I’d like to highlight the three blog posts that helped me the most in my research. In terms of building the Web API, I used a blog post by Koen Koninckx entitled Securing Your Web API. In terms of the JavaScript code used to consume the Web API with ADAL.js, I used the first two blog posts in the Extending SharePoint with ADAL and the Microsoft Graph API series by Julie Turner. I suggest you read these posts before going through this walkthrough.

Overview

Let me set the stage by outlining what we are trying to achieve. We want to be able to call into a custom Web API from JavaScript code running in a page in SharePoint Online. We also want the Web API to be able to make Client Object Model (CSOM) calls back to SharePoint using the identity of the calling user. Finally we want the Web API to authenticate callers to ensure they are members of the Azure Active Directory (AD) associated with SharePoint Online.
To do this the JavaScript code running in SharePoint Online will use ADAL.js to get an access token from Azure AD. It will then pass the token to the custom Web API when it makes web service calls. The Web API will use the access token it receives to authenticate that the user is a member of the Azure AD associated with SharePoint Online. Assuming that this is the case, the Web API will exchange the token passed from the user interface for a new token that it can use to make CSOM calls to back to SharePoint. Finally the Web API will make the CSOM calls and return any results back to the calling code.
There are a lot of moving parts here, I’ll try to explain each one to the best of my ability.

Disclaimers

What I’m showing you is the result of my research over the last couple weeks. I have not tried this in production nor have I tested it at any scale.
Also, there are potential issues for users who have added the SharePoint Online URLs to the Local Intranet or Trusted Sites zone in Internet Explorer or Edge. These are not potential issues for this solution, with the proper security zone configuration it will work fine in Internet Explorer and Edge. The issue would be with other sites that use the ADAL.js library. This topic is covered at the end of the blog post. I put it there instead of here because you need to understand the techniques used in the solution to put the potential issues in context.

Download


Building the Web API

I’ll be using Visual Studio 2015 Community Edition to build the Web API.
  • In Visual Studio, click File | New | Project…
  • In the New Project dialog, select the Visual C# > Web under Installed Templates
  • Select the ASP.NET Web Application template, set the Name to SpoWebApi and click OK
New Project
  • In the New ASP.NET Project select the following options:
    • Select the Empty template
    • Select the Web API checkbox in the Add folders and core references section
    • Uncheck the Host in the cloud checkbox in the Microsoft Azure section (we’ll create the Azure website later)
  • Click OK the close the dialog and create the project
New ASP.NET Project

Configure SSL

We want to ensure that our project uses SSL while debugging.
  • Click the project in the Solution Explorer
  • Set the SSL Enabled property to true in the Properties window
  • Copy the value of the SSL URL property to the clipboard
  • Double click on the Properties node in the Solution Explorer to bring up the project properties editor.
  • Select the Web tab
  • Paste the value of the SSL URL property into the Start URL property
  • Click the Save icon in the toolbar to save the changes to the project properties
Configure SSL

Create the Azure AD Application

Before we go any further on the Web API, let’s create the Azure AD application we’ll be using to identify and authenticate callers to the Web API and to get the access token necessary for the Web API to interact with SharePoint Online.
  • Navigate to https://portal.azure.com and login with your SharePoint Online administrator credentials
  • In the left pane select Azure Active Directory
  • In the next pane (second from the left) select App Registrations
App registration
  • Click the Add link at the top of the right pane
  • Set the Name to SpoWebApi
  • Set the Sign-on URL to the SSL URL property in the Visual Studio project
  • Click the Create button to create the Azure AD application
Create new application
  • Wait for the notification that the application has been created
  • In the App registrations pane find and click on the SpoWebApi application to access the settings
App registrations
We need to be enable the Implicit authentication flow to be able to authenticate with Azure AD from JavaScript. There is no setting for this so we’ll have to manually edit the application manifest.
  • At the top of the left settings pane, click on the Manifest link to open the app manifest editor
App settings
  • Change the setting for oauth2AllowImplicitFlow from false to true
  • Click the Save button at the top of the editor
  • Close the editor
Edit manifest

Now we’ll add permissions to enable us to access SharePoint Online from the Web API
  • In the Settings pane click Required permissions
  • Click the Add button at the top of the Required permissions pane
Required permissions
  • Click the Select an API pane in the Add API access pane
Add API access
  • Select Office 365 SharePoint Online (Microsoft.SharePoint) and click Select
Select an API
For this application we’ll just be reading user information so the only permission we’ll need is Read user profiles.
  • Check Read user profiles and click Select
  • Click Done in the Add API access pane to save
Enable Access
Now we need to create a client secret. This is effectively the password for the Azure AD application.
  • Go back to the settings for the application
  • In the right pane click in Keys
App settings
  • In the first row in the table set the Description to 1 year
  • In the Expires dropdown select the value in 1 year
  • Click the Save button
Keys
This is the only time you will see the generated key. If you close the Keys pane and come back to it the key will be hidden from you. If that happens and you forgot to copy the key somewhere, just delete the key and create a new one.
  • Copy the generated key into a text editor like Notepad
  • Close the Keys pane
Copy key to Notepad
There’s one final configuration we need to do. We need to set the base URL of SharePoint Online as a reply URL so JavaScript code running in SharePoint Online can authenticate.
  • Go back to the settings for the application
  • In the right pane click Reply URLs
App settings
The SSL URL for our Visual Studio project will already be there. That was one of the properties we set when we first created the Azure AD application.
  • Add the base URL of your SharePoint Online site to the list of sites in the Reply URLs pane and click the Save button
Reply URL

Add Azure AD Application Properties to the Application Settings of the Web API Project
Now that we’ve completed the configuration of the Azure Ad application we can get back to our Web API project.
The code in our Web API project is going to need some of the settings of our Azure AD application. We’ll store these settings in the appSettings section of Web.config.
  • In the Solution Explorer double-click the Web.config file to open it in the editor
  • Add the following nodes as children of the appSettings node
<add key="Audience" value="" />
<add key="Tenant" value="" />
<add key="ClientID" value="" />
<add key="ClientSecret" value="" />
The value of the ClientSecret element is the value of the key that we copied into Notepad.
  • Copy the key value from Notepad into the value attribute of the element for the ClientSecret
We’ll get the values of the other properties from the Azure AD application settings.
  • Go back to the settings for the application
  • In the right pane click Properties
App settings
  • Copy the key value Application ID into the value attribute of the element for the ClientID
  • Copy the key value App ID URI into the value attribute of the element for the Audience
App properties
The final setting we need in the Tenant ID.
  • Go back to the App registrations pane and click on Endpoints
App registrations
  • Click the copy button beside any of the links in the Endpoints pane
    • The tenant id is the Guid that follows the base URL
  • Copy the tenant id into the value attribute of the element for the Tenant
Tenant ID
The appSettings section of the Web.config file should now look something like this.
App settings
  • Save and close the Web.config file

Add NuGet Packages

We’re going to need some NuGet packages to enable support for OWIN middleware, cross-origin resource sharing (CORS), Azure AD authentication, and the SharePoint Client Object Model. We’ll add these packages to our project using the NuGet Package Manager Console.
  • In Visual Studio click Tools | NuGet Package Manager | Package Manager Console to open the console
  • Execute the following commands one by one
Install-Package Microsoft.AspNet.WebApi.Owin
Install-Package Microsoft.AspNet.WebApi.Cors
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Microsoft.Owin.Security.ActiveDirectory
Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory
Install-Package SharePointPnPCoreOnline

Add OWIN Startup Class

We’ll now add an OWIN startup class and add the code that will validate callers to the service.
  • In the Solution Explorer, right-click on the project and select Add | New Item…
  • Select the OWIN Startup class template from the Web section
  • Set the Name to Startup.cs
  • Click Add
Replace the code in Startup.cs with the following. You will need to fix up the namespaces if you named your project something other than SpoWebApi.
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using System.Web.Http;
using Microsoft.Owin.Security.ActiveDirectory;
using System.Configuration;
using System.IdentityModel.Tokens;

[assembly: OwinStartup(typeof(SpoWebApi.Startup))]

namespace SpoWebApi
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);

            HttpConfiguration config = new HttpConfiguration();

            WebApiConfig.Register(config);
            app.UseWebApi(config);
        }

        public void ConfigureAuth(IAppBuilder app)
        {
            app.UseWindowsAzureActiveDirectoryBearerAuthentication(
            new WindowsAzureActiveDirectoryBearerAuthenticationOptions
            {
                Tenant = ConfigurationManager.AppSettings["Tenant"],
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidAudience = ConfigurationManager.AppSettings["Audience"]
                },
            });
        }
    }
}
The important piece here is the ConfigureAuth method, it is the part of our service implementation that validates callers. To do that it does several checks against the incoming request. Is there an access token in the headers? Was the token issued by the expected authority? Has the token expired? Has the token been tampered with? And so on. If any of these checks fail then the service call fails.
For additional details see the MSDN article by Vittorio Bertocci entitled ASP.NET Web API – Secure ASP.NET Web API with Windows Azure AD and Microsoft OWIN Components.

Enable Cross-origin Resource Sharing (CORS)

Our Web API is going to be hosted in a different domain than SharePoint Online so we’re going to need to enable CORS so the service operations can be called by JavaScript. Open the WebApiConfig.cs file in the App_Start folder and add the following code to the Register method to enable CORS.
config.EnableCors(
    new EnableCorsAttribute("https://<tenant>.sharepoint.com", "*", "*"));
You’ll need to replace <tenant> with the name of your Office 365 tenant. The tenant name I’ll be using during this walkthrough is robwindsor2.
Enable CORS

Add the Web API Controller

We’re finally ready to add the actual service operations.
  • Right-click on the Controllers node in the Solution Explorer and select Add | Controller…
  • In the Add Scaffold dialog select Web API 2 Controller – Empty and click Add
Add Scaffold
  • In the Add Controller dialog set the Controller Name to TestController and click Add
Add Controller
  • Add the following using statements at the top of the TestController.cs file
using System.Configuration;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.SharePoint.Client;
The first method we’ll add is one that will get an access token we can use to make CSOM calls to SharePoint Online. It will take the URL to a SharePoint site collection and an access token (the one being passed from the caller) as parameters. We can use that information plus the credentials of the Azure AD application to get the access token required to make CSOM calls.
  • Add the following method to the TestController class
internal async static Task<string> GetSharePointAccessToken(string sharePointUrl, 
    string accessToken)
{
    var clientID = ConfigurationManager.AppSettings["ClientID"];
    var clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
    var tenant = ConfigurationManager.AppSettings["Tenant"];

    var appCred = new ClientCredential(clientID, clientSecret);
    var authContext = new AuthenticationContext(
        "https://login.microsoftonline.com/" + tenant);

    var resource = new Uri(sharePointUrl).GetLeftPart(UriPartial.Authority);

    var authResult = await authContext.AcquireTokenAsync(resource, appCred, 
        new UserAssertion(accessToken));
    return authResult.AccessToken;
}
The next method we’ll add will be a service operation to get information about the current user. We can use this to validate that the identity of the calling user is being picked up by the service.
  • Add the following method to the TestController class
[HttpGet]
public async Task<IEnumerable<string>> GetUserInfo(string sharePointUrl)
{
    var results = new List<string>();

    try
    {
        var userToken = this.Request.Headers.Authorization.Parameter;
        var newToken = await GetSharePointAccessToken(sharePointUrl, userToken);

        using (var context = new ClientContext(sharePointUrl))
        {
            context.ExecutingWebRequest +=
                (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add(
                "Authorization", "Bearer " + newToken);

            var web = context.Web;
            var user = web.CurrentUser;
            context.Load(user);
            context.ExecuteQuery();

            results.Add(user.Title);
            results.Add(user.LoginName);
            results.Add(user.Email);
        }
    }
    catch (Exception ex)
    {
        results.Add(ex.ToString());
    }

    return results;
}
We start by getting an access token we can use to make CSOM calls to SharePoint. We then implement the event handler for the context’s ExecutingWebRequest event. This ensures that the CSOM access token is included in the headers of each request. The remainder of the code is just standard CSOM to get the current user’s name, login name, and email address.
That completes the work on the Web API.

Add the SharePoint Client-side Code

We’re now ready to add the HTML and JavaScript that will consume our web service. We’ll create this file in Visual Studio and the upload it to the Site Assets library in SharePoint.
  • Right-click the project in the Solution Explorer and select Add | New Folder.
    • Name the folder SharePoint
  • Right-click on the SharePoint folder and select Add | HTML Page
    • Name the page SpoWebApi.html
  • Replace the markup in SpoWebApi.html with the following HTML and JavaScript
  • Save the file
<table>
    <tr>
        <td style="vertical-align:top">
            <input id="consentButton" type="button" value="Consent" />
            <br />
            <input id="getUserInfoButton" type="button" value="Get User Info" />
        </td>
        <td style="width:20px">
            &nbsp;
        </td>
        <td style="vertical-align:top">
            <div id="message">
                initializing...
            </div>
        </td>
    </tr>
</table>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<!-- Place holder 1 -->

<script type="text/javascript">
 (function () {
     "use strict";

            // Place holder 2

     jQuery(function () {
         jQuery("#consentButton").click(consent);
         jQuery("#getUserInfoButton").click(getUserInfo);
     });

     function consent() {
         var message = jQuery("#message");
         message.text("Consent button clicked");
     };

     function getUserInfo() {
         var message = jQuery("#message");
         message.text("User info button clicked");
     };

 })();
</script>
The top part of the file contains the user interface. There will be two buttons and an div element we can use to display the results of service calls.
Then we have a script element used to load jQuery.
Finally we have a script element containing our custom JavaScript. In the jQuery ready function (i.e. the function that gets called when the page load is complete) we attach the event handlers for the button clicks. Then we have the two functions that implement the event handlers. Right now we just write out a message to the page indicating that the button was clicked.

Deploy to SharePoint Site Assets Library

We could create a Sandbox Solution to deploy the file we just created but for this walkthrough we’ll just do it manually.
  • Open a browser and navigate to the Site Assets library of the SharePoint site you want to use for testing
    • NOTE: The example we are building may not work in Internet Explorer or Edge without some configuration of security zones. I’ll cover this towards the end of the post
    • I’ll be using Google Chrome for most of this walkthrough
  • In the Solution Explorer right-click on the SharePoint folder and select Open Folder in File Explorer
  • Deploy SpoWebApi.html to Site Assets using drag and drop or by clicking the Upload button in the toolbar for the library
Site Assets
  • In the Site Assets library click in the ellipses beside the newly uploaded file
  • Copy the URL to the file into a text editor
    • We’ll need this URL in a future step
File URL

Create a Web Part Page

  • Navigate to the Site Pages library
  • In the File tab of the ribbon drop down the options associated with the New Document button
New document
  • Click the Web Part Page option
  • Set the Name to SpoWebApi
  • Select the Full Page, Vertical template
  • Select Site Pages in the Document Library dropdown
  • Click Create to create the web part page
New web part page
Web part page

Add a Content Editor Web Part

  • Click the Add a Web Part link
  • Select Content Editor from the Media and Content category
  • Click the Add button to add the web part to the page
Add a web part
  • Open the content menu for the Content Editor web part
  • Click Edit Web Part to open the web part properties
Edit Web Part
We’ll now link the SpoWebApi.hrml file in the Site Assets library to the Content Editor web part.
  • Paste the URL we saved a few steps back into the Content Link text box
  • Click OK to close the web part properties editor
Content link
  • Click Stop Editing to take the page out of edit mode
Web part page
Clicking either of the buttons should show a message indicating the button was clicked.

Update Code to Grant Permissions Requested by Azure AD Application

The Azure AD application has requested permission to interact with active directory and with SharePoint. We need to grant those permissions before we can use the application. We can navigate to a well known URL to be presented with a page where we can grant the permissions. The format of that URL is:
https://login.microsoftonline.com/<tenant id>/oauth2/authorize?
response_type=code&client_id=<client id>&redirect_uri=<redirect URL>&prompt=admin_consent
We’ll update the event handler for the Consent button click to build this URL and navigate to the page but first we need some configuration information.
  • Replace the JavaScript comment Place Holder 2 with the following script
var config = {
 instance: 'https://login.microsoftonline.com/',
 tenant: '31793ae6-d547-42b4-9935-33cc7700f267',
 clientId: 'b678d3b5-85aa-45b7-aec5-cbf625a27d04',
 endpoints: {
     endPointApiUri: 'https://localhost:44391/'
 },
 cacheLocation: 'localStorage'
};

// var authContext = new AuthenticationContext(config);
We’ll discuss this code in more detail in an upcoming step. For right now we need the client id and the tenant id. These are the same values that are stored in the Web.config file of the Web API.
Now we can replace the implementation of the consent function.
  • Replace the existing consent function with the following code
function consent() {
    var url = "https://login.microsoftonline.com/" +
        config.tenant +
        "/oauth2/authorize?response_type=code" +
        "&client_id=" + config.clientId +
        "&redirect_uri=" + window.location.href +
        "&prompt=admin_consent";

    SP.Utilities.HttpUtility.navigateTo(url);
};
  • Save the file
  • Upload the file to the Site Assets library
    • Make sure you replace the existing file
  • Navigate to the SpoWebApi page in Site Pages library
    • Or refresh the page if you already have it open
  • Click the Consent button
Enter credentials
  • Enter your credentials (if necessary)
Consent dialog
We are now presented with the consent dialog. Here we can see the name of the app, the publisher of the app, the permissions being requested by the app, and the email of the logged-in user (i.e. your email – this is blurred in the screen capture) who will be granting the requested permissions.
  • Click Accept to grant the requested permissions to the app
You’ll be redirected back to the SpoWebApi page in Site Pages library.

Update Code to Authenticate with Azure AD

Now that we’ve granted permissions we are ready to add the code that will authenticate with the Azure AD application. As mentioned previously, we’ll be using the ADAL.js library to do the authentication.
  • Replace the HTML comment Place Holder 1 with the following script element
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.13/js/adal.min.js"></script>
Now we’ll create an AuthenticationContext object. We’ll be passing an object with information about Azure AD and our Web API to the constructor.
  • Uncomment the call that creates the AuthenticationContext object
var config = {
 instance: 'https://login.microsoftonline.com/',
 tenant: '31793ae6-d547-42b4-9935-33cc7700f267',
 clientId: 'b678d3b5-85aa-45b7-aec5-cbf625a27d04',
 endpoints: {
     endPointApiUri: 'https://localhost:44391/'
 },
 cacheLocation: 'localStorage'
};

var authContext = new AuthenticationContext(config);
As mentioned previously, the tenant and the clientId are the same values as those stored in the Web.config file of the Web API. The endpoints.endPointApiUri value is the URL to the Web API. That will be the SSL URL while we are debugging the Web API. The value of instance and cacheLocation are standard, they are not specific to our example.
Finally we’ll update the jQuery ready function to do the authentication.
  • Replace the existing jQuery ready function with the following code
jQuery(function () {
    var isCallback = authContext.isCallback(window.location.hash);
    console.log("Callback: " + isCallback);

    if (isCallback && !authContext.getLoginError()) {
        console.log("Handle callback");
        authContext.handleWindowCallback(window.location.hash);
    } else {
        var user = authContext.getCachedUser();
        if (!user) {
            console.log("Login");
            authContext.login();
        } else {
            console.log("Cached user");
            jQuery("#consentButton").click(consent);
            jQuery("#getUserInfoButton").click(getUserInfo);
        }
    }
});
We first check to see if we are getting a postback from Azure AD. As you’ll see when you run the application, the authentication handshake requires full page postbacks.  If we are receiving a postback and there weren’t any login errors then we let ADAL handle the postback. If the call is not a postback then we check to see if the user has already be authenticated by calling getCachedUser. If they haven’t then we call the login function.
Let’s test this code.
  • Save the file
  • Upload the file to the Site Assets library
    • Make sure you replace the existing file
  • Navigate to the SpoWebApi page in Site Pages library
    • Or refresh the page if you already have it open
  • Open the debugging tools for your browser
    • I’m using Google Chrome
  • Clear the cache for this domain
    • That’s done in the Application tab in Chrome
  • Open the Console tab in the debugging tools
  • Check the Preserve log checkbox
  • Refresh the page
Navigate no cache
I’ve added several calls to console.log() in the authentication code so we can see the authentication process. Near the top of log you can see the call to login. This does a postback but it does not require that you enter your credentials because you are already logged-in to SharePoint. In the next call authContext.isCallback is true so we let ADAL handle it. At this point the user information is cached which we can see in the results of the final call. Yes there are a lot of postbacks here. I’ve done a lot of Googling but have yet to find any way to reduce them.
There is some good news however. Now that we’ve gone through the handshake process we won’t need to do it again until the cached token expires.
  • Refresh the page
Navigate with cache
Note that this request is using the cached user information.

Make the Call to the GetUserInfo Service Operation

After a fairly long journey we are finally ready to make a service call. We’ll be using jQuery to make a call to the GetUserInfo operation in our service.
  • Replace the getUserInfo function with the following two functions
function getAuthToken(clientId) {
    var dfd = jQuery.Deferred();

    var tokenCache = authContext.getCachedToken(clientId);

    if (tokenCache == undefined) {
        authContext.acquireToken(clientId, function (error, token) {
            if (error || !token) {
                dfd.reject(error);
            }
            else {
                dfd.resolve(token);
            }
        });
    } else {
        dfd.resolve(tokenCache);
    }

    return dfd.promise();
};

function getUserInfo() {
    var call = getAuthToken(config.clientId);
    call.done(function (token) {
        if (token != undefined) {
            var call2 = jQuery.ajax({
                type: "GET",
                url: config.endpoints.endPointApiUri + "/api/test/getuserinfo",
                data: {
                    "sharePointUrl": _spPageContextInfo.webAbsoluteUrl
                },
                headers: {
                    'Accept': 'application/json',
                    "Authorization": "Bearer " + token
                }
            });
            call2.done(function (response) {
                var message = jQuery("#message");
                message.text("");
                jQuery.each(response, function (index, value) {
                    message.append(value);
                    message.append("<br/>");
                });
            });
            call2.fail(function (error) {
                var message = jQuery("#message");
                message.text(JSON.stringify(error));
            });
        }
    });
    call.fail(function (error) {
        var message = jQuery("#message");
        message.text(JSON.stringify(error));
    })
};
The first line of the getUserInfo function makes a call to getAuthToken to get an access token. The getAuthToken function will try to get the access token from the cache. If the token isn’t there it will go to Azure AD to get it.
Once the getUserInfo function has a token it uses jQuery to make a call to the GetUserInfo service operation in our Web API. This function takes the URL to a SharePoint site as a parameter. We pass the value of this parameter in the body of the call. For this example we are just passing the URL of the current site. Finally, we pass the access token to the service in the Authorization header.
The response from the service call is an array of strings representing information about the current user: their name, their login name and their email.
We’re now ready to test the code that calls the service.
  • Save the file
  • Upload the file to the Site Assets library
    • Make sure you replace the existing file
  • Navigate to the SpoWebApi page in Site Pages library
    • Or refresh the page if you already have it open
  • Run the Web API in the debugger by pressing F5 in Visual Studio
  • Go back to the browser and click the Get User Info button
Completed service call
You should see your name, your login name, and your email address.
If you’re able to login as another user you can run the test with their identity to ensure you’re getting the expected results.
  • Open another browser window in incognito mode
  • Login as a different user
  • Navigate to the SpoWebApi page in Site Pages library
  • Ensure the Web API is running in the debug mode
  • Go back to the browser and click the Get User Info button
Completed service call - different user
Now that we have a working Web API, let’s deploy it to an Azure Web App.

Create the Azure Web App

  • Navigate to https://portal.azure.com
  • Click the New button at the top of the left pane
  • Select the Web + Mobile category
  • Select Web App
New Web App
  • Set the App name to SpoWebApiHost<nnn> where nnn is any number you wish to use
    • The app name needs to be globally unique so we can’t all use the same name
  • Pick a subscription
    • The options here will depend on your environment
  • Create a new Resource Group with the same name as the App name
  • Pick an App Service plan
    • The options here will depend on your environment
  • Click the Create button to create the Azure Web App
  • Wait for the notification that the Web App has been created
  • Click the All resources category in the left pane
  • Find the newly created Web App
New Web App
  • Click on the Web App name to access its properties
Web App Properties
IMPORTANT: Check the App Service plan/pricing tier. If it does not say (Free) at the end you will be charged for the use of this Web App. Follow these steps if you want to use a free Web App.
  • Select the Scale up (App Service plan) category
  • Click F1 Free
  • Click the Select button
Free Web App
We are going to need some settings information to be able to deploy the Web API to the Azure Web App. There are several ways we can to this, in this example I’ll use Web Deploy.
  • Select the Overview category
  • Click Get publish profile in the toolbar at the top of the right pane
  • A file with the Web Deploy settings will be downloaded
Get publish profile

Deploy the Web API to Azure
  • Go back to Visual Studio
  • In the Solution Explorer right-click on the project and select Publish…
Publish
  • In the Publish Web dialog click Import
Import publish profile
  • Open the publish profile file downloaded from Azure
Web deploy settings
  • Click the Publish button to deploy the Web API to Azure
Publish complete

Update JavaScript Code to use Web API Hosted in Azure

  • Go back to Visual Studio
  • Open SpoWebApi.html
  • Set the value of config.endpoints.endPointApiUri to the URL of your Azure Web App
    • Make sure to use https
    • SSL is supported by all sites who’s domain ends in azurewebsites.net
  • Save the file and upload to the Site Assets library in SharePoint
    • Make sure to overwrite the existing file
Update service URL

Add Reply URL to Azure AD Application

  • Go back to the settings for the SpoWebApi Azure AD application
App settings
  • Select the Reply URLs category
  • Add the https URL to the Azure Web App to the list of reply URLs
  • Click the Save button in the toolbar
Update Reply URLs


Testing the Azure Hosted Web API

We’re now ready to test that the JavaScript in SharePoint can use the Web API deployed to Azure. You can use Fiddler or the browsers debugging tools to validate that the JavaScript code is using the Web API deployed to Azure.
  • Navigate back to the web part page we are using for testing
    • /SitePages/SpoWebApi.aspx
  • Refresh the page
  • Click the Get User Info button
Testing Azure deployed Web API
Boom! Now we have the JavaScript code in SharePoint Online using a custom Web API deployed to Azure.

Configuring Security Zones in Internet Explorer and Edge

There is a potential issue when using Internet Explorer or Edge. These browsers don’t share cookies across security zones and that prevents ADAL from being able to complete the authentication handshake.
When this is the case you can get errors like this one:
Internet Explorer
Basically, any site that uses ADAL.js needs to be in the same security zone as login.microsoftonline.com.
This may be a problem for users of SharePoint Online where it is common to have your SharePoint URLs added to either the Trusted Sites or Local Intranet zone.
  • <tenant name>.sharepoint.com
  • <tenant name>-my.sharepoint.com
In this scenario, you would need to add login.microsoftonline.com to the same zone as the SharePoint URLs to get this walkthrough to work.
However, that presents a different problem. What if you also use sites like foo.azurewebsites.net and www.acme.com that use ADAL,js? They will be in the Internet zone (which is the default zone) while login.microsoftonline.com is in Trusted Sites or Local Intranet. That means that the ADAL code used in these two sites will stop working correctly. And then what if you start using other sites that use ADAL.js? You could end up having to add dozens of sites to the Trusted Sites or Local Intranet zone. At some point things will become unmanageable.
Unfortunately I don’t have a quick and easy solution to this problem. During my research and my discussions with other MVPs I’ve come across a couple potential workarounds. I plan to explore these further and blog about my findings when I have something concrete.

What’s Left to Cover

Over time I’ll be expanding this post or writing other posts to cover the following topics:
  • Calling the Web API from a SharePoint-hosted Add-in
  • Making the Service Multi-tenant

Comments