Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly register and access Office 365 Graph API for OAuth2 (using omniauth from Ruby)?

I'm trying to access the Office 365 (Graph API) from our Ruby on Rails application (specifically, the Calendar Read API). We're using omniauth for our OAuth2 flows and as such, we have also tried to access the Graph API using the omniauth-office365 and the omniauth-microsoft-office365 gem. But I haven't been able to get an access token with neither of these gems so far.

I have registered our app in the Application Registration Portal, but any time I wanted to get Calendars.Read permission (using scope "profile https://graph.microsoft.com/calendar.read"), I always get the error AADSTS65005: The client application has requested access to resource 'https://graph.windows.net/'. This request has failed because the client has not specified this resource in its requiredResourceAccess list. Reading more articles about this, I got the impression that I need to actually go through Azure AD, so I signed up for that. But then it seems I have to register a completely new web application in the Azure dashboard that has no link to the previously created application. I gave it a try, but that only results in a AADSTS70002: Error validating credentials. AADSTS50011: The reply address 'https://example.com/auth/office365/callback?code=AQABA...a_very_long_string&session_state=e1029a3b-f6a5-4e7a-940e-18a21ee4c44f' does not match the reply address 'https://example.com/auth/office365/callback' provided when requesting Authorization code. error.

I'm at the point where I'm completely confused. What is the right way to go about this and to get this to work? It cannot really be that I need to go through Azure AD, right? What is the whole point of the Application Registration Portal then? It would be great if anyone could shed some light...

Thanks, Pascal

like image 612
Pascal Lindelauf Avatar asked Dec 13 '16 10:12

Pascal Lindelauf


2 Answers

Ok, after much fiddling around, I finally got a grip on things. And it doesn't help that there are so many different ways of accessing the different API's, each carrying their specific version, and each with their whole slew of outdated "this is how you do it" articles.

Let me summarize how I got everything to work and lessons learned.

  1. There is an "old way" and a "new way" to gain access to the API's. The old way (v1.0) involves Azure AD, where you have to get an Azure account and configure everything in that dashboard. The new way (v2.0) involves the Application Registration Portal and just that. Also see this article.
  2. The Graph API version is currently still at v1.0 and this version number has nothing to do with the authentication (Azure AD) version number described above. (also see this article)
  3. I was using the omniauth-office365 and the omniauth-microsoft-office365 gems, both of which are trying to access the resources at the host https://outlook.office.com/, whereas all examples refer to base https://graph.microsoft.com. Switching to the microsoft_v2_auth gem included in this Ruby sample got me further.
  4. The AADSTS65005 seemed to have to do with the exact "wording" of the scopes. I've seen wordings like :scope => 'openid email profile offline_access https://graph.microsoft.com/calendar.read', but the correct wording is :scope => 'openid email profile offline_access https://graph.microsoft.com/Calendars.Read' (so plural Calendars and Pascal case). This seemed to solve the problem for me.
  5. The "Insufficient privileges to complete the operation." occured after a succesful callback with an access token, but right when the gem wanted to get extra profile information from the /v1.0/me API. Only after I added https://graph.microsoft.com/User.Read to the scope in my Ruby application, as well as User.Read grant in the application registration, the gem seemed to have the permissions it needed and the error went away. NOTE! It seems updating your application configuration can take up to 30 minutes to take effect! This makes it so damn hard to make any progression and find exactly what actions have what effect.
  6. Microsoft access tokens expire within one hour, so you will need to refresh your access token often, using a refresh token. You get a refresh token with your initial authorization request, only if you include offline_access in your scope (see point 4). Then you can use the following type of code:

    oauth = OmniAuth::Strategies::MicrosoftV2Auth.new(
      nil, 
      ENV['OFFICE365_KEY'], 
      ENV['OFFICE365_SECRET'] 
    )
    token = OAuth2::AccessToken.new(
      oauth.client, 
      @access_token, 
      { refresh_token: @refresh_token }
    )
    new_token = token.refresh!
    @access_token = new_token.token if new_token.token
    

Also, when testing this it is invaluable to revoke the access token you've acquired during earlier tests. This can be done at myapps.microsoft.com.

I've also run into CSRF errors in this process, in which case you need to clear your cache.

If I find anything else of interest, I'll add it here, in the hopes that noone will have to wander long in these murky API forests. :(

like image 172
Pascal Lindelauf Avatar answered Oct 17 '22 21:10

Pascal Lindelauf


The relationship between the Office 365 API and Azure AD is that Azure AD acts as an authorization server and the Office 365 API is a Resource Server registered with Azure AD.

enter image description here

Follow these steps to get your app working

  1. Sign into the Microsoft App Registration Portal using either your personal or work or school account.
  2. Choose Add an app.
  3. Enter a name for the app, and choose Create application. The registration page displays, listing the properties of your app.
  4. Copy the application ID. This is the unique identifier for your app.
  5. Under Application Secrets, choose Generate New Password. Copy the app secret from the New password generated dialog.
  6. You'll use the application ID and app secret to configure the app.
  7. Under Platforms, choose Add platform > Web. Make sure the Allow Implicit Flow check box is selected, and enter http://localhost:3000/auth/microsoft_v2_auth/callback as the Redirect URI. The Allow Implicit Flow option enables the OpenID Connect hybrid flow. During authentication, this enables the app to receive both sign-in info (the id_token) and artifacts (in this case, an authorization code) that the app uses to obtain an access token. The redirect URI http://localhost:3000/auth/microsoft_v2_auth/callback is the value that the OmniAuth middleware is configured to use once it has processed the authentication request.
  8. Choose Save.

you might find this SO thread interesting. Also a working example of Accessing graph API in Rails here

like image 35
Tony Vincent Avatar answered Oct 17 '22 22:10

Tony Vincent