O365 OneNote API in Web Application

Microsoft recently announced OneNote API for O365 notebooks. I decided to give it a try but most of the examples/documentation I found was for native client applications which is obviously not working for web apps. After spending a lot of time on it and getting help from Microsoft OneNote API team, I was able to resolve it. Below are the steps you need to follow to access your notebooks using OneNote API for O365. Please note that the below code is for single tenant app and OneNote API are in preview.

  1. Register your application in Azure AD: We need to register the app in Azure AD as well as give OneNote API proper permissions. The steps are as follows:
    • Go to Azure AD related to your O365 account

      Azure AD
    • Navigate to Active Directory and click on Applications tab. Add a new application and select “Add an application my organization is developing”NewApplication
    • Give proper name to the app and select Web Application/OR Web API optionWebAppORWebAPI
    • Provide a Sign-On URL and a unique App ID URI.
      • Sign-On URL: https://localhost:44327/
      • APP ID URI: https://<tenantname>.sharepoint.com/SingleTenantOneNoteAPIDemo, replace <tenantname> with the name of your Office365 tenant.
    • Now the new application is ready. Go to “Configure” tab and at the bottom you will get the option to manage permissions. Azure AD already has “Enable sign-on and read user’s profiles” permission.ExistingPermissions
    • Click on “Add application” and select “OneNote” and give appropriate permission.OneNotePermission
    • In “Keys” area select 2 years. You will get a message that “The Key value will be displayed after you save it”. Make sure to copy this key value as this is client secret for your application. Also, copy the client id.Keys/ClientSecret
    • Now go to “Reply URL” and add a new one which will correspond to the address in which you will write the code to handle the return flow.ReplyURL
    • Click on “Save” and DON’T FORGET TO COPY KEY VALUE AND CLIENT ID. That’s it, your application is configured in Azure AD now.
  2. Access OneNote API in code
    • Open Visual Studio -> Click on “New Project” -> Select ASP.NET Web Application and give a proper name to your application. Click on OK.
    • In the next screen, select “Change Authentication” and click on “Organization Accounts”. Provide domain name of your O365 account as well as APP ID URI which you used while configuring the app in Azure AD.ChnageAuthentication
    • Click on “OK” and you will be asked to login to the O365 account. Login using the account you used while creating the entry in Azure AD and hit “OK”.
    • Your project will be set up. Open the web.config and you will notice tenant related entries. In the “appsettings” section enter the other details like client id and app key. You can directly use these in code or add them in web.config. I usually prefer web.configweb.config
    • Add nuget for ADAL in your project.ADAL
    • The code for accessing the OneNote API is given below. Please note, that here I am using AcquireTokenByAuthorizationCodeAsync method for authorization.

This is just basic code which will return all your notebooks. You can also create/edit the notebooks based on the permissions you provided to the application in Azure AD. You can change these permissions later.

My next step is to achieve this in multi-tenant app. As apparent from the code, the authorization URL contains the tenant URL which is not possible in multi-tenant apps. I will soon update the post for this scenario.

Lastly, I would like to thank Microsoft OneNote API team for their help while I was stuck in these issues. Apart from that, please go through Using ADAL’s AcquireTokenBy AuthorizationCode to Call a Web API From a Web App post, it explains the process in more detail.

Hope this helps. Happy coding!

Update Aug 4, 2015: For using OneNote API in multi-tenant app, go through WebApp-WebAPI-MultiTenant-OpenIdConnect-DotNet example. This is for graph API but works as it is for OneNote API too. A very good and self-explanatory sample.

Unique Identifier for O365

Multi-tenant apps are a very common scenario in real world. Whenever we create an app, we mostly target it to multiple tenants for wider reach. There are some cases in which we want to save our app data in custom database instead of app web or host web. These scenarios are very frequent and at that time we need a unique identifier to differentiate among tenants. So my search started for a pre-existing identifier of O365 and ended right at TokenHelper.cs file.
Yes, Microsoft has already provided a code for it, I just didn’t realize it’s a unique identifier. 🙂

In TokenHelper.cs there is a public method GetRealmFromTargetUrl(Uri targetApplicationUri) that takes URI of the target sharepoint site as its parameter. This method actually return a string representation of the realm GUID. According to msdn,

 Realm is unique to each tenant in Office 365 or to each SharePoint farm on-premises. It is possible to discover the realm at run time. So, it is not necessary to cache this information between requests, but it will cost you an extra round trip to SharePoint each time you want to look it up. If you use code similar toTokenHelper.GetRealmFromTargetUrl with the site URL, and cache the result per site (or even per site and per user), you can use this later without making the extra call.

For more details, visit this link.

Therefore, above method return a unique id for tenant. You can pass host web uri in it. This id can be saved in database for further use and can even be cached.

Source code for this method is given below. This is copy pasted from TokenHelper.cs class which Visual Studio auto-generates on creating provider hosted app.

 


public static string GetRealmFromTargetUrl(Uri targetApplicationUri)
{
WebRequest request = WebRequest.Create(targetApplicationUri + "/_vti_bin/client.svc");
request.Headers.Add("Authorization: Bearer ");

try
{
using (request.GetResponse())
{
}
}
catch (WebException e)
{
if (e.Response == null)
{
return null;
}

string bearerResponseHeader = e.Response.Headers["WWW-Authenticate"];
if (string.IsNullOrEmpty(bearerResponseHeader))
{
return null;
}

const string bearer = "Bearer realm=\"";
int bearerIndex = bearerResponseHeader.IndexOf(bearer, StringComparison.Ordinal);
if (bearerIndex < 0)
{
return null;
}

int realmIndex = bearerIndex + bearer.Length;

if (bearerResponseHeader.Length >= realmIndex + 36)
{
string targetRealm = bearerResponseHeader.Substring(realmIndex, 36);

Guid realmGuid;

if (Guid.TryParse(targetRealm, out realmGuid))
{
return targetRealm;
}
}
}
return null;
}

 

Hope this helps. 🙂