Building Daemon or Service Apps with Office 365 Mail, Calendar, and Contacts APIs (OAuth2 client credential flow)
Device and Web Server applications usually require a user to sign-on and consent to allow the application to access and work with their information in Office 365. The underlying protocol to gain access is based on the OAuth2 authorization code grant flow. I described this a while ago in one of my earlier blogs Using OAuth2 to access Calendar, Contact and Mail API in Office 365 Exchange Online. As part of this OAuth2 flow, the application gets an access token/refresh token pair that represents a delegation given to the application by a specific individual user for a set of permissions. Essentially before the application can access data for a user, it has to get an access token/refresh token for each user, and to get those the user has to sign-on to the application at least once.
There are however a category of applications where this is not desirable or possible. These applications usually run in the background as a daemon app or service and need access without the user having to sign-on. OAuth2 provides a different flow for these types of applications, called the client credential grant flow. You can read more about this flow in the AAD Authentication Protocol documentation here. We're happy to announce that Office 365 now supports this flow to gain access to the Office 365 Calendar, Contacts and Mail APIs.
When using this flow, the application presents its client credentials to the OAuth2 token issuing endpoint, and in return gets an access token that represents the application itself without any user information. This is sometime also called an "App-Only" token. There is no need for the application to get a refresh token. When the access token expires, it simply goes back to the OAuth2 token issuing endpoint to get a new one. Also, since there is no user information in the token, the app must specify the user within the API call when using this "App-Only" token.
Note: Access token lifetime is currently 60 minutes. This applies to all access tokens in Office 365, regardless which OAuth2 flow is used.
Defining permissions
The first step to configure your application to use "App-Only" tokens is to define what permissions your application needs. Just like permissions for authorization code grant flow apps, these permissions are defined with the application registration in the Microsoft Azure Management Portal for Azure Active Directory (AAD). While the permissions for the authorization code grant flow are called "Delegated Permissions" (because a user delegates those permission to the app), the permissions for the client credential flow are called "Application permissions" (because those permissions are directly assigned to the application).
Below is a screen shot of the AAD Application Registration permissions section:
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)
In ASP.Net you could simply retrieve the ID token via Request.Form["id_token"] on your redirect page. You can find the tenantId in the "tid" claim within the JWT id_token.
|
In the example below I used Azure Active Directory Client library (ADAL) to acquire an "app-only" access token via client credential flow. My application maintains the private key in a well-protected directory on my web server as a PFX file. However it is better to maintain key material in a more secure storage such as the Windows certificate storage of the computer account.
Code snippet: Getting an "App-Only" token
// need to address the tenant specific authority/token issuing endpoint // https://login.windows.net/<tenant-id>/oauth2/authorize // retrieved tenantID from ID token for the app during consent flow (authorize flow) string authority = appConfig.AuthorizationUri.Replace("common", tenantId); AuthenticationContext authenticationContext = new AuthenticationContext( authority, false);
string certfile = Server.MapPath(appConfig.ClientCertificatePfx);
X509Certificate2 cert = new X509Certificate2( certfile, appConfig.ClientCertificatePfxPassword, // password for the cert file containing private key X509KeyStorageFlags.MachineKeySet);
ClientAssertionCertificate cac = new ClientAssertionCertificate( appConfig.ClientId, cert);
var authenticationResult = await authenticationContext.AcquireTokenAsync( resource, // always https://outlook.office365.com for Mail, Calendar, Contacts API cac);
return authenticationResult.AccessToken;
Note: The complete sample of a web app using client credential flow is available in GitHub on · https://github.com/mattleib/o365api-as-apponly-webapp
|
Configuring a X.509 public cert for your application
One last challenge is how to actually configure a X.509 public certificate with the application definition. Unfortunately X.509 certificates are not exposed as a UI element in the AAD application management portal. The applications public certificates need to be managed through the manifest.
Step 0: (If you do not have an X.509 certificate already) Create a self-issued certificate You can easily generate a self-issued certificate with the makecert.exe tool.
1. From the command line, run: makecert -r -pe -n "CN=MyCompanyName MyAppName Cert" -b 12/15/2014 -e 12/15/2016 -ss my -len 2048 2. Open the Certificates MMC snap-in and connect to your user account. Find the new certificate in the Personal folder and export it to a base64-encoded CER file.
Note: Make sure the key length is at least 2048 when generating the X.509 certificate. Shorter key length are not accepted as valid keys.
Step 1: Get the base64 encoded cert value and thumbprint from a .cer X509 public cert file using PowerShell
Note: The instructions below show using Windows PowerShell to get properties of a x.509 certificate. Other platforms provide similar tools to retrieve properties of certificates.
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $cer.Import("mycer.cer") $bin = $cer.GetRawCertData() $base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cer.GetCertHash() $base64Thumbprint = [System.Convert]::ToBase64String($bin)
$keyid = [System.Guid]::NewGuid().ToString()
Store the values for $base64Thumbprint, $base64Value and $keyid, to be used in the next step. Step 2: Upload cert through the manifest file
3. Log in to the Azure Management Portal (https://manage.windowsazure.com) 4. Go to the AAD snap-in and there navigate to the application that you want to configure with an X.509 certificate 5. Download the application manifest file through the Azure Management Portal
6. Replace the empty “KeyCredentials”: [], property with the following JSON. NOTE: The KeyCredentials complex type is documented here: https://msdn.microsoft.com/en-us/library/azure/dn151681.aspx
"keyCredentials": [ { "customKeyIdentifier": "$base64Thumbprint_from_above", "keyId": "$keyid_from_above", "type": "AsymmetricX509Cert", "usage": "Verify", "value": "$base64Value_from_above" } ],
e.g.
"keyCredentials": [ { "customKeyIdentifier": "ieF43L8nkyw/PEHjWvj+PkWebXk=", "keyId": "2d6d849e-3e9e-46cd-b5ed-0f9e30d078cc", "type": "AsymmetricX509Cert", "usage": "Verify", "value": "MIICWjCCAgSgAwIBA***omitted for brevity***qoD4dmgJqZmXDfFyQ" } ],
7. Save the change to the application manifest file. 8. Upload the edited application manifest file through the Azure Management Portal. 9. Optional: Download the manifest again, and see your X.509 cert is present on the application.
Note: KeyCredentials is a collection, so it’s totally possible to upload multiple X.509 certificates for rollover scenarios, or delete certs for compromise scenarios. |
Must-Do Application Developing practices calling Mail, Calendar and Contacts API
Unrelated to OAuth2, there are three HTTP request headers you should always include when making requests to the Office365 APIs. This is the "User-Agent" the "client-request-id" and "Date". For User-Agent you can follow RFC 2616 which basically describes it as {ProductName}/{ProductVersion} [{comment}]. For "client-request-id" your app should create a new GUID for each request. When the request fails "return-client-request-id" returns the GUID that was submitted as "client-request-id" with the request. It is highly recommended that you persist the failed request together with client-request-id and all HTTP response headers in some application log. The "Date" request header signals the date and time that the message was sent. It should follow the "HTTP-date" format as defined by RFC 2616)}. If you ever need help troubleshooting your application with the Office 365 APIs, this will be pave the route to a fast and successful resolution of the problem.
Summary
To sum up:
· With the availability of the OAuth2 client credential flow it is now possible to build daemon or service apps in addition to device and web server applications.
· The installation or bootstrapping of those service apps is very similar to web server apps and requires an administrator of an Office 365 organization to consent.
· Unlike web server apps, which can use symmetric keys, a service app must use X.509 certificates as authentication strength to acquire access tokens and successfully call the Mail, Calendar and Contacts APIs.
· Service apps have the requested access level to all mailboxes in the Office 365 organization the administrator has consented to.
· Service apps can be revoked by an administrator of the Office 365 organization in the same way like web server apps.
As always, thank you for reading and feedback, questions, etc. are very much appreciated!
Please use Stack Overflow for questions, and be sure to tag your post with "office365".
Matthias Leibmann
References
OAuth2 client credential flow: https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx
OAuth2 client libraries: https://msdn.microsoft.com/en-us/library/azure/dn151135.aspx
AAD on GitHub: https://github.com/AzureADSamples
OAuth Sandbox: https://oauthplay.azurewebsites.net/
API Sandbox: https://apisandbox.msdn.microsoft.com/
Platform Overview: https://msdn.microsoft.com/en-us/office/office365/howto/platform-development-overview
Apps for Office: https://msdn.microsoft.com/en-us/library/office/fp161507(v=office.15).aspx
Office on Github: https://github.com/OfficeDev
Sample Web App to this Blog: https://github.com/mattleib/o365api-as-apponly-webapp
Comments
Anonymous
February 04, 2015
Hi Matthias, I have asked a question on Stack Overflow stackoverflow.com/.../building-daemon-or-service-apps-with-office-365-mail-throwing-401-error-code Can you please suggest?Anonymous
February 05, 2015
Hi Matthias and Team, I am having issues with the asymmetric key authentication. I'm using Ruby so its hard to follow the examples. However I have created a Stack Overflow question here: stackoverflow.com/.../office-365-rest-api-daemon-week-authentication Thanks, Nick.Anonymous
February 05, 2015
My colleague Jason Johnston and I provided some answers/insights on the stackoverflow.com/.../office-365-rest-api-daemon-week-authentication. Please also note after you are over the hurdle of getting the access token, if you get any 401 errors when hitting the Calendar, Contacts and Rest APIs to look at the x-ms-diagnostics http response header.Anonymous
February 06, 2015
It'd be great to have a sample console application that does something simple like listing the messages of a particular user's inbox. I've tried for a while now to get something running to no avail. For a while I was getting Unauthorized exceptions, but then finally got to a point with an access token and now get "Access is denied." exceptions.Anonymous
February 07, 2015
@Robert: you mean a console app that uses Rest API or EWS API and the described app-only flow or different flow? Let me know. I will see what I can do.Anonymous
February 08, 2015
Hi Matthias, Everything worked fine for me! It worked and I was able to fetch calendar and mail data, but now suddenly it has started throwing me an error: Caused by: com.microsoft.aad.adal4j.AuthenticationException: {"error_description":"AADSTS70002: Error validating credentials. AADSTS50064: Credential validation failed.rnTrace ID: 4d47209b-de38-4bfe-849e-4f999bd6d374rnCorrelation ID: 78e90f01-01f8-4c71-956f-18528061e05brnTimestamp: 2015-02-09 08:27:05Z","error":"unauthorized_client"} I have asked question on stack overflow related to this on link: stackoverflow.com/.../building-daemon-or-service-apps-with-office-365-mail-throwing-aadsts70002-error. Can you please suggest?Anonymous
February 08, 2015
@Matthias, I've been attempting to use the SDK (which I assume uses the REST API behind the scenes). I'm getting an access token and an OutlookServicesClient, but then fail with authentication issues when I try to... IPagedCollection<IMessage> messagesResults = await client.Users.GetById("the-user-guid").Messages.ExecuteAsync(); Yes, app-only flow (i.e. a service or scheduled console app that runs with no user interaction to perform management functions). An example of this flow would be amazing and much appreciated! I have a feeling I'm like 95% there, but missing something.Anonymous
February 08, 2015
@Matthias, I have a feeling that 1 thing I'm missing is the "initial one-time consent". Although I'm currently getting an access token, I haven't done any OAuth flow to do a one-time consent. Maybe that's what I'm missing.Anonymous
February 09, 2015
@Matthias, for what it's worth, I got a working example now using some code based off your "o365api-as-apponly-webapp" example. That example doesn't use the SDK calls but rather HttpClient, so I'm not sure where the breakdown is.Anonymous
February 15, 2015
Would this daemon/service app work for native mobile apps ? Or would I have to build a web api to get the data I need then access this through my native app?Anonymous
February 18, 2015
@Robert: It should be possible to use the Office 365 SDK. E.g. something like below: OutlookServicesClient exClient = new OutlookServicesClient(dcr.ServiceEndpointUri, async () => { var authResult = await authContext.AcquireTokenAsyn(......); return authResult.AccessToken; }); So, in theory, you should be able to get the app only access token in the delegate. Wrapping this in the delegate means that the code will be executed only when the Outlook client needs. And yes, you need to consent if your app want to access resources in other tenants. When you create your application in a tenant it automatically has the permissions granted for the tenant it is registered, but no other other tenant. For your app to work in other tenants, it requires that an admin in a tenant consents to the permissions. This will "install" under the cover your app (technically correct would be to create a app principal for your app with those permission in the consented tenant). So you are indeed probably very close getting this all working with the SDK :-) @Kelly: Yes, indeed the correct way would be to create a Web API (service) for your device app. You then protect your Web API by for example integrating it with AAD (many examples there on github, e.g. github.com/.../NativeClient-WebAPI-MultiTenant-WindowsStore). Your Web API then would get an App-Only token to do the work, and you would never expose the App Token to the device app.Anonymous
February 22, 2015
@Robert, how did you do this ( "initial one-time consent" ) in your client console application ?Anonymous
February 22, 2015
The comment has been removedAnonymous
February 23, 2015
@Pranjal : I'm facing the same issue as you mentionned. Did you managed to retrieve a token ? I could not find your stackoverflow question as it was probably removed, but I opened one for my issue here if anyone want have a look ? stackoverflow.com/.../active-directory-authentication-for-java-application-with-oauth2-client-credenti Thanks a lot.Anonymous
February 23, 2015
@Srabon : I may have missed that part too ( "initial one-time consent" ) as I could only understand that this part was refering to the configuration of the application in the Azure AD management. Could you confirm if it needs any other operation ? ThanksAnonymous
February 25, 2015
I finally managed to retrieve an access token by upgrading my common-codecs artifact from 1.4 (with which it was not working) to 1.10 (took the last one available, but don't know from which version the correction was made). We got thinking of the common-codecs artifact because the JWT token presented had a public certificate value with "rn" inserted when generated by my app, whereas it hadn't when it was generated from a dedicated sample test app. Those "rn" characters were inserted into the "x5c" JWT header property by a call to org.apache.commons.codec.binary.Base64.encodeBase64String(byte[]) from com.microsoft.aad.adal4j.AsymmetricKeyCredential.getPublicCertificate(). It appeared that my app had already a depency to common-codecs v1.4 which didn't suit to adal4j.Anonymous
February 25, 2015
@priya: The resource to specify is "outlook.office365.com/". The error message means that the resource that is specified in the request is not registered in the auth server (AAD), which is correct as "office.microsoft.com/outlook" is not registered on our application object for Exchange Online.Anonymous
February 25, 2015
@Robert/Srabon/Eric: I don't think you can trigger admin consent in a console app, though I never tried it. Though as you don't care about the redirect you can probably bootstrap a console app by simply calling the authorize endpoint and assume the consent happened. Then in your console app if the token request fails with an unauthorized error indicating consent is missing, you can surface this in some way to the system (e.g. logs, eventlogs, alerts). Probably better is to have some kind of "install the app" experience that is triggered at time when the app is acquired. It all depends on the scenario. E.g. if the app can be categorized as a "LOB app" that each organization acquires and installs separately you can also envision that a different AppID is created with a different key for each individual organization. But this would be a compete different route than treating it as a "multi-tenant" app (using the same AppID for all organizations signing-up for the service) with a different experience (a setup) you would have to build. I would have to look into possibilities if this can be for example automated via powershell tasks. Again, this would mean you build some kind of a setup one has to run before using the app, vs. leveraging the consent model that allows your app being made available in an organization by means of a sign-up experience via a web driven authorization flow.Anonymous
February 25, 2015
@Eric. Thank you for figuring this out and positively closing the loop with us! I'll let the developers know that this was the issue to stop pulling their hairs what's going on :-)Anonymous
March 01, 2015
Hi Matthias I followed your steps to create a certificate and uploaded the manifest file . It was successfully uploaded . when i try to download the file again and view i find the value field as null . Is it a mistake on my side ?Anonymous
March 08, 2015
@Archana : no I'm in this case too, but though this is quite disturbing, don't bother with it, it doesn't matter.Anonymous
March 10, 2015
In that case building the service apps look like a good idea.Anonymous
March 15, 2015
Is is possible to get a walkthrough or example on how to utilise this method with PHP? I'm struggling to utilise this method with PHP.Anonymous
March 16, 2015
I need help to implement on an apps php. Can you give me some help please ?Anonymous
March 16, 2015
Check out the git of my colleague Jason Johnston with a php example accessing calendar: github.com/.../php-calendar. While this is not client credential flow, it implements the code flow which probably is what you want for a regular user based web app.Anonymous
March 16, 2015
A brief note on EWS access as I did not mention this in great detail in my blog: EWS access is supported with app only in following way: 1. Configure the app for "Full Access to users mailbox" application permission - in the App Token this is "full_access_as_app". 2. Set the EWS impersonation header. 3. Set the "X-AnchorMailbox" http request header to the mailbox you like to access.Anonymous
March 17, 2015
thanks for this information. But i want to implement client credential flow with Php. Can you help me please?Anonymous
March 17, 2015
I would also be interested in some help regarding implementing this credential flow via PHP. The problem I'm having is that the library you are using (ADAL) has no PHP equivalent that I am aware of, and am thus unsure how anything is working within that library. Even a breakdown of what is going on during those library calls, what requests are made (with what data, and their responses) would be incredibly useful. I have the certificate, etc, but no idea what to do with it. Cheers!Anonymous
March 18, 2015
While we don't have a php example, we do have a .net and python example. Checkout the .net one in my git on github.com/.../o365api-as-apponly-webapp and the python one on github.com/.../python_clientcred. Both show you how to craft the payload and make the token request without the ADAL library and how to parse the response.- Anonymous
May 07, 2016
Hello Matthias, can you build an using PHP? I hope you can. Thank you - Anonymous
May 07, 2016
I have a project that I think the appropriate approach is daemon because it need to process without human intervention. I hope you can give an example using PHP. Thanks
- Anonymous
Anonymous
March 18, 2015
Thanks so much for the links, extremely helpful examples!Anonymous
March 24, 2015
Is it possible to use app only token to query "Discovery Service REST API"? It doesn't seem to work giving "access denied" error.Anonymous
March 31, 2015
The comment has been removedAnonymous
March 31, 2015
I've narrowed down the call to https://graph.microsoft.com... {"odata.error":{"code":"Authorization_RequestDenied","message":{"lang":"en","value":"Insufficient privileges to complete the operation."}}}Anonymous
March 31, 2015
The comment has been removedAnonymous
March 31, 2015
Thanks Jason & Matthias - woks like a treat now! One thing puzzles me in this sample - I supply the Admin user/pass for the one-off authorization, but this happens each time I launch the app. Is this just because of the App and it's not saving the fact it's been already registered for that tenant? Great example - now slice and dice it.Anonymous
April 28, 2015
HI Matthias, Is (OneDrive) File API supported in this style? stackoverflow.com/.../access-all-domain-users-file-in-offie365-onedrive Thanks, WeiAnonymous
April 30, 2015
Since there's no user context via OAuth client credential flow, how would one use the Discovery API with an access token? msdn.microsoft.com/.../discovery-service-rest-operations requires a/me
context.Anonymous
May 04, 2015
I have created a PHP example on how to implement the office365 OAuth2 client credential flow. github.com/.../php_office365_cleintcredAnonymous
May 05, 2015
Very nice Mark! Thank you for doing this and sharing!Anonymous
May 05, 2015
@Jake and Igor: You are correct. App-Only does not work for Discovery. @Wei, yes we recently added App-Only support of OneDrive for Business. Let me try to find out some more information for you on this.Anonymous
May 11, 2015
Please anyone can tell how to use x.509 in php or calling office 365 API.Anonymous
May 14, 2015
It would be great to see an openSSL alternative to makeCert + Powershell....for all the Linux dev's out there. I just spent much longer than I care to admit getting makeCert and Powershell up and running on a VM.Anonymous
May 15, 2015
The comment has been removedAnonymous
May 24, 2015
Hi Matthias et al, I also want to know if this access token mechanism work with the OneDrive for business? From my understanding behind the OneDrive for business are sharepoint servers, so theoretically the Sharepoint API should also work with OneDrive for business, as I know in Sharepoint API, there is SPUserToken method to do the Impersonate to other users, can you confirm if this method work with OneDrive for Business? Any more information about the OneDrive for business or some samples will be really helpful. Thanks in advance. JamesAnonymous
May 28, 2015
what is the point to set "X-AnchorMailbox" header?Anonymous
May 28, 2015
The "X-AnchorMailbox" is a routing hint for our front end servers to which backend to route the request for processing. You specify the e-mail address of the mailbox you want to get data from, and then our front ends lookup where the current active mailbox server is and deliver the request to this server. Hope this helps, MatthiasAnonymous
May 31, 2015
Blocked by the following two errors during Compilation of GIT Sample Project, can you let me know which step I missed? Could not copy the file "AccessMailboxAsAppContentmyappcert.pfx" because it was not found. Could not copy the file "AccessMailboxAsAppContentencryptionCert.pfx" because it was not found.Anonymous
May 31, 2015
Hello Matthias, For the GITHub Sampel Project, I just asked a question on Stack Overflow, could you please suggest? stackoverflow.com/.../building-daemon-or-service-apps-with-office-365-mail-compilation-errorAnonymous
June 03, 2015
Hi Matthias, is there any examples how to do queries to sharepoit using app only token?Anonymous
June 08, 2015
Hello Matthias, The project is up&running now, thanks to help of Jason. Yet I ran into another problem, could you please suggest? stackoverflow.com/.../building-daemon-or-service-apps-with-office-365-that-doesnt-require-sign-inAnonymous
June 11, 2015
@Dmytro. I am still trying to find out what the exact plan is on App tokens for SharePoint. Can you actually send me e-mail to mattleib@microsoft.com, so I can include some people from SharePoint team.Anonymous
June 11, 2015
@Ree: I saw that Jason already helped you there. Thank you Jason for being on the ball! :-)Anonymous
June 11, 2015
The comment has been removedAnonymous
June 15, 2015
Hi Matthias, I've worked on the problem a bit and I've some new results: For the authorize, I found that passing my admin email through login_hint parameter, it work, and gave me a "HTTP/1.1 100 Continue", that I think is an Ok, you're authorized.. Now, calling the api, I have a http 500 error, as I described here: stackoverflow.com/.../office-365-unified-api-object-reference-not-set-to-an-instance-of-an-object But, after a day of try, I've a question: Does the new unified api work with this authentication workflow? Or only the "exchange" api works? I've tried with the old sharepoint api, and they return to me that app-only token is not supported.. So, how can I create a daemon that access the user files? If you can answer, I would really appreciate (even on stackoverflow), because I need to decide if I need to change platform and don't use office 365. ThanksAnonymous
June 16, 2015
Hi Kira, sorry for the delayed response and thank you for working through this. First some brief things:
The Unified API does not support yet App Tokens
OneDrive also does not support App Tokens yet - both #1 and #2 is in the plans, but no solid ETA I could refer to. If this changes I will let the blog audience know as soon as I do
login_hint should not be required to make consent work
you only need to authorize the application once - you authorize against the authorize endpoint. Make sure you configured your app as multi-tenant app, so authorization works for all tenants.
To authorize, an admin of the tenant need to provide consent via a standard OAuth2 authorize flow. No secret and cert is required there
The result of #4 should be that your app knows that a tenant signed-up and maintains this information
after successful #4, your app never goes back to the authorize endpoint, but always gets new access tokens by calling the token issuing endpoint for a signed-up tenant. For this you need to assert your applications identity to the token issuing endpoint. You do this by means of a signed assertion. We require that this assertion is based on a private key, as service apps are quite powerful we believe this is the correct approach.
There is currently no other way for an app to authorize / install into a tenant than doing #4. Hope this helps. Thank you, Matthias
- Anonymous
June 21, 2016
Hi Matthias,You have mentioned the OneDrive does not support App Tokens yet on June 16, 2015 at 8:49 pm. Can you update me the current status of the same. I am looking to access all user's file through admin user token, like accessing the mails and contacts through office365 API.Thanks,Suresh
- Anonymous
- Anonymous
June 16, 2015
The comment has been removed - Anonymous
June 17, 2015
Hi Mix
- I guess you will not get access token from assertion, any way your daemon should be configured with tenant id, easyiest way to get tennat id is to pass user via authorization flow. So idialy it will go so: - user visits webpage with button "install app" - after he clicks on normal oauth2 flow goes - during this flow tennant id is obtained. - daemon provisioned with tennant id - now daemon can get app only access tokens
Anonymous
June 25, 2015
Hi Matthias. When I upload the application manifest with keyCredentials for my self-issued certificate to Azure AD it says "Successful". But when downloading it for checking I find the "keyCredentials": [ { ... , "value": null }]. Is this normal behavior?Anonymous
July 28, 2015
Is this a Security Issue in "Client credential flow"? Hi Matthias, I am trying a scenario in which I fetch OAuth token using approach you have explained in above document, without doing the first step i.e, "Granting consent and app authentication strength", I did not do Admin consent and directly call EWS Java API passing this OAuth token. It gives me data of users, and everyhting works. My questions is: if everything works without step 1 of admin consent, why that step is needed?Anonymous
August 12, 2015
Hi Matthias, Is the certificate mandatory for client credentials flow ? I have made a Rest API call to my application token endpoint URL with client id and client secret and resource as "https://outlook.office365.com", I have got the access token, but with this token from a standalone java application, when I pass this in Authorization header with Bearer and this token, it gives 401 Unauthorized and when Trace is enabled, I get the following x-ms-diagnostics : 2000010;reason="The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2.";error_category="insufficient_auth_strength" When I tried to google for this insufficient auth strength, it says that X.509 certificates are mandatory, is that the way client credentials flow works ? Please let me know. Thanks and Regards, Rama.Anonymous
August 12, 2015
x-ms-diagnostics : 2000010;reason="The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2.";error_category="insufficient_auth_strength".. When does this error come up? Is it when access token is acquired just with client id and client secret without X.509 certificates?Anonymous
August 12, 2015
Yes, certificates are mandatory and you will get that error if you just use a secret.Anonymous
August 13, 2015
I am using the example project from github.com/.../o365api-as-apponly-webapp and followed the instructions in the blog. I did the sign in and allowed a number of permissions to the app. Then when I try any operation on the Mail, Calender or Contacts it returns me the following error: { "error": "Unauthorized", "x-ms-diagnostics": "2000001;reason='The token is missing the claim type 'roles'.';error_category='invalid_token'" } Could you please tell me how I can fix this? Thanks in advance. MarcelAnonymous
August 13, 2015
Hi Matthias, I created a self signed certificate with openssl to create .pem file. I open the file with text editor which had the string between -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- , is that equivalent to base64value. How do i use openssl to get the base64 string Can you please help in generating the $bin = $cer.GetRawCertData() $base64Value = [System.Convert]::ToBase64String($bin) $bin = $cer.GetCertHash() $base64Thumbprint = [System.Convert]::ToBase64String($bin) $keyid = [System.Guid]::NewGuid().ToString()Anonymous
August 13, 2015
Figure it out already, I was setting Delegated Permissions instead of Application permissions. So this causes the error: { "error": "Unauthorized", "x-ms-diagnostics": "2000001;reason='The token is missing the claim type 'roles'.';error_category='invalid_token'" } MarcelAnonymous
September 09, 2015
Hi Matthias Thanks for valuable article it helps me a lot. but now i have to create calendar by this flow. can you suggest me the way. mayankAnonymous
September 30, 2015
Hello Matthias, I just asked a question on Stack Overflow, could you please suggest? stackoverflow.com/.../building-daemon-or-service-apps-with-office-365-mail-secret-upload-automationAnonymous
October 05, 2015
The comment has been removedAnonymous
October 07, 2015
The comment has been removedAnonymous
October 08, 2015
403 usually means you are passed authentication (token validation was passed), and now the API does access check against the requested resources and permissions in the token. You can decode the access token at http://jwt.calebb.net. Does it have the right permissions for the graph in the "roles" claim-type?Anonymous
October 13, 2015
Hi Matthias, Thank you very much for this insightful post! (Though, you could remove the Word formatting making it very hard to read. ;-)) I have one question regarding the app permissions: We want to build a web app that has access to certain user‘s calendars of our tenant (read/write) using client credential flow. But I don't want the app to have the ability to access all user's calendars. Is it possible to specify a certain access range for the app or only assign certain users? Can you help me there? Thanks Thomas- Anonymous
August 18, 2017
@Thomas - did you figure out a way to limit the scope of calendars read? I'd also like to limit which calendars my app is able to read. Thanks!
- Anonymous
Anonymous
October 27, 2015
Great post! But like others, I'm running into issues. I can click the link that redirects to Office365. I accept to connect the app. When redirecting to my app, I get an error in the 'consent' view when trying to set the access_token. I manually went through the steps in the 'get_access_token' function and tried to make the post request in Advanced Rest Client, and I get a 400 error: {"error":"unauthorized_client","error_description":"AADSTS70002: Error validating credentials. AADSTS50064: Credential validation failed.rnTrace ID: 63b501c5-296a-4235-a7af-4296441721f0rnCorrelation ID: 5513b3b3-6e2e-4b37-927d-0b6bad48a515rnTimestamp: 2015-10-27 19:54:35Z","error_codes":[70002,50064],"timestamp":"2015-10-27 19:54:35Z","trace_id":"63b501c5-296a-4235-a7af-4296441721f0","correlation_id":"5513b3b3-6e2e-4b37-927d-0b6bad48a515"} Any ideas?Anonymous
November 12, 2015
any update on the SharePoint support?Anonymous
November 13, 2015
When adding the value to the key credentials, should the base64 of the certificate data have the rn in it or not? There's a comment from somebody who had a problem with it, but he didn't actually say which way was the correct one.Anonymous
December 14, 2015
Hi Matthias , Thank you for the blog. I have completed all the steps correctly but when I try to get all the events with the access token I get the following error message in the x-ms-diagnostics : reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant". I am using the following curl command curl -v outlook.office.com/.../events -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsI......." Could you please help me to resolve this. Thanks.Anonymous
January 13, 2016
HI Matthias, Thanks for this article, it was a great help once I eventually found it! I'm having some issues with permissions when performing moves/deletes, please could you take a look at my SO post? stackoverflow.com/.../deleting-an-email-using-the-office-365-mail-rest-service-from-a-background-daemo Thank you, JamesAnonymous
January 17, 2016
Hi Matthias, The process described in the article works well. However, we'd like to offer our customers a simple solution that does not require an admin consent. It should allow users to view/edit events in meeting room calendars according to their permissions as allowed within the Office365 UI. Which version of the API/APP can support that? Thanks in advance, TommyAnonymous
January 21, 2016
Hey Matthias, Would this work on on-premise environment?Anonymous
January 31, 2016
@Tommy: This sounds like you want to build a app a user signs-on to work with their calendars and calendars they have permissions (are shared with them). We're currently working on making this available and will introduce a new permissions "Calendars.ReadWrite.All" that will allow you to build such an app. The OAuth flow to use would be a regular code flow where the user consents to the app. I cannot give you a firm ETA for this work when it will light up in production, but it's not too far out. @Shweta: We're looking to enable on-premises OAuth2 support targeting end of Q1/Q2 in two variations, in a pure on-premises deployment using ADFS 2016 as authorization server, and in a hybrid deployment where AAD can be the authorization server for on-premises.- Anonymous
August 18, 2017
@Matthias - I have the same requirements as Tommy, and would like to read all calendars visible in the Outlook UI without requiring admin authorization. It sounds like the "Calendars.ReadWrite.All" scope you hinted at would serve my need. Is that still on the roadmap? Thanks!- Anonymous
August 21, 2017
Yes. We have implemented the permission with a slightly different name: "Calendars.Read.Shared".
- Anonymous
- Anonymous
Anonymous
January 31, 2016
@Sarang. Did you resolve your issue? From the error returned by our auth module (in x-ms-diagnostics) it seems the app token has no app permissions in it. Make sure you select "Application Permissions" in the app registration and configure appropriate permissions for your app there. Once you get the app token you can copy&paste it into jwt.calebb.net and check if it contains a "roles" claim that expresses the app permissions you selected. If it has no "roles" claim something is not right with the app permission configuration.Anonymous
February 26, 2016
The comment has been removedAnonymous
April 03, 2016
Hi,I was able to get access token and when I used this access token to access the Outlook Mail API via request, I was getting "401 Unauthorized" error.ERROR: x-ms-diagnostics: 2000001;reason="The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2.";error_category="invalid_token"Can you please suggest?Anonymous
May 04, 2016
Hi Matthias,Can a daemon using the client credentials flow subscribe to Notifications from a particular user's mailbox using the Notifications Rest API? Is there any plans to support such a functionality? Stackoverflow link: http://stackoverflow.com/questions/37010223/office-365-mail-statisticsAnonymous
May 10, 2016
In Client Credential Flow, while registering an application and grant delegation for just one user rather than all the users? I want to develop a deamon service which just polls one user office365 mailbox and I dont want to give access to this app to all the users. In the first paragraph of this article, "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." Once granted the access to email to application by a user, how long the refresh token would work, assuming user never changes his password, will it work forever?- Anonymous
June 06, 2016
Hi MatthiasLike the previous comment, I want to develop a service type app using application permissions rather than asking the user to sign on. Effectively the app will have its own mailbox. It is for an institution and there is no way they will want to give Mail.Read to all mailboxes to a third party app. Is there any way of limiting access to a single mailbox and using this type of authentication? Thanks in advance for any ideas...Paul
- Anonymous
Anonymous
June 07, 2016
The comment has been removed- Anonymous
June 07, 2016
Here is the SO question: http://stackoverflow.com/questions/37689827/is-it-possible-to-grant-tenant-specific-app-only-tokens-for-o365-api-azure-ad
- Anonymous