Kirk Evans is a Microsoft Architect for the Azure Center of Excellence.
Introduction to SharePoint and Azure IaaS
Building SharePoint Apps with Windows Azure Platform as a Service
SharePoint Solutions and Architectures on Windows Azure Infrastructure Services
Understanding Authentication and Permissions with Apps for SharePoint and Office
This post will show how to create a custom Web API that calls the Office 365 APIs on behalf of a user.
How about that title?!? That’s pretty geeky.
I’ve been working with a customer who is interested in the new Office 365 APIs that were announced at SharePoint Conference. They are building multiple client applications that will consume their custom Web API endpoints. They did not want to reference the O365 APIs and perform all of the work from the client applications, but rather centralize that logic into a service that they control.
In this scenario, the user opens an application and logs in using their Azure Active Directory credentials. Once the user is authenticated, we obtain an access token used to call the custom Web API endpoint. The Web API endpoint, in turn, obtains an access token for O365 using the user’s token. This means when the O365 API is called, it is called using an app+user context on behalf of the currently logged in user. The current user must have permissions to write to the list, as does the app, and if one of them is missing permission then the operation fails.
Yeah, even after editing that paragraph several times over it’s hard to understand. Let’s walk through an example.
The code I am going to show is based on a sample posted by the Azure Active Directory team, Web API OnBehalfOf DotNet available on GitHub. I am not going to provide a code download for this post, I strongly urge you to go download their sample and become familiar with it. Instead of using the O365 API, that sample shows how to use the Graph API. This post details how to leverage some of the code in that example to call the O365 API. There are many things I omitted that you should spend time in that sample understanding.
There’s also a great post by Johan Danforth, Secure ASP.NET Web API with Windows Azure AD, that illustrates some of the same concepts that I show here. His example is well formatted and might help explain some of the settings that I am going to gloss over here. If you need help understanding the various parts (client ID, client secret, callback URL, app ID URI for the web API) that is a great resource as well.
The first part of this exercise requires that you manage your Office 365 tenant from the Azure Management Portal. A blog post with detailed instructions and an accompanying video are available at Using a existing Windows Azure AD Tenant with Windows Azure. I have an existing Office 365 tenant (https://kirke.sharepoint.com) that I log into with the domain “kirke.onmicrosoft.com”, and I want to add that account into Azure AD. To do this was as simple as logging into my Azure subscription using a Live ID and adding an existing directory in the Azure Management Portal.
Next, I log in using my Office 365 tenant administrator, and then the directory shows up in the Azure Management portal.
I can now open the directory and see all of my users from my O365 tenant.
Make sure you start In Private browsing before doing this, and it should work smoothly.
Create a new ASP.NET Application in Visual Studio 2013 and then choose Web API.
Click the button to Change Authentication. We’ll use organizational accounts in Azure AD to authenticate, using the same domain that our O365 tenancy was in.
Leave the default App ID URI value, which is your tenant + “/ListService”. When you click OK, you are prompted to sign in as a Global Administrator for the tenant. This is because Visual Studio is going to do several nice things for you, including registering the app and setting up the configuration settings for you.
Sign in, click OK, then click OK again, and the project is created. If you inspect the web.config, you’ll see that several settings were added for you.
Now go look at your Azure Active Directory tenant and click on the Applications tab. Notice that an application was added for you.
Click the ListService application to see its settings, then go to the Configure tab. In the section “keys”, create a new key:
Click Save, and the key is then displayed for you, and you can now copy its value.
Go back to web.config and add a new key “ida:AppKey” with the value of the key you just created. Add a key “ida:Resource” which points to your SharePoint Online tenant (note: this need not be the site collection you are accessing). Finally, add a key “ida:SiteURL” that points to your site collection.
The Web API has been registered with Azure AD, we now need to give it some permissions. In Azure AD, select your Web API application and choose Manage Manifest / Download Manifest.
We are prompted to download and save a .JSON file.
Save it, and then open it with Visual Studio 2013. If you have the Visual Studio 2013 Update 2 RC installed, you will be delighted to see color-coding while editing the JSON. There’s a property called appPermissions that we need to edit.
We’ll replace this with the following:
The result looks like this:
Save it, then upload it to Azure by going to Manage Manifest and Upload Manifest.
The last thing we need to do is to grant our Web API application permission to call the O365 APIs. In Azure AD, go to the Configure tab for your application. We will grant the Web API permission to “Read items in all site collections” and “Create or delete items and lists in all site collections”.
In the previous step, we added the appPermission claim “user_impersonation”. We will check for the user_impersonation scope claim, making sure it was registered for the Web API application in Azure AD. We need to perform this check every time an action is executed for our controller, so we create a filter. It doesn’t matter where you put the class, but I prefer to put this in a new folder named Filters in my Web API project.
To use the filter, we apply it to the top of our controller class. Yes, I am going to be lazy and just use the ValuesController class that Visual Studio generates.
We’re now ready to start doing the Azure Active Directory coding. Right-click the Web API project and choose Manage NuGet Packages. Search for “adal” to find the Active Directory Authentication Library, and use the drop-down that says “Include Prerelease” to find version 2.6.1-alpha (prerelease).
Install and accept the EULA. Next, add a class “SharePointOnlineRepository” to the Web API project. We borrow some code from the GitHub project mentioned previously to obtain the user token. This class will use the SharePoint REST API, passing an access token in the header. Here I give you two different ways to accomplish this: using XML (as in the GetAnnouncements method) and JSON (as in the AddAnnouncement method).
We will accept an HTTP POST from the caller to insert new announcements into the list. The easiest way to do model this is to create a class with the values that will be posted. I add a new class “Announcement” to the Models folder in my Web API project.
Now that we have the repository class that interacts with SharePoint, and we have our new model class, we update the ValuesController class. The Get action will simply return the entire string of data back from SharePoint. The Post action will read data from the form data, add an announcement to SharePoint, and then return the SPList data for the item. I leave it as an exercise to the reader to do something more meaningful with the data such as deserializing it into a model object.
In Azure AD, go to your directory and add a new application. Choose “Add an application my organization is developing”.
Next, choose “Native Client Application” and give it a name.
In the next screen, we provide a URL. Any URL is fine here. If we were building a Windows 8 application, then we’d provide the ms-app URL obtained from the store. To save some digital ink, this is why we chose a WPF application… any URL will do here as long as it is a valid URL (but it does not to have to actually resolve to anything). I used “https://OnBehalfOf/Client".
In the Update Your Code section, you see that a client ID was generated for you. Copy that, you’ll need it later.
In the Configure Access to Web APIs in Other Applications section, click Configure It Now. Under Permission to Other Applications, find the ListService that we modified the manifest for earlier in this process. When we modified the manifest, we modified the appPermissions section and provided a new permission. We now use this to grant the client application permission to the Web API. Choose ListService, and choose “Have full access to the To Do List service”.
Notice that we are configuring the permission for the client application here, and the only permission it needs is to access our Web API. We do not have to grant the client application access to O365 because it never talks directly to O365… it only communicates with our custom Web API.
And a reminder to self… you can change the text of the permission by downloading the manifest, editing the appPermission section with the proper text, and uploading it again. Now make sure to click Save!
Create a new WPF application. This is going to be the easiest way to get started as it is the most forgiving in terms of callback URL. If we used a Windows 8 app, we’d need to register it with the store and obtain the app ID (the one that starts with ms-app). For now, a WPF application is fine.
Add the Active Directory Authentication Library pre-release version 2.6.1-alpha NuGet package.
The UI for the application is simple, two buttons and a text box.
The code behind the XAML is what’s interesting. I am using the HttpClient classes to call our custom Web API. Note how we add the form data to the request using a Dictionary. I could have moved the settings to app.config instead of hard-coding everything in the GetAuthHeader method, I leave that as an exercise to the reader as well. The GitHub sample (link at the For More Information section of this post) does a better job of putting things in app.config, I’d suggest using that as a baseline.
This is a lot of configuration, and a lot of code. There are a lot of opportunities for errors here, so we’re going to cross our fingers, set the start-up projects, and run the application.
We run the application and click the button. A login screen shows itself (this is a good thing!)
I wait for a little bit and… darn it, I see this screen.
That’s not right, I should see the Announcements list data. I fire up Fiddler and watch the traffic. Looking through the requests, I notice that one of them does not have an access token value.
I double-check the code, yep… we’re providing it. What did I miss? I debug through the code and see that I do not get an access token. Hmm… maybe permissions? Let’s go double-check the Web API permissions in Azure AD.
Yep… that’s the problem. Earlier in the post I told you to add two permissions for Office 365 SharePoint Online, but apparently I forgot to hit Save. Do this step again, and this time, hit Save!
Run the application again, this time we see a different response… we get the items from our Announcements list!
We then try our operation to write an announcement to the list, and it works. The UI is horrible, but serves my purpose for now.
We then check the Announcements list in SharePoint and see the new announcement. Notice that the item was added by the Web API application on behalf of the current user!
Think about how incredibly cool that is. We just used constrained delegation to a downstream resource, all using OAuth.
Using a existing Windows Azure AD Tenant with Windows Azure
Web API OnBehalfOf DotNet
Secure ASP.NET Web API with Windows Azure AD
Awesome article Kirk! Is there any documentation for the Manifest schema? Trying to get a little deeper into the permissions, and what is possible. Any help or pointers appreciated.
Thanks, Pete. About the only documentation available at the moment is at msdn.microsoft.com/.../dn132599.aspx. What you see is pretty much the set of options at the moment. See github.com/AzureADSamples for more samples that demonstrate the capabilities.
Thanks Kirk, I will look into the links!
Is it possible that I get access to the Mails, Files, Folders of a user using Admin credentials? Let's just say it is one form of impersonation where the Admin plays the role of a user. Is this achievable?
I have changed my manifest, run Powershell commands for impersonation assignment, but nothing helps.
The REST APIs give changelogs only but when I try to fetch the Item that is changed it says, "Item does not exist. It may have been deleted by another user."
But the item is there, so I guess it is purely an access issue. If it is, then it is strange because what good is the Admin account then if it can't monitor such information!