Building Daemon or Service Apps with Office 365 Mail, Calendar, and Contacts APIs (OAuth2 client credential flow)

Device and Web Server applications usually require a user to sign-on and consent to allow the application to access and work with their information in Office 365. The underlying protocol to gain access is based on the OAuth2 authorization code grant flow. I described this a while ago in one of my earlier blogs Using OAuth2 to access Calendar, Contact and Mail API in Office 365 Exchange Online. As part of this OAuth2 flow, the application gets an access token/refresh token pair that represents a delegation given to the application by a specific individual user for a set of permissions. Essentially before the application can access data for a user, it has to get an access token/refresh token for each user, and to get those the user has to sign-on to the application at least once.

 

There are however a category of applications where this is not desirable or possible. These applications usually run in the background as a daemon app or service and need access without the user having to sign-on. OAuth2 provides a different flow for these types of applications, called the client credential grant flow. You can read more about this flow in the AAD Authentication Protocol documentation here. We're happy to announce that Office 365 now supports this flow to gain access to the Office 365 Calendar, Contacts and Mail APIs.

 

When using this flow, the application presents its client credentials to the OAuth2 token issuing endpoint, and in return gets an access token that represents the application itself without any user information. This is sometime also called an "App-Only" token. There is no need for the application to get a refresh token. When the access token expires, it simply goes back to the OAuth2 token issuing endpoint to get a new one. Also, since there is no user information in the token, the app must specify the user within the API call when using this "App-Only" token.

 

Note: Access token lifetime is currently 60 minutes. This applies to all access tokens in Office 365, regardless which OAuth2 flow is used.

 

 

Defining permissions

 

The first step to configure your application to use "App-Only" tokens is to define what permissions your application needs. Just like permissions for authorization code grant flow apps, these permissions are defined with the application registration in the Microsoft Azure Management Portal for Azure Active Directory (AAD). While the permissions for the authorization code grant flow are called "Delegated Permissions" (because a user delegates those permission to the app), the permissions for the client credential flow are called "Application permissions" (because those permissions are directly assigned to the application).

 

Below is a screen shot of the AAD Application Registration permissions section:

Machine generated alternative text: permissions to other applications Delegated Permissions: 1 Delegated Permissions: O Windows Azure Active Directory Office 365 Exchange Online Add application Application Permissions: 1 Application Permissions: 3

 

Note: When creating the application, the application must be created as a "Web Application and/or Web API" within the AAD application management portal.

 

 

Granting consent and app authentication strength

 

Now that application permissions are defined within the application registration, the application can ask for consent to be available in another Office 365 organization. Application Permissions must be consented by a tenant administrator (global administrator) of an Office 365 organization. This is because these applications are quite powerful in terms of what data they can access in the Office 365 organization. For example, a service application with the Mail.Read permission that acquires access tokens via client credential flow can read mail in any mailbox in the Office 365 organization.

 

Because of the broad access these kinds of apps enjoy, there is an additional requirement for the app to successfully obtain an access token. Instead of using a client ID and client secret, the app must use an X.509 certificate with a public/private key pair. Usage of simple symmetric keys are not allowed, and while the application could get an access token using a symmetric key, the API will return an access denied error with such an access token. Self-issued X.509 certificates are accepted, but the application must register the public X.509 certificate in AAD with the application definition. The app then maintains the private key and uses it to assert its identity to the token issuing endpoint.

 

The application implements the consent flow in a similar way as the authorization code grant flow by sending a request to the OAuth2 authorize endpoint. However when the authorize endpoint redirects back with an authorization code to the application, the code can be happily ignored and thrown away. For getting tokens only the client credentials are necessary. Most applications build an experience such as "Sign-up my Organization" to accomplish the initial one-time consent.

 

The key take away of this section is that one-time consent is part of a daemon app configuration. Once the consent is given, the service or daemon app can get access tokens and start calling the Office 365 Rest APIs.

 

 

Revoking consent

 

Consent to service applications can be revoked just like for other applications that are installed by a tenant administrator of the Office 365 organization. The administrator can either go to the AAD Azure Management Portal, find the application in the application view, select and delete it, or alternatively the administrator can use Azure AD PowerShell to remove the app via the "Remove-MSOLServicePrincipal" cmdlet.

 

 

Acquiring an access token with client credential flow

 

There is one important aspect when asking for app-only tokens: a tenant specific token issuing endpoint must be used. In other words, the "common" OAuth2 token issuing endpoint https://login.windows.net/common/oauth2/token cannot be used in this flow and will return an error.

 

The application can get the tenant specific endpoint quite easily. During the consent, when the authorize endpoint is hit and a code is delivered in the redirect to the application, an ID token can be requested together with the code. This ID token contains the tenant Id as "tid" claim-type. As the ID token is a JSON Web Token it is fairly simple to parse.

 

Getting an Initial ID token with Consent

 

Below is an example of how to trigger consent using an OpenID Connect Hybrid Flow (see OpenID spec here) request. This request will provide your application with the consent flow and redirect back with the code and ID token in a post request. Your application can ignore the code and get the ID token from the received form data (see OpenID spec on form post response mode)

 

GET https://login.windows.net/common/oauth2/authorize?state=e82ea723-7112-472c-94d4-6e66c0ca52b6&response_type=code+id_token&scope=openid&nonce=c328d2df-43d1-4e4d-a884-7cfb492beadc&client_id=0308CDD9-874D-4F87-85E0-A0DA7E05F999&redirect_uri=https:%2f%2flocalhost:44304%2fHome%2f&resource=https:%2f%2fgraph.ppe.windows.net%2f&prompt=admin_consent&response_mode=form_post HTTP/1.1

 

In ASP.Net you could simply retrieve the ID token via Request.Form["id_token"] on your redirect page. You can find the tenantId in the "tid" claim within the JWT id_token.

 

 

 

In the example below I used Azure Active Directory Client library (ADAL) to acquire an "app-only" access token via client credential flow. My application maintains the private key in a well-protected directory on my web server as a PFX file. However it is better to maintain key material in a more secure storage such as the Windows certificate storage of the computer account.

 

Code snippet: Getting an "App-Only" token

 

           // need to address the tenant specific authority/token issuing endpoint

          // https://login.windows.net/<tenant-id>/oauth2/authorize

          // retrieved tenantID from ID token for the app during consent flow (authorize flow)

    string authority = appConfig.AuthorizationUri.Replace("common", tenantId);

               AuthenticationContext authenticationContext = new AuthenticationContext(

                   authority,

                   false);

 

               string certfile = Server.MapPath(appConfig.ClientCertificatePfx);

 

                X509Certificate2 cert = new X509Certificate2(

                    certfile,

                    appConfig.ClientCertificatePfxPassword, // password for the cert file containing private key

                    X509KeyStorageFlags.MachineKeySet);

 

                ClientAssertionCertificate cac = new ClientAssertionCertificate(

                    appConfig.ClientId, cert);

                   

                var authenticationResult = await authenticationContext.AcquireTokenAsync(

                    resource, // always https://outlook.office365.com for Mail, Calendar, Contacts API

                    cac);

 

                return authenticationResult.AccessToken;

 

 

Note: The complete sample of a web app using client credential flow is available in GitHub on

· https://github.com/mattleib/o365api-as-apponly-webapp

 

 

 

Configuring a X.509 public cert for your application

 

One last challenge is how to actually configure a X.509 public certificate with the application definition. Unfortunately X.509 certificates are not exposed as a UI element in the AAD application management portal. The applications public certificates need to be managed through the manifest.

 

Step 0: (If you do not have an X.509 certificate already) Create a self-issued certificate

You can easily generate a self-issued certificate with the makecert.exe tool.

 

1. From the command line, run: makecert -r -pe -n "CN=MyCompanyName MyAppName Cert" -b 12/15/2014 -e 12/15/2016 -ss my -len 2048

2. Open the Certificates MMC snap-in and connect to your user account. Find the new certificate in the Personal folder and export it to a base64-encoded CER file.

 

Note: Make sure the key length is at least 2048 when generating the X.509 certificate. Shorter key length are not accepted as valid keys.

 

 

Step 1: Get the base64 encoded cert value and thumbprint from a .cer X509 public cert file using PowerShell

 

Note: The instructions below show using Windows PowerShell to get properties of a x.509 certificate. Other platforms provide similar tools to retrieve properties of certificates.

 

$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2

$cer.Import("mycer.cer")

$bin = $cer.GetRawCertData()

$base64Value = [System.Convert]::ToBase64String($bin)

 

$bin = $cer.GetCertHash()

$base64Thumbprint = [System.Convert]::ToBase64String($bin)

 

$keyid = [System.Guid]::NewGuid().ToString()

 

Store the values for $base64Thumbprint, $base64Value and $keyid, to be used in the next step.

Step 2: Upload cert through the manifest file

 

3. Log in to the Azure Management Portal (https://manage.windowsazure.com)

4. Go to the AAD snap-in and there navigate to the application that you want to configure with an X.509 certificate

5. Download the application manifest file through the Azure Management Portal

Machine generated alternative text: VIEW Manifest MANAGE MANIFEST UPLOAD Lca

 

6. Replace the empty “KeyCredentials”: [], property with the following JSON. NOTE: The KeyCredentials complex type is documented here: https://msdn.microsoft.com/en-us/library/azure/dn151681.aspx

 

  "keyCredentials": [

    {

      "customKeyIdentifier": "$base64Thumbprint_from_above",

      "keyId": "$keyid_from_above",

      "type": "AsymmetricX509Cert",

      "usage": "Verify",

      "value": "$base64Value_from_above"

     }

   ],

 

e.g.

 

  "keyCredentials": [

    {

      "customKeyIdentifier": "ieF43L8nkyw/PEHjWvj+PkWebXk=",

      "keyId": "2d6d849e-3e9e-46cd-b5ed-0f9e30d078cc",

      "type": "AsymmetricX509Cert",

      "usage": "Verify",

      "value": "MIICWjCCAgSgAwIBA***omitted for brevity***qoD4dmgJqZmXDfFyQ"

    }

  ],

 

7. Save the change to the application manifest file.

8. Upload the edited application manifest file through the Azure Management Portal.

9. Optional: Download the manifest again, and see your X.509 cert is present on the application.

 

Note:   KeyCredentials is a collection, so it’s totally possible to upload multiple X.509 certificates for rollover scenarios, or delete certs for compromise scenarios.

 

 

Must-Do Application Developing practices calling Mail, Calendar and Contacts API

 

Unrelated to OAuth2, there are three HTTP request headers you should always include when making requests to the Office365 APIs. This is the "User-Agent" the "client-request-id" and "Date". For User-Agent you can follow RFC 2616 which basically describes it as {ProductName}/{ProductVersion} [{comment}]. For "client-request-id" your app should create a new GUID for each request. When the request fails "return-client-request-id" returns the GUID that was submitted as "client-request-id" with the request. It is highly recommended that you persist the failed request together with client-request-id and all HTTP response headers in some application log. The "Date" request header signals the date and time that the message was sent. It should follow the "HTTP-date" format as defined by RFC 2616)}. If you ever need help troubleshooting your application with the Office 365 APIs, this will be pave the route to a fast and successful resolution of the problem.

 

 

Summary

 

To sum up:

· With the availability of the OAuth2 client credential flow it is now possible to build daemon or service apps in addition to device and web server applications.

· The installation or bootstrapping of those service apps is very similar to web server apps and requires an administrator of an Office 365 organization to consent.

· Unlike web server apps, which can use symmetric keys, a service app must use X.509 certificates as authentication strength to acquire access tokens and successfully call the Mail, Calendar and Contacts APIs.

· Service apps have the requested access level to all mailboxes in the Office 365 organization the administrator has consented to.

· Service apps can be revoked by an administrator of the Office 365 organization in the same way like web server apps.

 

As always, thank you for reading and feedback, questions, etc. are very much appreciated!

 

Please use Stack Overflow for questions, and be sure to tag your post with "office365".

 

Matthias Leibmann

 

 

References

OAuth2 client credential flow:  https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx

OAuth2 client libraries:  https://msdn.microsoft.com/en-us/library/azure/dn151135.aspx

AAD on GitHub: https://github.com/AzureADSamples

OAuth Sandbox:  https://oauthplay.azurewebsites.net/

API Sandbox:  https://apisandbox.msdn.microsoft.com/

Platform Overview:  https://msdn.microsoft.com/en-us/office/office365/howto/platform-development-overview

Apps for Office:  https://msdn.microsoft.com/en-us/library/office/fp161507(v=office.15).aspx

Office on Github:  https://github.com/OfficeDev

Sample Web App to this Blog: https://github.com/mattleib/o365api-as-apponly-webapp