monofilm
monofilm

Reputation: 67

Accessing Outlook Calendar Graph API from background service

I'm working on a web app (SPA and backend) that must constantly access the user's Outlook calendar. The users may come from multiple directories and personal accounts. The app will work 24/7, running the background service and modifying the user's calendar without the user's involvement. So far, I have completed the following:

  1. I created AAD app registration with supported account types "Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)."

  2. I set RedirectURI to be my SPA base URL and created a secret.

  3. I implemented authentication in SPA with MSAL (msal-browser). My authorization request looks like this: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?scope=https://graph.microsoft.com/Calendars.ReadWrite+openid+profile+offline_access&response_type=code&client_id=my_client_id...

  4. After the user successfully authenticates and gives all permissions, his account looks like enter image description here

  5. I also retrieved tenantId from the id token (tid claim)

  6. Just to verify that my access_token is valid and permissions set correctly, I successfully requested events from https://graph.microsoft.com/v1.0/me/events At this point, I assume the user has successfully given all the required permissions to my app and my background service can access the user's calendar

  7. I successfully requested access_token with client_credentials flow

    curl --location 'https://login.microsoftonline.com/tenantId_from_step_5/oauth2/v2.0/token'
    --header 'Content-Type: application/x-www-form-urlencoded'
    --data-urlencode 'client_id=clientId_from_step1'
    --data-urlencode 'scope=https://graph.microsoft.com/.default'
    --data-urlencode 'client_secret=secret_from_step2'
    --data-urlencode 'grant_type=client_credentials'

I requested user's events with the access_token obtained on the previous step https://graph.microsoft.com/v1.0/users/[email protected]/events but got the following error

{
"error": {
    "code": "ErrorAccessDenied",
    "message": "Access is denied. Check credentials and try again."
}

}

I'm stuck at this point and would appreciate any help or suggestions.


UPDATE:

JWT Token obtained with credential_flow (step 7 from above) from https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/oauth2/v2.0/token misses roles (permissions) enter image description here

I think all personal accounts share the same tenantId=9188040d-6c67-4c5b-b112-36a304b66dad

My app (outlook-sync-dev) permissions in Azure Portal: enter image description here

Upvotes: 1

Views: 405

Answers (3)

monofilm
monofilm

Reputation: 67

I think that @Rukmini's answer is correct, but omits some not-obvious details. I wanted to add clarification in the comment, but it is just too long, so I'm adding the answer.

My initial question was about accessing Outlook Calendar (of a personal account) via Graph API from the background service. What we know so far:

  • We can't use "client credentials flow" since it is not available for personal accounts
  • We can't use "authorization code flow" since there is no user to interact with in the background service

Therefore, the proposed solution is the following:

  1. The personal account user has to log into the app at least once to give the app permission to access the calendar and user profile. We use "authorization code flow" for that.
  2. When we exchange code for a token, we get not only access_token but also refresh_token. We store refresh_token securely somewhere on the server
  3. When the background service needs to access Graph API (without the user being involved), it exchanges refresh_token for the access_token. enter image description here
  4. Background service periodically should acquire the new refersh_token since it expires in 90 days, according to https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens

Upvotes: 0

Rukmini
Rukmini

Reputation: 15829

I created a Microsoft Entra multi-tenant application and granted API permission like below:

enter image description here

Generated access token via Client credential flow:

https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/oauth2/v2.0/token

client_id:ClientID
client_secret:ClientSecret
scope↵:https://graph.microsoft.com/.default
grant_type:client_credentials

enter image description here

By using the above access token, I tried to call events API for personal account and got the same error:

https://graph.microsoft.com/v1.0/users/UserID/events

enter image description here

Note that: Client credential flow does not support Microsoft personal accounts. Hence you must make use of any user interaction flow to call personal account related APIs that is the user must perform login process.

Hence to resolve the error, make use of Authorization code flow:

Grant delegated API permissions:

enter image description here

Generate auth code:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
&client_id=ClientID
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=https://graph.microsoft.com/.default offline_access
&state=12345

And sign-in with personal account:

enter image description here

Generated access token Authorization code flow:

https://login.microsoftonline.com/common/oauth2/v2.0/token

grant_type:authorization_code
client_id:ClientID
client_secret:ClientSecret
scope:https://graph.microsoft.com/.default offline_access
code:Code
redirect_uri:https://jwt.ms

enter image description here

I am able to successfully call events API:

https://graph.microsoft.com/v1.0/users/UserID/events

enter image description here

To do the same JavaScript, refer this MsDoc Acquire a token to call a web API (single-page apps) - Microsoft identity platform | Microsoft

Reference:

Exceptions in MSAL Java - Microsoft Authentication Library for Java | Microsoft

Upvotes: 0

instance
instance

Reputation: 26

Try removing the admin consent requirement.

Upvotes: 0

Related Questions