Quantcast
Channel: Outlook dev blog
Viewing all 108 articles
Browse latest View live

New sample: accessing OneDrive from a mail app

0
0

The Office 365 REST APIs (Mail, Calendar, Contacts, and Files) provide an easy way to integrate Office 365 features into your app. But what if your app is a mail app? With a little creativity, it's not much different than any other app. Let's take a look at how you can use the Files API to access OneDrive from within a mail app.

To be able to handle the redirect back from the OAuth authorization endpoint, most of the major processing happens in a Web API hosted on the same server as the mail app. The mail app presents the OAuth sign-in and consent UI, but the Web API handles the redirect back from the OAuth endpoint and completes the flow. The Web API then uses the OAuth access token to access OneDrive via the Files API.

Backstory: If you attended the mail app deep dive at SharePoint Conference 2014 (or watched the video), you may remember Andrew Salamatov showing a mail app that saved an attachment from an email message to SharePoint. Andrew asked me to take his app and polish it to publish as a sample. His sample was the starting point for this one. The end result of my tinkering is now on GitHub.

What's it do?

The updated sample mail app activates in read forms for messages that have attachments. The user can authorize the app to access their OneDrive for Business, then click a button to save all file attachments to their OneDrive. The sample consists of two parts:

  • The mail app (Home.html, Home.js) – This part shows up in Outlook and Outlook Web App. It provides the UI for the app, initiates the OAuth2 consent follow to Azure Active Directory, and sends the attachment IDs to the WebAPI component of the sample.
  • The Web API (OAuthController.cs, GetAttachmentController.cs) – This part manages the OAuth2 response and caches the refresh token, uses Exchange Web Services to download attachment data, and uses the Files API to upload attachments to OneDrive for Business.

Things to note

  • The Web API component uses an in-memory cache to store user's refresh tokens for accessing OneDrive. This isn't a great approach for a real app, where you want that data to survive app restarts and be better protected. For a real app, storing these tokens in a secure database is recommended.
  • Because you have to open a new browser window to access the Azure AD logon site for OAuth, the UI for the "Authorize access" step is a little rough. Luckily this only has to happen once (until you restart the app!) since the refresh token will be cached.

Download the sample and check it out. Pull requests are welcome (bonus points if you fix my fugly UI!). Let me know what you think on Twitter (@JasonJohMSFT).


Guides for OAuth2 with Azure for Office 365 apps

0
0

As I started out learning about how to get an OAuth2 access token to use the Office 365 APIs, I created these "cheat-sheets" for myself. I realized that they might be helpful for others, so I've cleaned them up and posted them on GitHub. Right now I've got a guide for manually registering your app, and one for parsing  your access token to look for problems. Check them out here: https://github.com/jasonjoh/office365-azure-guides.

Feedback is welcome, either in comments here or on Twitter: @JasonJohMSFT.

 

Office 365 APIs and Python Part 1: OAuth2

0
0

Knowing absolutely nothing about Python (other than it is apparently named after Monty Python!), I've decided to build a Python web app that integrates with the Office 365 APIs, specifically, the Contacts APIs. For your amusement, I'll be chronicling my misadventures as I learn Python and figure out how to use it to call the Office 365 APIs. If you're new to Python, let's learn together! If you're a Python veteran, please feel free to send me pointers or correct my mistakes in the comments.

Getting Started

When I decided to do this, I knew that I wanted to do a web app. From what I gathered on http://www.python.org, I'd need a web development framework. I settled on Django, and as it turns out, that was a great choice. Django really made this project easy, even for a Python newbie. Their documentation is great and had pretty much everything I needed.

Installation

Installing Python and Django on my Windows 8.1 laptop was easy and painless. I followed the installation guide and installed Python 3.4.2 and Django 1.7.1. The only thing that I needed to do that wasn't in the guide was add my Python install directory (C:\Python34) and the Scripts subdirectory (C:\Python34\Scripts) to my PATH environment variable.

Finding my way

After I was set up, I dove right into this awesome Django tutorial. I went through it all the way to the end, and it was fun! It touched on a lot of the cool features of Django and I had a working app in a couple of hours. Not bad. If you're new to Django, I highly recommend this tutorial.

Once I had completed the tutorial, it was time to start coding my own app. I set the goal of getting the OAuth2 authorization code grant flow working. I figure if I can figure that part out, the rest should be easy. Following along with the steps I learned from the tutorial, I created a new Django project with the highly-imaginative name of "pythoncontacts".

django-admin.py startproject pythoncontacts

Implementing OAuth2

At this point, I stopped to consider how I wanted to handle authorization and access tokens. Django is very database-centric, with each project having its own database associated with it. Django also has a user login framework, with user accounts being created and stored in the project's database. So I decided to go with the approach of having a "Django user" on the site connect their Office 365 account. A user would visit the site, login with their site-specific username and password, then connect their Office 365 account via the OAuth2 flow. The app stores the connection information in the database and associates it with the Django account.

Models

So with that in mind, I came up with the following model (Django's "thing which lives in the database"):

class Office365Connection(models.Model):

# The local username (the one used to sign into the website)

username = models.CharField(max_length = 30)

# The user's Office 365 account email address

user_email = models.CharField(max_length = 254) #for RFC compliance

# The access token from Azure

access_token = models.TextField()

# The refresh token from Azure

refresh_token = models.TextField()

# The resource ID for Outlook services (usually https://outlook.office365.com/)

outlook_resource_id = models.URLField()

# The API endpoint for Outlook services (usually https://outlook.office365.com/api/v1.0)

outlook_api_endpoint = models.URLField()

When a user connects their Office 365 account, the app will create an Office365Connection record in the database for them. The next time the user visits the site, they won't have to go through the connection process again, as long as the refresh token is still valid.

Views

The next thing to do is come up with the views for the app. I wanted to keep things simple, so I started with the index view for the contacts app. This is what the user will see if they browse to http://hostname/contacts. The index view should show data from Office 365, so we want to make sure that a user is logged in so we have a Django account. One thing that the Django tutorial didn't cover was handling login/logout. However, I found this tutorial on effectivedjango.com which pointed me in the right direction. By using the @login_required decorator I was able to make the index view require a logged in user. It will redirect to the login page if the user's not already logged in. I also used their example login and logout pages.

The index view looks for an Office365Connection for the user in the database. If there is one, it checks to see if there's already an access token. If there isn't, it uses the refresh token to obtain one. It then passes the access token to the view template for display. If there isn't one, it passes None to the view template, which will then prompt the user to connect their Office 365 account.

That prompt will take users to the connect view. This one is pretty simple, it just generates the authorization URL (login.windows.net) and redirects the user there. The user signs in and is presented with the consent page. Once the user consents (or cancels), their browser is redirected to the app's third view, the authorize view.

The authorize view uses the authorization code provided in the redirect to obtain an access token for the Discovery Service. It then calls the Discovery Service to find the API endpoint for the Contacts API. It also stores the refresh token in the database. That way, we can request an access token for the Contacts API later, without having to re-prompt the user. Finally, the view redirects the user to the index view.

Communicating with the OAuth2 endpoints

To implement all the actual work of making HTTP request to the OAuth2 endpoints, I created a separate module called o365service. I found the requests module, which was exactly what I needed to send requests. The o365service module provides methods to do the initial authorization, do discovery, and get access tokens from refresh tokens.

Overall impressions

I'm really pleased with how this effort has gone so far. Django is super easy to pick up and seems very well suited for doing OAuth. I'm confident that adding calls to the Contacts API is going to be a breeze. I'll tackle adding that to this sample in part 2, so stay tuned!

Grab the sample on GitHub, and let me know what you think on Twitter (@JasonJohMSFT).

Office 365 APIs and Python Part 2: Contacts API

0
0

This post is the second part of a series of posts chronicling my (mis)adventures learning Python and Django, and using them to integrate with Office 365. This extends the sample app from Part 1 to add CRUD operations with the Contacts API.

When I left off in Part 1, I had a working Python/Django app that was able to retrieve an OAuth2 access token from Azure AD. Now it's time to take that token and do something interesting with it! In this post, I tackle calling the Contacts API.

Fixing the view

The index view from Part 1 just showed the access token in base64 format. That's not what we want, so we need to do a bit of work on the index view. The end goal is to show something along these lines:

 

Instead of just passing the access token, we want to pass a list of contacts. So the first thing was to define a DisplayContact class (in models.py) to represent the bits of the contacts that we're going to work with:

class DisplayContact:

given_name = ''

last_name = ''

mobile_phone = ''

email1_address = ''

email1_name = ''

email2_address = ''

email2_name = ''

email3_address = ''

email3_name = ''

id = ''

I also added a function (load_json) to that class to initialize itself from the JSON returned from the Contacts API, and another function (get_json) to generate the JSON that the Contacts API expects when creating or updating a contact.

Now the app needs to actually get that JSON. In o365service.py, I added a function called get_contacts. The version on GitHub has a lot of logging added, but here's a stripped down version without logging, so you can see how straightforward this really is:

def get_contacts(contact_endpoint, token, parameters):

headers = { 'Authorization' : 'Bearer {0}'.format(token),

'Accept' : 'application/json' }

get_contacts = '{0}/Me/Contacts'.format(contact_endpoint)

if (not parameters is None):

get_contacts = '{0}{1}'.format(get_contacts, parameters)

r = requests.get(get_contacts, headers = headers)

if (r.status_code == requests.codes.unauthorized):

return None

return r.json()

You may have noticed the "parameters" parameter to the function, and maybe you're wondering what that's for. That is an optional parameter used to specify OData query parameters. When I call this function from the index view in views.py, I actually pass "?$select=GivenName,Surname,MobilePhone1,EmailAddresses". If you compare with the fields in the DisplayContact class, you'll notice that they match up. By using the $select parameter, I'm asking the server only to send me the fields I care about, and reducing the size of the response payload.

Doing more than just read

So now the app can get all the contacts from the server and display them. Time to move on to the other operations: Create, Update, and Delete.

Delete

To implement delete, I created a "delete" action in views.py that takes the contact ID as a parameter. I then created a delete_contact function in o365service.py which is very similar to the get_contacts function. Using the Django template language I added a Delete button to each contact entry in the table, with a link to the "delete" action that includes the contact's ID as a parameter.

Edit

To implement edit, I followed a similar pattern. I created an "update" action in views.py, and an update_contact function in o365service.py. I also added an Edit button to each contact in the table. The one difference was that for edit, we need to actually display a new UI to show all the fields and allow the user to change them. For that, I added an "edit" view in views.py and a get_contact_by_id function in o365service.py. This view calls the get_contact_by_id method to load the contact, then renders the details.html template, which is basically an HTML form. The submit action on the form calls the "update" action.

Create

Create follows a similar pattern as Edit. There's a view called "new", and an action called "create", both in views.py. The "new" view renders the same template (details.html) as the "edit" view. Through the magic of the Django template language, the submit action for the form changes to the "create" action when the template is rendered with a null DisplayContact. The "create" action calls the create_contact function in o365service.py.

Modifying the EmailAddresses property

I found some interesting "quirks" when working with the EmailAddresses property. If you've looked at a JSON response, you may have noticed that the EmailAddresses property is an array of 3 Microsoft.OutlookServices.EmailAddress objects. These correspond to the "Email", "Email 2", and "Email 3" entries on Outlook Web App, in order. Empty entries will come back as "null" in the response. When you're writing to this property, the null entries matter!

When you're updating the property, you'll want to be careful to achieve the desired result. For example, if there currently exists just one entry, and you want to add a second entry, you need to update the property with the original entry, followed by the new entry, followed by null. If you just send the new entry, it will overwrite the old one!

However, when you're creating a new contact, you cannot include null entries. Doing so will result in a 500 HTTP error. This means you can't create a contact with "Email" blank and "Email 2" filled in, if that's your goal. You can however update the contact to move "Email" to "Email 2", if you really want to.

Get the code

The updated code is posted on GitHub. As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT).

Office 365 APIs and Python Part 3: Mail and Calendar API

0
0

This post is the third part of a series of posts chronicling my (mis)adventures learning Python and Django, and using them to integrate with Office 365. This extends the sample app from Part 2 to add CRUD operations with the Mail and Calendar APIs.

I had so much fun making part 1 and part 2 of this sample, I figured why not take it farther and implement a bit of all three of the Exchange-based APIs? In this post, I take on the Mail API and the Calendar API.

Paving the way

My plan was to extend the existing o365service module to include Mail and Calendar functions. But before I started, I decided to try to streamline the existing code a bit. To that end, I added a make_api_call function to the module that handles setting headers and sending the HTTP request. I updated all of the API-specific function like create_contact to use this function.

Being a good citizen

Since all of my API calls are now going through the make_api_call method, I figured this was a good time to add client instrumentation. Now each request's id is written to the debug log. If this was a production application and I needed help from Microsoft to figure out why calls were failing, this forensic data could help expedite a resolution, so it's definitely a recommended practice.

Enable Fiddler capture

Another small change I made while consolidating the HTTP code was to add a global flag to turn off SSL certificate validation. While developing part 1 and 2, I was never able to get Fiddler captures of the calls made by my app. With Fiddler running, I would always get SSL certificate validation errors. It turns out that the requests module will fail requests when the SSL certificate isn't valid. Of course that's a good thing, but it makes it difficult to use Fiddler. No problem though! The requests module has the ability to suppress SSL certificate validation on a per-request basis. If you want to use Fiddler, set the verifySSL variable at the top of o365service.py to False.

Adding the APIs

Adding the Mail and Calendar APIs was fairly easy since I had the Contacts API functions as a starting point. Copying those methods and making a few tweaks was really all it took. The function create_message isn't very different than create_contact. You gotta love REST. I won't go into detail here, the relevant changes are all in o365service.py.

Invoking the new APIs

If you look at the 1.2 update on GitHub, you may notice that there are no new views in the app. That's right! I didn't change the UI of the app to use these new APIs. Instead, I wrote tests. Django has a neat testing framework. I created the MailApiTests and CalendarApiTests classes, and threw in a ContactsApiTests class for good measure. What I like about the way Django testing works is that I can easily invoke all tests with one command, or I can be selective. For example, I can run all of the Mail API tests with this command:

python manage.py test contacts.test.MailApiTests

Or, I can just invoke the test_delete_message test with this command:

python manage.py test contacts.test.MailApiTests.test_delete_message

You have to manually copy a valid access token into test.py to get these tests to run. If you use the app up to the point where http://hostname/contacts will display a list of contacts, you can browse to http://hostname/admin, login as your superuser, and copy the access token from your Office365Connection record. Keep in access tokens expire after an hour.

Expanding coverage

Some of you may have noticed that the o365service module doesn't cover everything. For example, you can work with contacts, but not with contact folders. Messages are there, but not mail folders. Or more subtly, you can get messages from the Inbox, but not from other folders. So what if you want to do more? The make_api_call method can help here. You can add another method to the o365service module, or you can call make_api_call directly! As an example of this, I use it in the test_send_draft_message test to get the messages from the Drafts folder.

Moving on

This will likely be my last post in the Python series. I've had a blast playing with Python and Django, and they seem like a natural platform for consuming the Office 365 APIs. As fun as it's been, it's time for me to move on to another language. What will it be? Stay tuned to find out!*

*Probably Ruby. However, I'd love to hear suggestions from the community. Is there a language or platform you're dying to see the Office 365 APIs run on? Drop a note in the comments.

Get the code

The updated code is posted on GitHub. As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT).

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

0
0

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);

               AuthenticationContextauthenticationContext = 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);

 

                ClientAssertionCertificatecac = new ClientAssertionCertificate(

                    appConfig.ClientId, cert);

                   

                varauthenticationResult = 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=MyCompanyNameMyAppName 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:  http://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: http://msdn.microsoft.com/en-us/library/azure/dn645543.aspx

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

AAD on GitHubhttps://github.com/AzureADSamples

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

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

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

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

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

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

 

 

Client Credentials Flow for Mail API in Python

0
0

Matthias announced support for the Client Credentials flow in the Mail, Calendar, and Contacts APIs a couple of weeks ago, and since then, we’ve had a lot of questions about implementing it. Matthias published a great sample using .NET and ADAL, but many of you have asked for details on implementing this on other platforms, especially those that don’t have an ADAL library. I enjoyed working with Python so much, I figured I’d try implementing this in Python.

Note: I’m going to go into gory details here. If you just want to get to the code, you can find it on GitHub.

Details, Details

Matthias’ post covers the steps to request consent and access tokens. For the most part this was straightforward. However, I quickly ran into an issue. What information do I need to include in my token request? ADAL makes it look simple. I know I need the private key from the certificate that I uploaded to my app’s manifest, but what do I do with it? Well, ADAL is open source, so I could go dig through their source, but luckily, Vittorio posted the details on his blog. Matthias also updated his sample to include an alternative method of requesting the token that bypasses ADAL.

So the POST body isn’t that much different than in the authorization code grant flow. As expected, the grant_type is different. The big difference is that instead of a client_secret, we have two new fields: client_assertion_type and client_assertion. The client_assertion_type is always "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", so that just leaves the real challenge: client_assertion.

Building the Assertion

Vittorio’s blog provides the details. We just need to create a JWT with the appropriate fields. Here’s the breakdown:

Header

  • alg: Always set to "RS256".
  • x5t: Thumbprint of the app’s cert. This is the same value that is present in the customKeyIdentifier field of the keyCredentials parameter in your app’s manifest file in Azure AD.

Payload

  • sub: Your app’s client ID.
  • iss: Your app’s client ID.
  • jti: A random GUID.
  • exp: A UNIX epoch time value that specifies when this assertion expires. Recommended to be Now + 10 minutes.
  • nbf: A UNIX epoch time value that specifies the start time of this assertion’s validity period. Recommended to be Now – 5 minutes.
  • aud: The URL to the tenant-specific token endpoint. Note: The common endpoint "https://login.windows.net/common/oauth2/token" will NOT work.

Sample Assertion

So let’s say you have an app with a client ID of bcdb5ae8-1ab8-47c3-b0b2-6756048e2ddc, and the consenting admin’s tenant ID (from the ID token you received during the consent process) is 5e0699a2-7e10-4d08-8ebb-4f7d7406ad09. Your assertion might look like:

{
"alg": "RS256",
"x5t": "W-Kt0b4B7RkK34cdXVR91e_nf9x"
},
{
"sub": "bcdb5ae8-1ab8-47c3-b0b2-6756048e2ddc",
"iss": "bcdb5ae8-1ab8-47c3-b0b2-6756048e2ddc",
"jti": "9ef089d9-b116-40d8-bbe2-57a756e32ea9",
"exp": 1423168488,
"nbf": 1423167888,
"aud": "https://login.windows.net/5e0699a2-7e10-4d08-8ebb-4f7d7406ad09/oauth2/token"
}

This is very easy to do in Python using dictionaries.

Encoding and Signing

Now that you have the assertion, we need to encode it and sign it before it’s ready to use in the client_assertion claim.

Encoding

To encode the assertion, take each part separately (the header and the payload), and base64-encode it. You should then make it url-safe, and remove any trailing '=' characters. Finally, you join the two values together with a '.' character. Using the sample assertion above, that gives us:

eyJhbGciOiAiUlMyNTYiLCAieDV0IjogIlctS3QwYjRCN1JrSzM0Y2RYVlI5MWVfbmY5eCJ9.eyJhdWQiOiAiaHR0cHM6Ly9sb2dpbi53aW5kb3dzLm5ldC81ZTA2OTlhMi03ZTEwLTRkMDgtOGViYi00ZjdkNzQwNmFkMDkvb2F1dGgyL3Rva2VuIiwgImp0aSI6ICI5ZWYwODlkOS1iMTE2LTQwZDgtYmJlMi01N2E3NTZlMzJlYTkiLCAiZXhwIjogMTQyMzE2ODQ4OCwgImlzcyI6ICJiY2RiNWFlOC0xYWI4LTQ3YzMtYjBiMi02NzU2MDQ4ZTJkZGMiLCAic3ViIjogImJjZGI1YWU4LTFhYjgtNDdjMy1iMGIyLTY3NTYwNDhlMmRkYyIsICJuYmYiOiAxNDIzMTY3ODg4fQ 

In Python, this involves using the built-in JSON library to dump the dictionaries to a string serialization, encode that into a UTF-8 byte array, and base64-encode it with the urlsafe_b64encode function.

Signing

With our encoded value in hand, you now need to sign it. Using the same certificate that you used when registering the app in Azure AD, create an RSA-256 signature. You then base64-encode the signature, and append it to the existing value, again using the '.' character as a separator. With our sample data, that results in something like:

eyJhbGciOiAiUlMyNTYiLCAieDV0IjogIlctS3QwYjRCN1JrSzM0Y2RYVlI5MWVfbmY5eCJ9.eyJhdWQiOiAiaHR0cHM6Ly9sb2dpbi53aW5kb3dzLm5ldC81ZTA2OTlhMi03ZTEwLTRkMDgtOGViYi00ZjdkNzQwNmFkMDkvb2F1dGgyL3Rva2VuIiwgImp0aSI6ICI5ZWYwODlkOS1iMTE2LTQwZDgtYmJlMi01N2E3NTZlMzJlYTkiLCAiZXhwIjogMTQyMzE2ODQ4OCwgImlzcyI6ICJiY2RiNWFlOC0xYWI4LTQ3YzMtYjBiMi02NzU2MDQ4ZTJkZGMiLCAic3ViIjogImJjZGI1YWU4LTFhYjgtNDdjMy1iMGIyLTY3NTYwNDhlMmRkYyIsICJuYmYiOiAxNDIzMTY3ODg4fQ.BjLYFjloB4XojO5YELKeXc7m_-uhEHsDZ2MKjYHY4UX5fJYSLxVpNsn6uv_ICEhEcmn5wqoh6H7x2qgCUyf-jFto56GK7ygGqf1thGAVpDMzWF3fYsvZ7g3G-x_xPxOSul2EehXse8TqDhM1fhYHD2wpTeRGCSgZtuQwJiXy4R-ysnV9YFhzu0OhBxSwjfwA7XZaqPgvZNNm1BI-L9KQ-yAIrhBkv1LnVJ3k6oNAdxRbCmzLGdEm-Dq4EYZEVXqgdPn5wo98xtETDQ6ee6VH3xjyxpoyxHAfw3BO9Ve27EdVNiQLCEwOhsuRjQvmuzqKa3IUzuVkIzudO3RYQL3qfw 

Side Note: You can parse these tokens using any JWT parser, like http://jwt.calebb.net/.

Signing in Python was a little trickier. I found the Python-RSA library, which worked for me. I had to use OpenSSL to extract the private key from my certificate into an RSA-formatted PEM file in order to use this library.

Seeing it in Action

The sample app on GitHub implements all of this logic (see clientcredhelper.py) and uses the resulting access token to call the Mail API to list messages in the Inbox. Once you login with an admin account and grant permission, you can then view the 10 most recent messages in the Inbox of any user in your organization by entering their email address in the User Email field and clicking Set User. As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT).

Ruby on Rails and Office 365 Tutorial

0
0

I published a quick tutorial on writing your first Office 365 API app using Ruby on Rails. The tutorial takes you from creating a new Rails project to listing the contents of the user's Inbox using the Mail API. I'd love to get feedback on the tutorial. What do you think of the format? Does it go into enough detail, or too much? For you Ruby experts out there, did I leave any crucial information out?

Let me know what you think in the comments or on Twitter (@JasonJohMSFT).


Announcing C# Support in the API Sandbox for Office 365 APIs

0
0

Today we bring you a guest post from Xiaoying Guo, a Senior Program Manager in our Cloud Platform Tools group. Enjoy!

We're pleased to announce that C# is now supported in the API Sandbox for Office 365 APIs! After our initial release with JavaScript in October, the team has been hard at work to deliver a C# language experience as well. I'd love to take this chance to tell you more about API Sandbox: what it is, and how it can help you to get started with the Office 365 APIs.

Figure 1: The home page of API Sandbox

Just one click to try the code with Office 365 APIs

Last October, the Office Team announced the general availability of the Office 365 APIs, including Mail, Calendar, Contacts, and Files. These robust, REST-based APIs enable all developers to integrate their custom experiences with the information in Office 365, regardless of platform or device preferences. To make your programming more productive, we provide multiple client libraries and SDKs for apps that run on different platforms using different programming languages. In order to build an app that uses the Office 365 APIs, you would need to sign up for an Office 365 tenant, register your app in Azure AD, and perhaps install the tools to build the apps. However, if you simply want to explore Office 365 APIs, no sign-up is required when you use API Sandbox. This is our goal for API Sandbox: a quick getting-started experience to try Office 365 APIs without any sign-up or environment setup. No matter whether you simply want to know what these APIs are capable of, or you want to learn more about how to use them by tinkering with code, the only thing you need to do is to open your favorite web browser using your favorite OS, then launch API Sandbox at http://apisandbox.msdn.com.

As its name implies, API Sandbox can run your code in a "sandbox" environment (i.e. a sample Office 365 tenant) we set up for you. By simply clicking the "Run" button above the code snippets, you will see right away the result printed in the "Console" panel underneath the code snippets.

Of course, if you have already have an Office 365 account, and want to try APIs against your real data, API Sandbox also enables an option for you to sign in your own Office 365 tenant for the API calls.

Figure 2: Select "My Office 365" to call the APIs using your own Office 365 tenant

Interactive learning experience for Office 365 APIs

API Sandbox provides an interactive code editing experience for you to learn Office 365 APIs. The code editor of API Sandbox supports syntax highlighting and IntelliSense in both C# and JavaScript, which makes it easy for you to discover the APIs using client libraries.

Figure 3: Get the IntelliSense for outlookClient object

No idea where to start? No problem! You can find multiple code samples in API Sandbox to start with. Those code samples cover both of the scenarios of using Office 365 client libraries and the REST APIs.

Figure 4: Expand "Code Samples" to get sample code snippets for client libraries and REST

API Sandbox is also integrated with the Office 365 API documentation to provide an interactive experience for you to learn the APIs faster. In the Office 365 REST API documentation, for all
GET requests, you can click the "Try" button in the sample request, and see the live response returned from the sample tenant.

Figure 5: Interactive Office 365 REST API reference at http://api.msdn.com

Similarly, in the client library documentation, the "Open" button under the code snippets will open the selected snippet in API Sandbox, so that you can modify and run it.

Figure 6: Interactive Office 365 client library documentation

Note that API Sandbox has implemented the authentication and the creation of Office 365 client objects, so that you can focus on learning the APIs that can get you information from Office 365 mail, calendars, contacts, and files.

Looking ahead

Currently, API Sandbox is still in Preview. In the current release of the sandbox we support GET requests for Office 365 APIs, and allow you to write code with C# and JavaScript. We are working on more features that can help you to ramp up on the new APIs, including better integration with documentation, clearer guidance for you to export the code snippets into your projects, support for more Microsoft cloud services, etc. We are looking to enhance support over time, and want to deliver the features that are important and useful for you. If you have any thoughts on what you want to see in API Sandbox, please send us your feedback through the API Sandbox. We are looking forward to hearing from you!

Figure 7: Send us feedback!

Happy learning!

Xiaoying Guo

Sr. Program Manager, Microsoft Cloud Platform Tools

 

 

Using the Calendar API in PHP

0
0

In the same spirit as my Python experiment, I decided to give PHP a try and see how far I could get with the Office 365 APIs. The same disclaimers apply: I am a total PHP neophyte. I'm certain that my code is probably non-optimal, but hey, it's a learning experience!

The idea

I had this idea for a simple app that could make use of the Calendar API. The app is a list of upcoming shows for a local community theater's Shakespearean festival. Visitors to their site can connect their Office 365 account and then add events directly to their calendar for the shows that they are attending.

The setup

There are a lot of options for PHP development, on multiple platforms. I expect this sample to run on any of them (please let me know if it doesn't!). In my case, I decided to install IIS 8 on my laptop and install PHP via the Web Platform Installer. If you're developing on a Windows machine, this is super easy. In just a few short minutes I was up and running. I didn't have to install anything else, the installer included all of the libraries I needed.

The execution

I started by creating a home page (home.php) that would show a table of upcoming shows. To make it a little dynamic, I created a class to generate the list based on the current date, and to randomize the location and whether or not a voucher was required (more on this later). To keep things constant throughout a session, I save that list in the $_SESSION global so I can reuse it. I ended up with a page that looks like this:

Now to make the "Connect My Calendar" button do something.

OAuth

I decided to create a static class to implement all of the Office 365-related functionality. I created Office365Service.php and created the Office365Service class. The first thing we need it to do is "connect" the user's calendar. Under the covers what that really means is having the user logon and provide consent to the app, then retrieving an access token. If you're familiar with OAuth2, then this is pretty straightforward. Essentially, we need to implement the authorization code grant flow against Azure AD.

To start that process, the "Connect My Calendar" button will send the user right to the authorization code request link. I added the getLoginUrl function to build this link based on my client ID:

 private static $authority = "https://login.windows.net";
private static $authorizeUrl = '/common/oauth2/authorize?client_id=%1$s&redirect_uri=%2$s&response_type=code';

// Builds a login URL based on the client ID and redirect URI
public static function getLoginUrl($redirectUri) {
  $loginUrl = self::$authority.sprintf(self::$authorizeUrl, ClientReg::$clientId,
    urlencode($redirectUri));
  error_log("Generated login URL: ".$loginUrl);
  return $loginUrl;
}

I created authorize.php to serve as the redirect page, which is where Azure sends the authorization code response. All this file needs to do is extract the code parameter from the request, and use that to issue a token request.

 $session_state = $_GET['session_state'];

...

// Use the code supplied by Azure to request an access token.
$tokens = Office365Service::getTokenFromAuthCode($code, $redirectUri);

So that's the next function I added to Office365Service:

 // Sends a request to the token endpoint to exchange an auth code
// for an access token.
public static function getTokenFromAuthCode($authCode, $redirectUri) {
  // Build the form data to post to the OAuth2 token endpoint
  $token_request_data = array(
    "grant_type" => "authorization_code",
    "code" => $authCode,
    "redirect_uri" => $redirectUri,
    "resource" => "https://outlook.office365.com/",
    "client_id" => ClientReg::$clientId,
    "client_secret" => ClientReg::$clientSecret
  );

  // Calling http_build_query is important to get the data
  // formatted as Azure expects.
  $token_request_body = http_build_query($token_request_data);

  $curl = curl_init(self::$authority.self::$tokenUrl);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_POST, true);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $token_request_body);

  if (self::$enableFiddler) {
    // ENABLE FIDDLER TRACE
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
    // SET PROXY TO FIDDLER PROXY
    curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8888");
  }

  $response = curl_exec($curl);

  $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  if (self::isFailure($httpCode)) {
    return array('errorNumber' => $httpCode,
                 'error' => 'Token request returned HTTP error '.$httpCode);
  }

  // Check error
  $curl_errno = curl_errno($curl);
  $curl_err = curl_error($curl);
  if ($curl_errno) {
    $msg = $curl_errno.": ".$curl_err;
    return array('errorNumber' => $curl_errno,
                 'error' => $msg);
  }

  curl_close($curl);

  // The response is a JSON payload, so decode it into
  // an array.
  $json_vals = json_decode($response, true);
  return $json_vals;
}

As you can see, I used curl for issuing requests. I found it very well suited for the job. I could easily build the request body as a standard array, encode it with http_build_query, and send it. Handling the response was easy too, with the built-in JSON functionjson_decode. That puts the response into an easy to manage array.

Now, back in authorize.php, I can save the tokens into the $_SESSION global and redirect back to the home page:

 // Save the access token and refresh token to the session.
$_SESSION['accessToken'] = $tokens['access_token'];
$_SESSION['refreshToken'] = $tokens['refresh_token'];
// Parse the id token returned in the response to get the user name.
$_SESSION['userName'] = Office365Service::getUserName($tokens['id_token']);

// Redirect back to the homepage.
$homePage = "http".(($_SERVER["HTTPS"] == "on") ? "s://" : "://")
                  .$_SERVER["HTTP_HOST"]."/php-calendar/home.php";
header("Location: ".$homePage);

Notice that I also get the user's name from the ID token. This is just so I can display the logged on user's name in my app. Check the getUserName function in Office365Service.php if you're interested to see how that's done.

Calendar API

Now that the user can login, my homepage looks a little different:

Notice that the buttons now say "Add to Calendar", and the user's name appears in the top right along with a "logout" link. The logout link is very simple, it just goes to:

 https://login.windows.net/common/oauth2/logout?post_logout_redirect_uri=<URL to post-logout page>

The value you set in the post_logout_redirect_uri is where Azure will send the browser after logging the user out. In my case, I created logout.php, which removes the data I stored in the $_SESSION global and then redirects to the home page.

CalendarView

Now on to what we came to see, the Calendar API. Clicking on the "Add to Calendar" takes us to the addToCalendar.php page:

So the first use of the Calendar API is the table of events on the right-hand side. To get this data, I created the getEventsForData function in Office365Service.php:

 // Uses the Calendar API's CalendarView to get all events
// on a specific day. CalendarView handles expansion of recurring items.
public static function getEventsForDate($access_token, $date) {
  // Set the start of our view window to midnight of the specified day.
  $windowStart = $date->setTime(0,0,0);
  $windowStartUrl = self::encodeDateTime($windowStart);

  // Add one day to the window start time to get the window end.
  $windowEnd = $windowStart->add(new DateInterval("P1D"));
  $windowEndUrl = self::encodeDateTime($windowEnd);

  // Build the API request URL
  $calendarViewUrl = self::$outlookApiUrl."/Me/CalendarView?"
                    ."startDateTime=".$windowStartUrl
                    ."&endDateTime=".$windowEndUrl
                    ."&\$select=Subject,Start,End" // Limit the data returned
                    ."&\$orderby=Start"; // Sort the results by the start time.

  return self::makeApiCall($access_token, "GET", $calendarViewUrl);
}

This function uses the CalendarView API to get the list of events on a specific day. The advantage of using CalendarView is that when responding to a CalendarView request, the server handles expanding recurring meetings to figure out if a recurring meeting has an instance that falls in the specified time window. The instance will be included in the results like a normal appointment!

You may have noticed that the function ends by calling another function, makeApiCall. Even though it's a detour from the Calendar API, let's take a quick look at that function, because it shows some things that apply to all of the Office 365 REST APIs.

 // Make an API call.
public static function makeApiCall($access_token, $method, $url, $payload = NULL) {
  // Generate the list of headers to always send.
  $headers = array(
    "User-Agent: php-calendar/1.0", // Sending a User-Agent header is a best practice.
    "Authorization: Bearer ".$access_token, // Always need our auth token!
    "Accept: application/json", // Always accept JSON response.
    "client-request-id: ".self::makeGuid(), // Stamp each request with a new GUID.
    "return-client-request-id: true"// The server will send request-id in response.
  );

  $curl = curl_init($url);

  if (self::$enableFiddler) {
    // ENABLE FIDDLER TRACE
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
    // SET PROXY TO FIDDLER PROXY
    curl_setopt($curl, CURLOPT_PROXY, "127.0.0.1:8888");
  }

  switch(strtoupper($method)) {
    case "GET":
      // Nothing to do, GET is the default and needs no
      // extra headers.
      break;
    case "POST":
      // Add a Content-Type header (IMPORTANT!)
      $headers[] = "Content-Type: application/json";
      curl_setopt($curl, CURLOPT_POST, true);
      curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
      break;
    case "PATCH":
      // Add a Content-Type header (IMPORTANT!)
      $headers[] = "Content-Type: application/json";
      curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PATCH");
      curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
      break;
    case "DELETE":
      curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
      break;
    default:
      error_log("INVALID METHOD: ".$method);
      exit;
  }

  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  $response = curl_exec($curl);

  $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);

  if (self::isFailure($httpCode)) {
    return array('errorNumber' => $httpCode,
                 'error' => 'Request returned HTTP error '.$httpCode);
  }

  $curl_errno = curl_errno($curl);
  $curl_err = curl_error($curl);

  if ($curl_errno) {
    $msg = $curl_errno.": ".$curl_err;
    curl_close($curl);
    return array('errorNumber' => $curl_errno,
                 'error' => $msg);
  }
  else {
    curl_close($curl);
    return json_decode($response, true);
  }
}

The first thing I want to bring attention to is the set of headers sent with every request. Matthias and I have both talked about this before, but it bears repeating. While Authorization and Accept are self-explanatory, the others are related to what we refer to as client instrumentation. Doing this should be considered a "must-have" when using the REST APIs. It can be invaluable if you run into errors.

The second thing is subtle but important. For POST and PATCH requests, you MUST set the Content-Type header to application/json. If you don't, you'll get an ErrorInvalidRequest error with the message "Cannot read the request body."

Creating Events

Ok, detour over, back to the Calendar API! The second use of the API is to create the event when the user clicks on the "Add to my calendar" button on the addToCalendar.php page. Clicking that button takes the user to doAdd.php, which does the actual adding. For this, I added the addEventToCalendar function to Office365Service.php:

 // Use the Calendar API to add an event to the default calendar.
public static function addEventToCalendar($access_token, $subject, $location,
    $startTime, $endTime, $attendeeString) {
  // Create a static body.
  $htmlBody = "<html><body>Added by php-calendar app.</body></html>";

  // Generate the JSON payload
  $event = array(
    "Subject" => $subject,
    "Location" => array("DisplayName" => $location),
    "Start" => self::encodeDateTime($startTime),
    "End" => self::encodeDateTime($endTime),
    "Body" => array("ContentType" => "HTML", "Content" => $htmlBody)
  );

  if (!is_null($attendeeString) && strlen($attendeeString) > 0) {
    $attendeeAddresses = array_filter(explode(';', $attendeeString));

    $attendees = array();
    foreach($attendeeAddresses as $address) {
      error_log("Adding ".$address);

      $attendee = array(
        "EmailAddress" => array ("Address" => $address),
        "Type" => "Required"
      );

      $attendees[] = $attendee;
    }

    $event["Attendees"] = $attendees;
  }

  $eventPayload = json_encode($event);

  $createEventUrl = self::$outlookApiUrl."/Me/Events";

  $response = self::makeApiCall($access_token, "POST", $createEventUrl, $eventPayload);

  // If the call succeeded, the response should be a JSON representation of the
  // new event. Try getting the Id property and return it.
  if ($response['Id']) {
    return $response['Id'];
  }

  else {
    return $response;
  }
}

Notice how easy PHP makes it to build the JSON payloads. All I need do is create an array and use json_encode to generate the payload. Very nice! Again this uses makeApiCall to send the request. We also don't need to worry about sending meeting invites. By adding attendees, the server takes care of that for us!

Adding an attachment

Remember before I said we'd get to the "voucher required" thing later? The voucher was really an excuse to add an attachment. If you add an event that requires a voucher to your calendar, the app will add the voucher as an attachment on the event. To do this, I added the addAttachmentToEvent function:

 // Use the Calendar API to add an attachment to an event.
public static function addAttachmentToEvent($access_token, $eventId, $attachmentData) {
  // Generate the JSON payload
  $attachment = array(
    "@odata.type" => "#Microsoft.OutlookServices.FileAttachment",
    "Name" => "voucher.txt",
    "ContentBytes" => base64_encode($attachmentData)
  );

  $attachmentPayload = json_encode($attachment);
  error_log("ATTACHMENT PAYLOAD: ".$attachmentPayload);

  $createAttachmentUrl = self::$outlookApiUrl."/Me/Events/".$eventId."/Attachments";

  return self::makeApiCall($access_token, "POST", $createAttachmentUrl,
    $attachmentPayload);
}

The value of $attachmentData is the binary contents of the file to attach. In this case, it's a simple text file.

At this point you might be wondering: "Why not just include the attachment as part of the event when you created it? Wouldn't that be more efficient?" Well yes, it would, but it doesn't work! In order to add an attachment, you have to create the event first then POST to the event's Attachments collection.

The end result

If I stick with the premise of this being an experiment, then I have to conclude that PHP is a great language for calling the Office 365 REST APIs. Using just the built in libraries I was able to do everything I needed to do in a straightforward way. If PHP is your language of choice, you should have no trouble integrating with Office 365.

The sample app is available on GitHub. As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT).

Customizing the read experience with contextual mail apps for Outlook

0
0

It is common for messages and meeting requests to include key pieces of information users need to access additional information about.  Let’s say you receive an email from a co-worker suggesting a meeting be scheduled for 2pm tomorrow. Rather than having to remember the details, it would be convenient to click on the text suggesting the meeting and schedule it rather than switching over to the calendar.

In the past, read apps supported special activation rules which controlled when an app would appear on a message.  The downside was these contextually activated apps would appear in the app bar and a user may not even notice the app has activated. We decided to improve this experience and are excited to announce a new feature, Contextual Apps, in the Apps for Outlook platform. This means for information such as addresses and phone numbers the app can now be launched directly from the text that activated the app. 

How can I see a contextual app?

Contextual apps are already available to Office 365 users and will be coming to Outlook in the future. There are two pre-installed apps that will show you contextual apps, Suggested Meetings and Bing Maps. The Bing Maps app will activate on addresses that are surfaced in a message or a meeting request you have received.  The text that activated the app is highlighted, blue is the default color, and underlined with a dotted line.

Once the highlighted text is clicked on a card appears near the text and opens the app. Below is a screen of Bing Maps to show how contextual apps will appear when launched.

How do I write a contextual app?

If you are interested in building a contextual app the good news is it is simply a read app that utilizes ItemHasKnownEntity and/or ItemHasRegularExpressionMatch activation rules in the manifest. Contextual apps can activate on known entities such as addresses and phone numbers or on a regular expression you define. Once the activation rules are defined the app will automatically be shown on the relevant text, compose apps cannot be contextual. This means if you have an app that is currently activating using the rules mentioned above your app should automatically make use of this new feature.  If it doesn’t light up you may be hitting one of the special cases where we are unable to highlight. For more information please take a look at our documentation.

Office 365 APIs and Node.js

0
0

When we launched the Office 365 APIs, the Visual Studio folks released some very helpful client-side implementations of these APIs, making it much easier to get started. One of these implementations, the Microsoft Office 365 APIs Client Libraries for Cordova Applications, got a lot of attention from JavaScript developers. However, there was some confusion about the library and what it was designed to do. As the name suggests, this library was created to be used in applications built on the Cordova platform, and more specifically, for Multi-Device Hybrid App projects in Visual Studio. Developers who tried using it in a web app soon ran into issues. It just wouldn't work.

With all of the interest in using this library from a web app, when I was asked if we could modify the library to make it work in Node.js, I said "why not?"

Finding the problem

As it turns out, the big stumbling block is that the library uses the AJAX XMLHttpRequest object to send API requests. AJAX is all client-side, and not available to server-side platforms like Node.js. So it makes sense that it doesn't work in web apps.

The solution

Luckily, someone clever already provided a solution for this. The XMLHttpRequest for node.js module sounds like exactly what we need! If we can use this, we can avoid having to change the Cordova library much at all.

With that out of the way, the question left is how to load this non-Node module. As before, someone clever has already found the solution to that. With this trick in my toolbox, I decided to write a Node module to wrap the Cordova libraries (to be specific, just the Mail, Calendar, and Contacts APIs).

The node-outlook module

The code for the module was incredibly simple. All I needed to do was create an index.js file and put the following in it:

 var fs = require("fs");
var path = require("path");
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

var currentDirectory = path.dirname(fs.realpathSync(__filename));

exchangefile = fs.readFileSync(path.join(currentDirectory,
  './exchange-lib/exchange.js'), 'utf8');
eval(exchangefile);
utilityfile = fs.readFileSync(path.join(currentDirectory,
  './exchange-lib/utility.js'), 'utf8');
eval(utilityfile);

exports.Microsoft = Microsoft;

The exchange.js and utility.js files were copied straight from the Cordova library NuGet package. Pretty simple right? Well, it's even simpler now! I published node-outlook on the NPM registry, so you can save yourself the hassle and install via NPM. Or if you prefer, get the code on GitHub.

Using node-outlook

For a full sample that uses this library, check out node-mail. I'll cover the basic requirements here.

In order to use the library, you need to implement a callback function that returns an OAuth2 access token. The OutlookServices.Client class takes this callback as a parameter to its constructor. Whenever the client class makes a call to the Office 365 service, it calls this function to get the token. How you implement the callback will depend on how you're storing and managing tokens. As an example, let's look at how node-mail does it.

 function getAccessToken(resource, session) {
  console.log("getAccessToken called for " + session);
  var deferred = new outlook.Microsoft.Utility.Deferred();
  if (session.token.expired()) {
    session.token.refresh(function(error, result) {
      if (error) {
        console.log("Refresh token error: ", error.message);
      }
      session.token = result;
      console.log("NEW ACCESS TOKEN: ", session.token.token.access_token);
      deferred.resolve(session.token.token.access_token);
    });
  }
  else {
    // Return the token right away
    console.log("EXISTING ACCESS TOKEN: ", session.token.token.access_token);
    deferred.resolve(session.token.token.access_token);
  }
  return deferred;
}

function getAccessTokenFn(resource, session) {
  return function() {
    return getAccessToken(resource, session);
  }
}

The node-mail app caches tokens in the session. The getAccessToken function returns the cached token if it's not expired. Otherwise, it refreshes the token and returns the new token. The getAccessTokenFn function exists to wrap getAccessToken as a function with no parameters, which is what the OutlookServices.Client class expects.

With authorization taken care of, using the library works just like it does in a Cordova app. For example, you could get messages in the inbox with the following code:

 var outlookClient = new outlook.Microsoft.OutlookServices.Client(
  'https://outlook.office365.com/api/v1.0',
  authHelper.getAccessTokenFn('https://outlook.office365.com/', session));

outlookClient.me.messages.getMessages()
.orderBy('DateTimeReceived desc').fetchAll(10).then(function (result) {
  result.forEach(function (message) {
    console.log(message.subject);
  });
}, function (error) {
  console.log(error);
});

For more examples of using the library, see:

As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT). Feel free to report issues or submit pull requests on GitHub!

Announcing Outlook Mail Apps UserVoice page

0
0

 

We at Microsoft put our users’ satisfaction first. It is even more important for us in the Outlook Mail Apps team as we have two set of users, developers and consumers. To make sure that every single one of our users are able to communicate their feedback to us we are opening a UserVoice page at http://appsforoutlook.uservoice.com.  Our UserVoice page helps us to ensure every developer’s voice is heard.

In addition to our UserVoice page, we are also monitoring the “office365-apps” tag on StackOverFlow to help you out in developing your apps.

Over the past few months we have enhanced our API set more and added Contextual Mail Apps to Outlook. We have more exciting features in the pipeline and your feedback is going to help us deliver the best experience with the best timing. Please provide us your feedback and wishes.

Ruby gem for Mail, Calendar, and Contacts APIs

0
0

Today I published a new Ruby on Rails sample, the Office 365 VCF Import/Export Sample. It's basically a simple tool that allows a user to export contacts from her Office 365 mailbox to vCard files, or import vCard files into her contacts.

But wait, there's more! Instead of implementing the Contacts API in the sample, I created a separate Ruby gem and implemented a portion of the Mail, Calendar, and Contacts APIs. That gem is published on rubygems.org for you to install and use in your projects.

vCard Import/Export

The look and feel of the sample app owes a LOT to the great work done by Michael Hartl in his Ruby on Rails Tutorial.

The sample app basically uses the Contacts API in two ways: to create new contacts and to get existing contacts.

Getting the contacts list

When the user signs in, the app presents a list of existing contacts, sorted alphabetically by display name. The list is paged, showing 30 contacts at a time. Here's what that looks like using the ruby_outlook gem:

 # Maximum 30 results per page.
view_size = 30
# Set the page from the query parameter.
page = params[:page].nil? ? 1 : params[:page].to_i
# Only retrieve display name.
fields = [
"DisplayName"
]
# Sort by display name
sort = { :sort_field => 'DisplayName', :sort_order => 'ASC' }

# Call the ruby_outlook gem
wrapped_contacts = outlook_client.get_contacts user.access_token,
view_size, page, fields, sort

The wrapped_contacts variable is a JSON hash of the returned contacts.

Getting details on a single contact

When the user clicks the "Export" button for a contact, the app gets all fields for that single contact, using its Id. Here's how that's done with the ruby_outlook gem:

 outlook_client = RubyOutlook::Client.new

# Call the ruby_outlook gem to get the contact from its ID
contact = outlook_client.get_contact_by_id current_user.access_token, contact_id

The contact variable is a JSON hash of all the fields of the contact. That is used to build a vCard stream, which the user can download.

Creating a new contact

When the user clicks the "Import" button on the main page, they are given the option to enter vCard data manually, or to open a local vCard file for import.

When the user clicks "Import" on this page, the vCard data is transformed into a JSON hash conforming to the Contact entity defined by the Contact API. It then uploads that to the server via the ruby_outlook gem:

 outlook_client = RubyOutlook::Client.new
response = outlook_client.create_contact current_user.access_token, contact

The contact variable contains the JSON hash of the new contact, and the response variable contains the JSON hash that's returned by the server after creation.

More on the ruby_outlook gem

The gem doesn't just implement the Contacts API. It implements the following:

  • Contacts API: CRUD, including a "get by ID" function.
  • Calendar API: CRUD, including a "get by ID" function and a calendar view function.
  • Mail API: CRUD, including a "get by ID" function. Also implements a send mail function.

You may have noticed that there's a lot here that isn't implemented. For the stuff that isn't there, I created a make_api_call function that allows you to call any part of the API that you need. If you look at the source for the gem, you'll notice that all of the implemented functions (like get_contacts for example), use the make_api_call to do the actual work. Following their example, you can implement any other API call you want. Let's take a look at an example.

 # method (string): The HTTP method to use for the API call. 
# Must be 'GET', 'POST', 'PATCH', or 'DELETE'
# url (string): The URL to use for the API call. Must not contain
# the host. For example: '/api/v1.0/me/messages'
# token (string): access token
# params (hash) a Ruby hash containing any query parameters needed for the API call
# payload (hash): a JSON hash representing the API call's payload. Only used
# for POST or PATCH.
def make_api_call(method, url, token, params = nil, payload = nil)

Using the gem to create a folder

The gem currently has no functions for working with mail folders. However, you can use the make_api_all function to do the work. The details on creating a folder are documented here. Using that information, you can do the following to create a subfolder in the Inbox:

 outlook_client = RubyOutlook::Client.new

create_folder_url = '/api/v1.0/me/folders/inbox/childfolders'
new_folder_payload = { 'DisplayName' => 'New Subfolder' }

create_result = outlook_client.make_api_call('POST', create_folder_url,
    token, nil, new_folder_payload)

Using similar methods you should be able to call anything that the APIs support.

Go download the gem, the sample, or both! As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT). Feel free to report issues or submit pull requests on GitHub!

Links

Populate your Office 365 Developer Tenant with sample data

0
0

An Office 365 Developer Tenant is a great resource for developing with the Office 365 REST APIs. It gives you a place to register your app, and a user to test against. However, you have to manually add data to your user’s mailbox or OneDrive. Today we’re excited to announce a sample Windows Store App project that will populate data for Office 365 API services such as mail, calendar, contact and files. Not only do you automate the process of getting test data into your developer tenant, but you get some sample code for creating items!

The app works by reading new contacts, mails and calendar events from an XML file and adding them to the logged in Office 365 user's account. For files, you browse and select one or more files from your computer and the app uploads them for you. Existing data on the Office 365 tenant account will not be affected by this app.

You can use this app to quickly populate data into your Office 365 developer tenant so you can get started building Office 365 apps by interacting with the populated data. Let’s take a look at how it works.

Get the sample

Before you start, make sure that you have the needed tools. You’re going to need:

The sample is hosted on GitHub here: https://github.com/OfficeDev/O365API-PopulateSampleData. Download or clone the repository and open it up in Visual Studio. The README contains all of the details on configuring the project to run on your machine. Go ahead and follow Step 1 and Step 2.

Customize the data

Step 2 in the README introduce you to the XML files that contain the sample data for mail, calendar, and contacts. They contain a few sample entries to get you started, but you can add as many more as you like. One important detail that’s easy to miss is that for mail, the app adds mails by sending them as you. So to have them show up in your Inbox, make sure that you add your email address to the ToRecipients entries.

You’ll browse for files in the app itself, so there’s no XML file for files. For convenience you may want to generate some test files in a single directory.

Run the sample App

Press F5, to run the app. This is how the application looks:

Now you can click the buttons to add data to your user. The first one you choose will prompt you to login.

Add Files to OneDrive

When you click “Add MyFiles”, you’ll get a browse window to select files. Once you select the files you want and click Open, the app will upload them to the user’s OneDrive.

Add Mail, Events, or Contacts

Adding mail, events, or contacts is as easy as clicking the corresponding button. The app will parse the corresponding XML file and create new items for each entry. After it’s done, it will show a list of the current items of the appropriate type.

Next steps

Besides populating data, the app can also update or delete data. The README on GitHub has instructions for updating the XML files for update or delete.

We hope that you find this app useful! Please report issues on GitHub.


Check out dev.outlook.com!

0
0

As you can probably tell from announcements we made at BUILD this week like add-ins for Outlook coming to Outlook.com, new features in our REST APIs like web hooks and search, and new ribbon extensibility in the next version of Outlook; we are serious about investing in Outlook, Outlook.com, the Office 365 Outlook Service, and Exchange to build a powerful unified platform that reaches hundreds of millions of users using web standards like REST, OAuth, JSON, HTML, and JavaScript. 

In addition to all of these investments we wanted to make it even easier for developers to build add-ins and applications that integrate with Outlook and Exchange by creating a single site for the Outlook platform with tutorials, references, support and feedback forums, SDK’s, and helpful tools.  That site is http://dev.outlook.com– we encourage you to check it out and give us your feedback.

We are going to be iterating on the content quickly over the upcoming weeks, adding getting started guides, code samples, and useful information on the rollout of the extensibility platform to Outlook.com.  So bookmark it and check back often to see what we have added. 

http://dev.outlook.com is an Outlook centric-view on Office extensibility.  If you are interested in the overall Office platform make sure to check out http://dev.office.com where you will find information on the entire Office platform.

Making App Registration Easier

0
0

I know I've been a little quieter than usual recently. No new blog posts, no new samples...you'd think I was taking a break. In reality, I've been heads-down working on the https://dev.outlook.com portal that we recently launched. If you haven't checked it out, you should! There's step-by-step tutorials for the Outlook REST APIs for .NET, Node.js, and Ruby, and there's more in the works. The tutorials that require you to register your app manually even have an inline form so you can get a client ID and secret directly from the tutorial page.

That leads me to today's news. I'm happy to announce that we've expanded that app registration tool into a stand-alone page: https://dev.outlook.com/appregistration. Now you can register your apps that use the Mail, Calendar, or Contacts APIs with a simple tool. The best part is, all you need is an Office 365 account. No Azure subscription required!

A caveat: at this time, the tool only supports delegated permissions, so you cannot use this too to register apps that will use the client credential auth flow.

If you're developing or planning to develop with the Outlook APIs, please give this tool a try, especially if you've had problems with registering via the Azure management portal. If you do try the tool, please send feedback! We're still working out the feedback channel for the site, so in the meantime, leave it in the comments below, or send it to me on Twitter: @JasonJohMSFT.

Office 365 API Walkthrough for Windows Store App

0
0

With Office 365 API Tools, you can integrate Office 365 services - including Mail, Calendar, Contacts, Files and other services - with your applications. Office 365 API Tools take care of registering your application to consume Office 365 APIs and adding necessary NuGet libraries to your project. Before you get started, you will need an Office 365 Developer Tenant. If you don’t have an Office 365 Developer tenant yet, but are interested in the Office 365 APIs, the API Sandbox offers you a quick in-browser C#/JavaScript code editing experience to try the APIs with no sign-up needed.

Note: To use Office 365 API Tools with Visual Studio 2015 RC, you will need Microsoft Visual Studio Enterprise 2015 RC with Microsoft Office Developer Tools. Choose the Custom option when running the Visual Studio installer and check the option to install Microsoft Office Development Tools. 

If you are using Visual Studio 2013, then you will need the latest version of Microsoft Office Developer Tools for Visual Studio 2013.

In the following walkthrough, we will build a simple Windows Store app and integrate Office 365 APIs to let users sign in, view files stored on their OneDrive accounts and their contacts. Let’s start by creating a new Windows 8 store app. For this demo, I named my app O365SampleApp.

1. Configure Office 365 API

We will use Office 365 API Tools to configure the app to use Office 365 APIs. To do so, right-click on the project node in the solution explorer and select Add -> Connected Service… Select Office 365 APIs from the list of providers and click Configure.

Click on the Register your app link and enter the credentials for your Office 365 Developer tenant account. Note: This step registers your app on Azure Active Directory associated with your Office 365 tenant.

Now you will be presented with the page to set permissions for various Office 365 services. Set the following permissions and click the Add button once all the permissions are set:

  1. For the Contacts service, set the Read your contacts permission.
  2. For the My Files service, set the Read your files permission.

The above steps will add some NuGet libraries, and also enable Read your contacts and Read your files permissions for the app that was registered on Azure Active Directory. A complete list of libraries that were added can be seen in the Visual Studio output window.

2. Access Office 365 API by using .NET Client Library

To access Office 365 APIs using the .NET client library, first acquire an access token. Then get the Outlook Services client for fetching Contacts and SharePoint Client for fetching MyFiles. We can then send asynchronous queries to interact with contacts/files data.

2.1 Get Access Token

In this walkthrough, we will use Azure Active Directory Authentication Libraries (ADAL) to authenticate users and obtain an access token for securing API calls.

Add Microsoft.IdentityModel.Clients.ActiveDirectory to your project through the NuGet Package Manager.

Next, add a new class to the solution; let’s call it AuthenticationHelper.cs and replace the content of this file with:

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Threading.Tasks;
using Windows.Security.Authentication.Web;
using Windows.Storage;
using Windows.UI.Popups;

namespace O365SampleApp
{
class AuthenticationHelper
{
public static readonly string DiscoveryServiceResourceId = "https://api.office.com/discovery/";
const string AuthorityFormat = "https://login.microsoftonline.com/{0}/";
static readonly Uri DiscoveryServiceEndpointUri = new Uri("https://api.office.com/discovery/v1.0/me/");
static readonly string ClientId = App.Current.Resources["ida:ClientID"].ToString();
static ApplicationDataContainer AppSettings = ApplicationData.Current.LocalSettings;
static string _authority = String.Empty;
static string _lastTenantId = "common";
const string _lastTenantIdKey = "LastAuthority";
static AuthenticationContext authContext = null;

public static Uri AppRedirectURI
{
get
{
return WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
}
}

public static string LastTenantId
{
get
{
if (AppSettings.Values.ContainsKey(_lastTenantIdKey) && AppSettings.Values[_lastTenantIdKey] != null)
{
return AppSettings.Values[_lastTenantIdKey].ToString();
}
else
{
return _lastTenantId;
}
}
set
{
_lastTenantId = value;
AppSettings.Values[_lastTenantIdKey] = _lastTenantId;
}
}

public static string Authority
{
get
{
_authority = String.Format(AuthorityFormat, LastTenantId);
return _authority;
}
}

public static async Task<AuthenticationResult> GetAccessToken(string serviceResourceId)
{
AuthenticationResult authResult = null;
if (authContext == null)
{
authContext = new AuthenticationContext(Authority);

#region To enable Windows Integrated Authentication (if you deploying your app in a corporate network)
//// To enable Windows Integrated Authentication, in Package.appxmanifest, in the Capabilities tab, enable:
//// * Enterprise Authentication
//// * Private Networks (Client & Server)
//// * Shared User Certificates
//// Plus add (uncomment) the following line of code:
//authContext.UseCorporateNetwork = true;
#endregion

authResult = await authContext.AcquireTokenAsync(serviceResourceId, ClientId, AppRedirectURI);
}
else
{
authResult = await authContext.AcquireTokenSilentAsync(serviceResourceId, ClientId);
}
LastTenantId = authResult.TenantId;
if (authResult.Status != AuthenticationStatus.Success)
{
LastTenantId = authResult.TenantId;
if (authResult.Error == "authentication_canceled")
{
// The user cancelled the sign-in, no need to display a message.
}
else
{
MessageDialog dialog = new MessageDialog(string.Format("If the error continues, please contact your administrator.\n\nError: {0}\n\n Error Description:\n\n{1}", authResult.Error, authResult.ErrorDescription), "Sorry, an error occurred while signing you in.");
await dialog.ShowAsync();
}
}
return authResult;
}

public static void SignOut()
{
authContext = new AuthenticationContext(Authority);
authContext.TokenCache.Clear();
authContext = null;
ApplicationData.Current.LocalSettings.Values["TenantId"] = null;
ApplicationData.Current.LocalSettings.Values["LastAuthority"] = null;
}
}
}

 

2.2 Get Outlook Services Client and SharePoint Client

An OutlookServiceClient is required to access Calendar, Contacts and Mail APIs. While, a SharePointClient is required to access Files APIs.

Add a new class to the solution; let’s call it Office365Helper.cs and replace the content of this file with:

using Microsoft.Office365.Discovery;
using Microsoft.Office365.OutlookServices;
using Microsoft.Office365.SharePoint.CoreServices;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace O365SampleApp
{
class Office365Helper
{
private static IDictionary<string, CapabilityDiscoveryResult> AppCapabilities = null;

private static async Task getAppCapabilities()
{
DiscoveryClient discoveryClient = new DiscoveryClient(
async () =>
{
var authResult = await AuthenticationHelper.GetAccessToken(AuthenticationHelper.DiscoveryServiceResourceId);
return authResult.AccessToken;
}
);
AppCapabilities = await discoveryClient.DiscoverCapabilitiesAsync();
}

public async static Task<OutlookServicesClient> CreateOutlookClientAsync(string capability)
{
if (AppCapabilities == null)
{
await getAppCapabilities();
}
var myCapability = AppCapabilities
.Where(s => s.Key == capability)
.Select(p => new { Key = p.Key, ServiceResourceId = p.Value.ServiceResourceId, ServiceEndPointUri = p.Value.ServiceEndpointUri })
.FirstOrDefault();
OutlookServicesClient outlookClient = new OutlookServicesClient(myCapability.ServiceEndPointUri,
async () =>
{
var authResult = await AuthenticationHelper.GetAccessToken(myCapability.ServiceResourceId);
return authResult.AccessToken;
});
return outlookClient;
}

public async static Task<SharePointClient> CreateSharePointClientAsync(string capability)
{
if (AppCapabilities == null)
{
await getAppCapabilities();
}
var myCapability = AppCapabilities
.Where(s => s.Key == capability)
.Select(p => new { Key = p.Key, ServiceResourceId = p.Value.ServiceResourceId, ServiceEndPointUri = p.Value.ServiceEndpointUri })
.FirstOrDefault();
SharePointClient spClient = new SharePointClient(myCapability.ServiceEndPointUri,
async () =>
{
var authResult = await AuthenticationHelper.GetAccessToken(myCapability.ServiceResourceId);
return authResult.AccessToken;
});
return spClient;
}
}
}

 

2.3 Interact with Contact and Files Data

By sending async queries to OutlookServiceClient and SharePointClient, we can interact with Contact and Files data respectively.

Add the following getContacts() and getMyFiles() methods to Office365Helper class in Office365Helper.cs file.

public static async Task<List<IContact>> getContacts(OutlookServicesClient contactsClient)
{
List<IContact> myContacts = new List<IContact>();
var contactsResult = await contactsClient.Me.Contacts.OrderBy(c => c.DisplayName).ExecuteAsync();
do
{
var contacts = contactsResult.CurrentPage;
foreach (var contact in contacts)
{
myContacts.Add(contact);
}
contactsResult = await contactsResult.GetNextPageAsync();
} while (contactsResult != null);
return myContacts;
}

public static async Task<List<Microsoft.Office365.SharePoint.FileServices.IItem>> getMyFiles(SharePointClient spClient)
{
List<Microsoft.Office365.SharePoint.FileServices.IItem> myFiles = new List<Microsoft.Office365.SharePoint.FileServices.IItem>();
var myFileResult = await spClient.Files.ExecuteAsync();
do
{
var files = myFileResult.CurrentPage;
foreach (var file in files)
{
myFiles.Add(file);
}
myFileResult = await myFileResult.GetNextPageAsync();
} while (myFileResult != null);
return myFiles;
}

 

3. Update MainPage.xaml to Include GetContacts and GetMyFiles Buttons

Now we will add 3 buttons to our MainPage to trigger the getContacts, getMyFiles and SignOut calls. To do so, open MainPage.xaml from solution explorer and add the following 3 buttons:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="BtnGetContacts" Content="Get Contacts" HorizontalAlignment="Left" Margin="58,110,0,0" VerticalAlignment="Top" RenderTransformOrigin="-1.737,-2.446" Click="BtnGetContacts_Click"/>
<Button x:Name="BtnGetMyFiles" Content="Get My Files" HorizontalAlignment="Left" Margin="58,167,0,0" VerticalAlignment="Top" RenderTransformOrigin="-1.737,-2.446" Click="BtnGetMyFiles_Click"/>
<Button x:Name="BtnSignOut" Content="Sign Out" HorizontalAlignment="Left" Margin="58,225,0,0" VerticalAlignment="Top" RenderTransformOrigin="-1.737,-2.446" Click="BtnSignOut_Click"/>
</Grid>

Let’s now create button click event handlers. Open MainPage.xaml.cs file and add these methods within the MainPage class.

private async void BtnGetContacts_Click(object sender, RoutedEventArgs e)
{
var contactsClient = await Office365Helper.CreateOutlookClientAsync("Contacts");
var contacts = await Office365Helper.getContacts(contactsClient);

string contactsString = "";
foreach (var contact in contacts)
{
contactsString += contact.DisplayName + Environment.NewLine;
}
MessageDialog mg = new MessageDialog(contactsString, "Contacts");
await mg.ShowAsync();
}

private async void BtnGetMyFiles_Click(object sender, RoutedEventArgs e)
{
var spClient = await Office365Helper.CreateSharePointClientAsync("MyFiles");
var myFiles = await Office365Helper.getMyFiles(spClient);

string myFilesString = "";
foreach (var myFile in myFiles)
{
myFilesString += myFile.Type + " - " + myFile.Name + Environment.NewLine;
}
MessageDialog mg = new MessageDialog(myFilesString, "My Files");
await mg.ShowAsync();
}

private void BtnSignOut_Click(object sender, RoutedEventArgs e)
{
AuthenticationHelper.SignOut();
}

 

Note: Add using Windows.UI.Popups; to MainPage.xaml.cs so that we can show Contacts and Files results on a MessageDialog.

Our app is now ready to fetch a list of Contacts and MyFiles. You can now press F5 and click on “Get Contacts” button to see a list of your contacts or “Get My Files” button to see the files stored on OneDrive.

Next Steps

Apart from just interacting with Contacts and Files APIs, you can also interact with Mail, Calendar and other services. A complete list can be found on Office 365 API reference. For any service that you want your app to interact with, remember to set the right permissions when using the Office 365 APIs connected service wizard to configure Office 365 APIs. You can also explore the Office 365 APIs using the API Sandbox. Various sample projects for almost every project type can be found on Office Developer GitHub

Announcing Exchange ActiveSync v16

0
0

One advantage of having your mailbox in Office 365 is that we usually deploy our innovations there first. We’re making enhancements to Exchange ActiveSync (EAS) in Office 365 that will soon be available to Exchange ActiveSync applications. Update: Exchange ActiveSync v16 will also be included in on-premise installations of Exchange Server 2016.

Updated EAS, which we will call version 16, contains three new major capabilities:

  1. The major focus is enhanced calendar reliability brought about through a reworking of the calendar workflow between server and client. This is a major benefit but ideally will be largely unnoticed by end-users. Success here will be the absence of those hard-to-diagnose calendar related problems sometimes seen when the server and client are not produced by the same company.
  2. Calendar attachments. Previously calendar items synced with EAS could not include any attachments such as agendas, decks or spreadsheets. In version 16 that is now possible.
  3. Syncing the drafts folder was previously not supported. We’ve added that support in version 16. Now you can start an email on your EAS device and continue editing that draft back at your desktop by retrieving the mail from the drafts folder. Or, write your draft at your computer, then make the final tweaks and send it from your phone while you're on the go.

Customers with their mailbox in Office 365 can take advantage of these new features when their favorite EAS client is updated. Update: We are happy to share that iOS 9, announced June 9 at Apple’s Worldwide Developer Conference, will support the calendar features of version 16 when it is released this fall.

As an end user, how can you tell if your mailbox is enabled for EAS version 16? The simplest way is to run an EAS Autodiscover test at https://testconnectivity.microsoft.com and look for version 16 listed as one of the supported protocol versions in the test results. Note that you need a valid Office 365 email address and password in order to run that test, and the results are specific to that mailbox only (not the entire tenant).

For partners who are building Exchange ActiveSync applications, we will have updated protocol documentation posted on MSDN by the end of the month. Partners holding a current EAS patent license and an active developer support agreement can contact their Technical Account Manager (TAM) now for more information prior to the documentation being posted.

Note to developers: Exchange ActiveSync is different from the Office 365 Outlook REST APIs. Exchange ActiveSync is highly optimized for mobile clients connecting directly to Office 365/Exchange Online, and requires a licensing fee to use. The Outlook REST APIs are standards-based REST+JSON+OAuth Mail, Calendar, and Contact APIs that have no licensing fee, and are great for both mobile device-to-Office 365/Exchange Online, and service-to-Office 365/Exchange Online applications. We recommend that developers explore and evaluate the Outlook REST APIs for their applications.

Unified Outlook APIs for Office 365 & Outlook.com are here!

0
0

We are excited to announce Outlook REST APIs for mail, calendar, and contacts now support both Office 365 & Outlook.com! This single REST API endpoint https://outlook.office.com/api combined with a new app registration portal and v2.0 app model announced by the Azure Active Directory team, makes it very easy to build a single app that can be used by both Office 365 and Outlook.com users.

As previously announced, we are upgrading Outlook.com users to a new Office 365-based infrastructure. As users are upgraded, apps can use Outlook REST APIs to access these users’ mailboxes. This new platform will enable developers to reach 400 million monthly active Outlook.com users, in addition to tens of millions of Office 365 users. If your app utilizes both REST APIs and our add-in platform, you are all set as we also support Outlook Add-ins for Office 365 and Outlook.com users.

You will find more information in an overview of the Unified Outlook Service API and getting started instructions for multiple platforms at https://dev.outlook.com. Please note that the new OAuth endpoint is in preview and you can check here for details on what is and isn’t supported during Preview.

Please go ahead and try our REST APIs out and let us know your feedback by posting to UserVoice. The new API endpoint and app registration portal are a direct result of the feedback from our developer community. We look forward to seeing many awesome apps being built using the unified Outlook API endpoint to delight Office 365 & Outlook.com users!

Viewing all 108 articles
Browse latest View live




Latest Images