Walkthrough: Building a Custom Web API for use with SharePoint Online

Published on Author Rob Windsor

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.



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.



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 the Visual Studio Project created in this post.


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


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)

            HttpConfiguration config = new HttpConfiguration();


        public void ConfigureAuth(IAppBuilder app)
            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.

    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
public async Task<IEnumerable<string>> GetUserInfo(string sharePointUrl)
    var results = new List<string>();

        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;

    catch (Exception ex)

    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
        <td style="vertical-align:top">
            <input id="consentButton" type="button" value="Consent" />
            <br />
            <input id="getUserInfoButton" type="button" value="Get User Info" />
        <td style="width:20px">
        <td style="vertical-align:top">
            <div id="message">

<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 () {

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

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


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 +

  • 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");
    } else {
        var user = authContext.getCachedUser();
        if (!user) {
        } else {
            console.log("Cached user");

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) {
            else {
    } else {

    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");
                jQuery.each(response, function (index, value) {
            call2.fail(function (error) {
                var message = jQuery("#message");
    call.fail(function (error) {
        var message = jQuery("#message");

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…


  • 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

41 Responses to Walkthrough: Building a Custom Web API for use with SharePoint Online

  1. Hi Rob,

    Thank you so much for effort to write above detail blog post about ADAL with SharePoint Online.

    A quick question – Is it possible for all users to automatically grant (Azure AD) permissions when the logged in to SharePoint Online portal? For example if i wanted to call Graph API for current Logged In user, does user need to click on consent to be able to access the data? Can this be done automatically?

    • The permissions requested by the application can be granted by an administrator for all users. The standard way to do this is to use admin consent which is what I show in the blog post. Notice the message in the consent dialog which states, “If you agree, this app will have access to the specified resources for all users in your organization. No one else will be prompted”. The other option is to grant the permissions using the Grant Permissions button in the Azure Portal. This button is in the command bar in the Required Permissions tab for the app.

  2. consider this use case:
    In a university scholarship application,
    1. student submits her application.
    2. consequently, the college admin can view the application and update some of the fields (not the same field as student)
    3. the grant admin, then approves/rejects that record. the approver can not update any other field than the Approval field. once the approval/rejection occurs, the record status will be appropriately updated, along with datetime.
    4. Admissions records admin can delete the entire row, but can not update or approve the record.

    how can such field level authorization be accomplished using Azure Claims (Roles)?

    • I don’t see how this question relates to the subject of this blog post. I suggest you post the question to SharePoint StackExchange (sharepoint.stackexchange.com) or StackOverflow (stackoverflow.com).

      • I was able to extend what you have done here, to include an authorization module inside the controller code. The code looks at a SharePoint list first which validates which token belongs to what profile and which profile is authorized to CRUD what field.

        So my question was a mere extension of what you have done here and putting it to a real life use, where permissions could require a bit complex logic. That’s all.

        Thanks for writing up this piece. It was awesome help to me.

  3. Thanks for the detailed post Rob. We followed all the steps exactly however we get an error on authContext.login(); method of JavaScript. Not sure what is causing this error. Any help regarding this would be appreciated.

    You can’t access this application
    TestWebAppJS needs permission to access resources in your organization that only an admin can grant. Please ask an admin to grant permission to this app before you can use it.
    Have an admin account? Sign in with that account

    • It sounds like you forgot to grant the permissions requested by the app. Please see the section entitled “Update Code to Grant Permissions Requested by Azure AD Application” above.

  4. Hi Rob,

    Actually while granting permission in Azure Read User Profile option is showing NO for my app.Is that fine to continue?
    while creating new API in visual studio which credentials we have to use?
    Microsoft credential or the sharepoint site credential which we are using to connect to sharepoint site?
    Because am getting error after deploying the app in sharepoint page saying
    The reply address ‘Site page url’ does not match the reply addresses configured for the application: ‘clientid’. More details: not specified

    Please advice.


    • When you are requesting permissions the No icon beside the permission name indicates that the permission does not require administrator approval. It does not indicate that the permission is not available to use.

      In the example I presented the credentials of the currently logged-in user are used to authenticate with SharePoint and with Azure Active Directory.

      When configuring the Reply URL you can enter the full URL to the page(s) that use your custom Web API. For example: contoso.sharepoint.com/sites/marketing/SitePages/MyPage.aspx. You can also use a wildcard (asterisk) so that any page in the site or site collection can use your custom Web API. For example: contoso.sharepoint.com/sites/marketing/*

  5. Hi Rob,

    Do we need Azure subscription to create the Azure app using Azure active directory?
    Please give a detailed explanation on this .


    • Your Office 365 tenant gives you access to Azure Active Directory configuration. You do not need a separate Azure subscription.

  6. Hi Rob,

    I am trying to follow your steps above to create a web api that can be called from a SharePoint workflow. In theory(or to me) it seems that all the steps should just work. But I have been struggling.

    I have at the moment created the web api project and have registered it in Azure AD. What else is required. Any guidance will be very helpful.


  7. Excellent post, With your clear instructions i was able create a solution to get/edit SharePoint elements from CRM. Many thanks

  8. I have problem Web API calls when we have [Authorize] at controller, i am getting access denied. Any idea on how to fix it?


  9. I want to use this to call Azure AD registered web API from SharePoint 2013 on-premises web parts and CEWP using adal.js. Is this possible? Sharepoint is registered with ADFS.

  10. Thanks for your example.
    It has been very useful for me.

    I’ve a question: I want to perform the same operation within a Visual Studio SharePoint Online workflow.
    I tried to understand the http calls


    in order to reproduce the ADAL behavior within HTTP Send Workflow Activity, but with no luck, because (it seams) workflow is not authenticated.

    Do you have a solution for this problem?
    I can’t find nothing about this on the network.
    I’m the only developer who wanto to invoke external Web API service from SharePoint Workflows^

    Thanks a lot,

    • I’m sorry but I don’t know the answer to your question. I haven’t build a workflow in Visual Studio in quite some time. I’ve never built one for SharePoint Online.

      I suggest you ask this question on the SharePoint StackExchange forum: https://sharepoint.stackexchange.com/

  11. Not doing this in .NET – is it possible to know what API calls are being made in order to exchange the users JWT for a service token? Specifically what request is made for “authContext.AcquireTokenAsync”??

    internal async static Task 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;

  12. Hi Rob,

    I used the demo project you provided and I am getting AdalServiceException when executing GetSharePointAccessToken method. I have already updated web.config to use my tenant and clientid. I even tried getting the latest version of all packages using Nuget, but still the same error. Do you know what is causing this error?

    Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: AADSTS240002: Input id_token cannot be used as ‘urn:ietf:params:oauth:grant-type:jwt-bearer’ grant.

    • Microsoft made a change to the token redemption effective May 1, 2018. That is what’s causing the error. At this point I don’t have a workaround. I’m going to investigate and update the post when I have more information.

      • In case of access token on client side was got using ADAL.JS or similar library or approach which uses “grant type” equals to “implicit”, this access token won’t be exchanged anymore. Instead you need to retrieve it on client side using “grant type” equals to “Authorization Code” (see “Authorize access to Azure Active Directory web applications using the OAuth 2.0 code grant flow” article on “docs.microsoft.com”).

  13. Hi Rob

    I have followed the steps exactly what you mentioned in this blog.
    When i hit api URL(localhost) with Authorization header and token using postman chrome extension, i am getting the response.
    But from from the test page, by clicking the Get User Info button , i am getting the below error in the console..
    could you please me on this…
    Error details:

    Failed to load https://localhost:44330/api/test/getuserinfo?sharePointUrl=https%3A%2F%2Fcompanydev.sharepoint.com%2Fsites%2FSPDevSite: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://companydev.sharepoint.com’ is therefore not allowed access. The response had HTTP status code 400.

    Thanks in advance

    • That response indicates that you did not enable CORS. Please see the section entitled Enable Cross-origin Resource Sharing and confirm that you followed the steps there.

  14. Hi Rob,

    Im having a problem when i install the packages. seems like the latest packages has some problems.

    Install-Package : Unable to resolve dependencies. ‘System.IdentityModel.Tokens.Jwt 5.2.4’ is not compatible with ‘SharePointPnPCoreOnline 2.27.1806 constraint:
    System.IdentityModel.Tokens.Jwt (>= 4.0.0 && < 5.0.0)'.

    • Make sure you’ve updated Visual Studio to use the most recent Nuget Package Manager. Go to Tools -> Extensions and updates, and then click Updates on the left side of the dialog.

        • There’s a solution to the package issue. You need to install a specific version of the System.IdentityModel.Tokens.Jwt package after installing Microsoft.Owin.Security.ActiveDirectory. This will address the version incompatibility.

          Install-Package System.IdentityModel.Tokens.Jwt -Version

          However there is another issue with the solution described in this blog post. One for which I do not currently have a workaround. Please see the update I added to the beginning of the post for more information.

          • Hi Rob

            I’ve already clicked the consent button but when i click the get user button it is saying that “Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: AADSTS65001: The user or administrator has not consented to use the application with ID “

  15. Hi Rob,

    I am getting an error in GetSharePointAccessToken method Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: AADSTS240002: Input id_token cannot be used as ‘urn:ietf:params:oauth:grant-type:jwt-bearer’ grant.

    Above error i am getting when passing id_token but when i pass access_token from adal js it works fine . I have set oauth2AllowImplicitFlow to true in manifest also. Can you please let me know if i am missing something

    • Microsoft made a change to the token redemption effective May 1, 2018. That is what’s causing the error. At this point I don’t have a workaround. I’m going to investigate and update the post when I have more information.

  16. Hi Thank you so much for the detailed blog with video.

    I am also have Erro “Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: AADSTS240002: Input id_token cannot be used as ‘urn:ietf:params:oauth:grant-type:jwt-bearer’ grant.”

    I also understood its beacuase of “Microsoft made a change to the token redemption”. But it would be great full if you can find fix or at least help me think through to solve the problem.

  17. hi your contact us page is not working….

    Mixed Content: The page at ‘https://blogs.msmvps.com/windsor/contact-me/’ was loaded over HTTPS,

  18. Issues related to “AADSTS240002” is appearing because same application identity is used in front-end and back-end app because Azure AD does not allows to use the same Id token to get another access token.. You could find more details on resolving this issue at the links below.