Office 365 Online Exchange (Calendars)- Access to OData is disabled - microsoft-graph-api

You can see in the request URL that it's addressing the URL of Office 365 https://outlook.office365.com/api/v1.0/users/{user-email}/calendarview
Request:
GET /api/v1.0/users/testuser#testapp.onmicrosoft.com/calendarview HTTP/1.1
Host: outlook.office365.com
User-Agent: myappagent/1.0
Authorization: Bearer eyJ**********
Accept: application/json
Prefer: outlook.timezone=Europe/London
Response:
HTTP/1.1 403 Forbidden
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
Server: Microsoft-IIS/10.0
request-id: d5ae24e8-0c34-43b5-a8a3
X-CalculatedFETarget: *******.internal.outlook.com
X-BackEndHttpStatus: 403
X-FEProxyInfo: *********.PROD.OUTLOOK.COM
X-CalculatedBETarget: *********.prod.outlook.com
X-BackEndHttpStatus: 403
X-RUM-Validated: 1
X-BeSku: WCS6
x-ms-appId: ********
OData-Version: 4.0
X-AspNet-Version: 4.0.30319
X-DiagInfo: ********
X-BEServer: ********
X-Proxy-RoutingCorrectness: 1
X-Proxy-BackendServerStatus: 403
X-FEServer: ********
X-Powered-By: ASP.NET
X-FEServer: ********
Date: Fri, 18 Jun 2021 20:22:02 GMT
{
"error":{
"code":"ErrorAccessDenied",
"message":"Access to OData is disabled."
}
}
I have came across this article:
https://learn.microsoft.com/en-us/archive/blogs/wushuai/how-to-fix-access-to-odata-is-disabled-when-calling-graph-api
But I don't think it applies since I'm using the Office 365 Calendar API.
When I checked my access policy, it's empty. Which I'm assuming means there's no restrictions, and therefore my app shouldn't have any problem.
And these are the permissions of the app (I added Graph API calendar just in case, but still same 403 response)

You are using a very old api endpoint, it is recommended that you use the latest ms graph api.
For your question, you need to use the client credential flow to get the token, and then specify the scope as: https://outlook.office365.com/.default.
Parse the token:

After long research and trying. I realized the solution is in the (ApplicationAccessPolicy).
Reference :
https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access
https://learn.microsoft.com/en-us/powershell/module/exchange/get-applicationaccesspolicy?view=exchange-ps
Helpful Powershell commands :
Test-ApplicationAccessPolicy
Get-ApplicationAccessPolicy
New-ApplicationAccessPolicy
Remove-ApplicationAccessPolicy
Set-ApplicationAccessPolicy
Hope this is helpful for anyone stuck with this issue ;)

Update:
Did you see this blog post? https://developer.microsoft.com/en-us/outlook/blogs/outlook-rest-api-v1-0-deprecation-update/
The API you're using is announced to be deprecated roughly 40 months ago (October 2017). And is actually deprecated April 30th 2021, so you're in luck if it still works for some tenants, but it will stop working very soon.
You could switch to the v2 endpoint of the outlook api, I guess that will be the most compatible. But that endpoint is also announced to be retired https://developer.microsoft.com/en-us/outlook/blogs/outlook-rest-api-v2-0-deprecation-notice/
My advice, enjoy the outlook api while you still can, but invest a little time to convert to the new Graph endpoints if you want your app to continue to work in the near future.
Old (irrelevant) answer:
Have you checked the aud claim of the token? Copy the token and paste it at https://JWT.ms
It should be https://outlook.office365.com (since you’re using the old api).
May I ask why you would want to create a new application that talks to the old api?
Apart from the obvious stuff, the documentation you’re referring to also talks about an “allow list”, if it’s empty no application has access, that is also possible.
The new api (graph endpoints) has an option to do resource scoping (allow an application with client credentials, to certain mailboxes instead of all). If that is configured in the tenant I’ve seen similar errors.

Related

Microsoft Graph API findMeetingTimes endpoint returning incorrect response

Yesterday (December 16, 2020), I noticed that the findMeetingTimes endpoint started returning an empty response object (when testing using Graph Explorer) for requests that would previously result in some meetingTimeSuggestions.
I've been using this endpoint for a while and haven't changed how I interact with it.
Example:
{"attendees":[{"emailAddress":{"address":"<EMAIL>"},"type":"Required"}],"timeConstraint":{"timeslots":[{"start":{"dateTime":"2020-12-19T21:52:35.093Z","timeZone":"Pacific Standard Time"},"end":{"dateTime":"2020-12-26T21:52:35.093Z","timeZone":"Pacific Standard Time"}}]},"meetingDuration":"PT1H"}
Looking at the "Find meeting times" documentation, I'm not seeing any major changes to the API (and the reference was last updated over a month ago).
Any ideas on what might be going on?
I've checked StackOverflow, the "Known issues with Microsoft Graph" page, and Twitter, and I've also done some Googling but haven't found much on this.
It's actually not empty, it's even weirder. It started aggressively returning a gzip encoded body (violation of RFC) and ignoring Accept-Encoding header (NB: If you don't put "Accept-Encoding: gzip" it won't event say Content-Encoding:gzip but it is!)
I don't know how one can log an API bug, and tho I just paid for a "support package" it didn't apparently go through yet. So I'm going to hack my back-end to detect the gzip magic bytes ( 1f 8b ) and gunzip it.

Get HTTP Status Description from RestKit .20.3

I'm able to get the status code just fine, but I'm looking for a way to get the "HTTP Reason Phrase" from the response. I've spelunked through RKObjectRequestOperation and don't see it anywhere. I feel like I'm just missing it. Is this something that's supported by RestKit?
In my particular case, the service I'm hitting is sending this back:
HTTP/1.1 409 A user with this phone number already exists
Cache-Control: private
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 24 Feb 2015 22:13:21 GMT
Content-Length: 0
I'm trying to get at the "A user with this phone number already exists" bit.
Thanks very much in advance.
This isn't a RestKit thing, it's a URL handling system thing as it is the HTTP underpinnings that RestKit is built on top of.
So it should be provided by localizedStringForStatusCode: from the NSHTTPURLResponse that you can ferret out of the RestKit operation.
To be honest I've never had cause to use it so I'm not sure if it's purely a local client lookup from the returned status code or if it does link to any server response data. As the docs reference RFC 2616 it should pick up the text if the server sends it out...

How to connect iOS App to Symfony2 RESTful Webservice?

I have an iOS app that is working and able to connect to a RESTful PHP Webservice. The webservice was initially built in native PHP and now I am migrating it to a Symfony2 web service. On the iOS side, I am sending requests as JSON, the server processes the data and returns a JSON response data, everything works pretty well.
Now, my symfony2 webservice is able to respond with the correct response to requests made from the browser window or terminal using httpie (for testing) and it returns json data in the browswer window if I specify .json in the parameters. However, I am not sure how to make the iOS app send the request rather than the browser. I am using the FOSRestBundle if that information is of any significance. Here is an example from the terminal:
http http://serveripaddress/web/app_dev.php/users/1.json Accept:application/json
It returns json data representation of the user with the id = 1 as expected:
HTTP/1.1 200 OK
Allow: GET, PUT, DELETE
Cache-Control: no-cache
Content-Type: application/json
Date: Fri, 29 Nov 2013 20:15:43 GMT
Server: Apache/2.2.22 (Ubuntu)
Set-Cookie: PHPSESSID=n8o72oq61isruuov1bqgc9udf5; path=/
Transfer-Encoding: chunked
X-Debug-Token: 6c3818
X-Powered-By: PHP/5.3.10-1ubuntu3.8
{
"id": 1,
"last_name": "Robinson",
"first_name": "Jack",
....More data truncated ...
}
From my browser, I can get the same result by using: http://serveripaddress/web/app_dev.php/users/1.json in the address bar. How can I make the iOS app send the same request? I guess the only change I need is in the symfony2 code to accept the request? Kindly give a brief example and I can figure out the rest from the example.
Edited with more detail:
Just to clarify, what I need is how to initiate a connection to the server-side REST Symfony2 application. Again, this I have previously done when using native PHP but how do I connect to a Symfony2 route? While using the native version, what I did was to construct a dictionaryWithObjectandKeys passing key value pairs of the command (on the PHP side) and the arguments as JSON and having the server side return JSON data response and a success or failure, simple and straightforward. How can this be done if I am using Symfony2 which I guess the commands now become a route? I am using AFNetworking and everything on the iOS client side should not need changes except the part where I connect to Symfony2. Kindly give an example to give me a concrete picture.
You can use AFNetworking 2 for example. It is a complete framework that allows you to consume web services (and more).
By the way, you could simply use (and fire) NSURLRequest, but if your application interact a lot with a webService i recommend to use AFNetworking. (Trust me, it is really something that you want to know how to use if you want to make apps on iOS)
I think binding to session and cookies may violate REST nature. I strongly recommend you to use something like WSSE. You can use EscapeWSSEAuthenticationBundle for WSSE. Or create your own.
Also useful link to dive deep:
How to create a custom Authentication Provider
Configure WSSE on Symfony2 with FOSRestBundle

Is there a way to get Indy to accept expired cookies?

Using Delphi XE2 Update 4 and Indy components 10.5.8.0 as installed by Delphi.
A website I am trying to work with is deliberately sending me a cookie with an already expired date for security reasons. It is deliberate on their part and I have to live with it and other developers working with them have managed to do so.
The server response to my IdHTTP.Get() looks like this:
Recv 8/30/2012 3:06:15 PM: HTTP/1.1 200 OK<EOL>
Date: Thu, 30 Aug 2012 19:06:35 GMT<EOL>
Server: Apache/2.2.3 (CentOS)<EOL>
X-Powered-By: PHP/5.1.6<EOL>
Set-Cookie: dati=eJxLtDKxqi62MrdSykyJLy1OLVKyLrYytVIyNjcxtISyc%2FLTM%2FNAbKAqt4CgYJfUMhDPEqinOB4omZ6aAuIbWilFgmkDkGGpeSWZaZkQ8wyNrZTSchKLMwryM%2FNK4ouKlaxrAYdvJUQ%3D;
expires=Thu, 01-Jan-1970 00:03:00 GMT<EOL>
Content-Length: 16<EOL>
Connection: close<EOL>
Content-Type: text/plain;; charset=ISO-8859-1<EOL><EOL>
0 24.141.251.145
Note the 1970 expiry! I have been using Indy for all of 3 days now and as near as I can tell this attempt to create a cookie is simply ignored by IdHttp and the associated CookieManager.
Assuming I need to receive and use the cookie anyway what would be the proper approach to capture it, or should I just run away screaming now? I have a lot of Delphi experience but this is my first foray into the wonderful world of internet connectivity and this expired-on-arrival cookie business is leaving me scratching my head!
Sending an expired cookie is how a webserver deletes a cookie on the client end. Indy's cookie manager supports that. When it receives a cookie, it will delete any existing cookie of the same name/domain/path trio and will then discard the new cookie if it is already expired. This is hard-coded behavior.
If, for whatever reason, you have to keep the expired cookie (which does not make sense as no webclient should ever do that), you will have to use the TIdCookieManager.OnNewCookie event to alter the expiration of the cookie after it is parsed by TIdCookie but before it is processed by TIdCookieManager. You can set the TIdCookie.Expires property to a future date, or to 0.0 to disable its expiration. You will have to do this anyway, as TIdCookieManager implements cleanup of expired cookies (which is triggered whenever TIdHTTP asks for cookies to send in an HTTP request), so if you don't alter the expiration then the cookie will just get discarded anyway at a later time.

MVC Framework Browser Caching Issue with RC1

In my latest project which is in RC1 I have noticed that I have this browser caching issue that I just can't shake. This is what my header looks like
HTTP/1.1 200 OK
Date: Tue, 03 Mar 2009 15:11:34 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
X-AspNetMvc-Version: 1.0
Cache-Control: private
Expires: Tue, 03 Mar 2009 15:11:34 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 4614
Now technically if this is private it shouldn't have an expiration date on the content right? I've tried no-cache as well with the same results. Anybody have any experience with this particular issue?
Cache-Control: private only specifies that the response is only intended for a single user and should not be stored in a shared cache (say, in a proxy) and used to serve requests for other users. I don't see anything in the protocol documentation that would preclude the use of an Expires header with a value. In fact, it seems a perfectly reasonable thing to say "use this for subsequent requests for this user only, but not after this time." There are other values for Cache-Control where Expires may not make sense, but I believe that the protocol has a means for disambiguating between conflicting headers (See section 4 of the protocol docs).
Quoting from Section 16.2 of the HTTP 1.1 protocol docs:
private
Indicates that all or part of the response message is intended for
a single user and MUST NOT be cached by a shared cache. This
allows an origin server to state that the specified parts of the
response are intended for only one user and are not a valid
response for requests by other users. A private (non-shared)
cache MAY cache the response.
Note: This usage of the word private only controls where the
response may be cached, and cannot ensure the privacy of the
message content.
There's no reason why private content can't be cached, its just that it should only be cached by the browser in the current users context, it should not be cached server side or by other caches such as a proxy server.

Resources