404s when interacting with Google Sheets REST API, 200s with Google API Explorer - google-sheets-api

I'm attempting to interact with the Google Sheets API and running into an inexplicable problem that I'm finally reaching out to see how anyone else may have tackled it. Put simply, I can use the in-page API Explorer tool with only the https://www.googleapis.com/auth/spreadsheets.readonly OAuth2 scope at https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/get to query my spreadsheet (just the spreadsheet ID, leaving all other fields to empty defaults) and I'll see the 200 with the response in the bottom as expected.
Of course, I can't re-use the same access token that tool uses, but if provision an access code for the same user for my own app (same scope), and make the same GET request to https://sheets.googleapis.com/v4/spreadsheets/<spreadsheetId> in Postman (again, no other fields populated), substituting the access token into the Authentication header with Bearer <accessToken>, I get a 404.
I know the file is there - I've triple checked that I'm using the same spreadsheet ID across either request and I'm consistently getting a 404 (not a 401 or 403) indicating that my access token does authenticate.
I've tried broadening my OAuth2 scopes to include the full range listed on the API Explorer:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/spreadsheets.readonly
Of course, I don't want to have to use all those scopes for my purposes - I'd like to use the most narrow scope possible, but I also wanted to rule out that it wasn't failing to work for some scoping scenario. No difference - still a 404 every time I make the request in Postman. I've tried issuing multiple access tokens now, using accounts.google.com to invalidate the tokens for my app between re-issuances, but to no avail.
To be clear, the Google Sheets API has been enabled for my app.
In hopes that someone else has experienced the same inability to query Google's v4 REST API despite using valid access tokens, could you share how you managed to do it?
I appreciate it!
Update:
So I've been playing around with the OAuth 2.0 Playground shared in the comments and found that the authorization endpoint I was using was identical, but the token endpoint differed. This doesn't seem to matter since I used the custom option to use the alternate endpoint and the Playground was still able to work without issue just like the API Explorer.
Using the custom entries, I also entered my own app's client ID and client secret (after registering the playground redirect URI), minimizing the differences between what I'm doing in Postman and in the various Google tools. Again, my GET request to the spreadsheet works without issue.
Just to be clear, here's what I've been doing in the Playground:
In Step 1, I've specified the https://www.googleapis.com/auth/spreadsheets.readonly scope to authorize. I click the Authorize APIs button and log in with the user account.
It returns with the authorization code, so I exchange that code for the tokens via a POST to the token endpoint.
I then make a GET request to https://sheets.googleapis.com/v4/spreadsheets/<spreadsheetId> with no additional headers and it works without issue - 200 OK and all the data I'm expecting to see.
Here my approach in Postman:
Make a GET request to:
https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&redirect_uri=https://<myDomain>/oauthResp&client_id=<appClientId>&scope=https://www.googleapis.com/auth/spreadsheets.readonly&state=abc123
Receive a response similar to the following in my browser on the redirect (since the domain intentionally 404s).
https:///oauthResp?state=abc123&code=zyx098&scope=https://www.googleapis.com/auth/spreadsheets.readonly
Make a POST request to: https://www.googleapis.com/oauth2/v4/token with a body of:
client_id=<appClientId>
client_secret=<appClientSecret>
redirect_uri=https://<myDomain>/oauthResp
grant_type=authorization_code
Receive a response similar to:
{
"access_token": "abc123",
"expires_in": 3599,
"refresh_token": "zyx098",
"scope": "https://www.googleapis.com/auth/spreadsheets.readonly",
"token_type": "Bearer"
}
Make a GET request to https://sheets.googleapis.com/v4/spreadsheets/<spreadsheetId> with a 'Content-Type' header of application/json and an 'Authorization' header of Bearer abc123 (per the access token above).
Unlike the API Explorer and the OAuth 2.0 Playground, this yields a 404 - exactly the issue I've been experiencing for no obvious reason.
Further, if I simply take the fresh access token from the Playground and drop that into Postman, I get the same 404.
Any other ideas?

Related

What is a __cf_bm cookie?

First off, I apologize if I use any incorrect terminology. I'm familiar with calling internal apis, but this a side project and new territory for me.
I've been using chrome debugger and powershell to write some scripts against the api of a public site. I was able to retrieve the JWT token, however after a few minutes my calls will start to fail with a 403 error. To get around this I've been exporting all the request headers from chrome and storing those locally for my script to pass whenever my call to the api fails. I narrowed it down to 4 headers that must be present, the one that is most suspect seems to be "cookie".
I then removed all key value pairs in the cookie header until I found the one that makes it fail. It's named "__cf_bm". A quick google says it's some kind of cloudflare bot management data. I don't see any of the data present in the __cf_bm cookie being sent in a response during authentication, I only see it in request headers. Is it not possible to construct this value? Does this essentially prevent me from interacting with the site's api?

Getting back 401 from Dynamics 365 despite being issued valid token

I am attempting to rewrite a client app that currently connects to Dynamics 365 using JavaScript
let URL = "https://<company-name>.operations.dynamics.com/data/FinancialDimensionValues?cross-company=true";
let body = '';
var headers = {'Content-Type':'application/json'};
let response = ai.https.authorizedRequest(URL, 'GET', body, headers);
Currently this JavaScript application works and gets back JSON data. I am attempting to rewrite this application using C#. I am first starting with Postman to make sure I have all the authentication steps in place before moving on the writing the C# code.
Using Postman I am able to successfully obtain a JWT token using the "Client Credentials" flow where I pass the Client ID and the Client Secret to the Access Token Request URL. However, when trying to access an API endpoint within Dynamics 365 I receive back an HTTP 401 even though I am passing the JWT access token properly.
Here is the Access Token Request URL:
https://login.microsoftonline.com/722b0db7-9629-4304-92a0-dfb4a1debe62/oauth2/token?resource=https://<company-domain-here>.dynamics.com
I am thinking that I must be authenticating properly or I would not get back a valid access token. Also since the JavaScript application already in place works without issue I am assuming that Dynamics 365 is provisioned properly to allow API access.
What I am trying to figure out is what I might be doing wrong within Postman that results in my receiving a 401? What could be different between the working JavaScript request and what I am sending via Postman?
Issue is finally solved.
As mentioned follow the documentation mentioned.
If you face 401 Error, here is the last trick.
In postman Under Authorization--> Add authorization data to--> select Request Headers.
Now fire the query you shall have the 200 ok.
Ref Article which helped me figure it out.

Exact online REST API: POST call not working

I am trying to make a post call in Exact Online REST API. I'm trying to create a SalesInvoiceLine. I can perfectly do a GET call via my browser. I am logged in in Exact Online so I don't need authentication since this should be passed via a cookie. I tried POST via a browser. The browser prompts me to login, when I do nothing happens. I've also tried this in Postman:
I am 100% certain these initials are correct, I can login with them in Exact Online. What am I doing wrong?
If this is not the way, how can I post data to Exact Online? There are not that many concrete examples to find online.
You can't log on to the Exact Online REST API with Basic authentication, the mode you are using now.
The web service uses OAuth as authentication mechanism, meaning you have to acquire a token first. The steps to do so are outlined in the official documentation.
It will need some work on your end to register an app, get the flow up and running. Depending on your business needs, you might be helped with one of the apps for Exact Online by the company I work for.
You need to retrieve the CurrentDivision through GET Request https://{Base Uri}/api/v1/current/Me only from OAuth then you need to assign
CurrentDivision to whatever may be the API call .../api/v1/{CurrentDivision}/../....
Without authorization by Auth 2.0, neither is impossible.
To authorize the ExactOnline API calls you have to do the following:
Register the app in the developer portal. Bear in mind that you have to do a separate registration for French, UK or Dutch version of ExactOnline (this is indeed a pain).
In case you want your application to be used by other accounts than yours, you have to submit the app for validation, this usually takes 2-3 weeks.
EO uses standard OAuth 2.0 schema (very similar to what Google is using with their services). You have to use endpoint GET /api/oauth2/auth for building an authorization link and endpoint POST /api/oauth2/token for obtaining both access and refresh tokens.
Please bear in mind that many Auth 2.0 services are proving long-lasting refresh token. This is not the case of EO. The refresh token is invalidated every time when the access token is requested (endpoint POST /api/oauth2/token). With access token new refresh token is supplied, so make sure you update you refresh token as well.
The access token is placed in HTTP header as "Authorization: Bearer {{ACCESS_TOKEN}}"
In case you want to automate the EO API calls and do not want to code anything on your own, you can try one of the pre-build Exact Online API connectors, created by the company I work for.

Accessing the `student_view_url` via Open Edx API authentication or alternatives?

I’m working on a React Native application where all the Edx course information needs to be displayed offline.
I’ve authenticated the app using oauth2 endpoint (client_id=…&grant_type=password&username=user#example.com&password=p455w0rd to {{root}}/oauth2/access_token/) and can access the API endpoints to get the users enrolled courses ({{root}}/api/enrollment/v1/enrollment) and the blocks within those courses ({{root}}/api/courses/v1/blocks/?course_id={{course_id_url_friendly}}&depth=all&nav_depth=3&return_type=list&username={{username}}). What I’m struggling to get is the contents of the HTML blocks.
I see in the official app (when viewing requests via a proxy) that it will request the actual webpage of the course, presumably the student_view_url.
Is this the only way to get that content or is there an API endpoint I can use to return the content?
If the only way is to request the rendered student_view_url, how do I access that page? The only way I can tell in the official app is it looks like it’s passing the cookies to authenticate with the studen_view_url, which it must get when it authorises the user via the oauth2 endpoint. The cookies I get don’t work though, which I’ve tested in Postman (if I use cookies that I got from browsing my Edx site in Chrome, they do work). This begs me the question, is my authentication correct as I don’t get any scope returned when the official app returns a scope filled in with read write and two others.
A sample of what my authentication returns:
{
"access_token": "a12345...",
"token_type": "Bearer",
"expires_in": 2591999,
"scope": ""
}
To reiterate;
Is there a way to request the HTML content via the API (so returned in a JSON request) rather than the student_view_url
If not then how do I authenticate to access the student_view_url?
So I worked out how to do point 2.
Before requesting any HTML content you need to hit the endpoint {{root}}/oauth2/login/ with the auth token that you provide to other endpoints. This will pass back a usable sessionid cookie that will let you access content that's not public (so long as the user has access).
As an aside, to pass the cookie through fetch() requests, I had to pass the option credentials: 'include'.

What HTTP error codes should my API return if a 3rd party API auth fails?

I'm writing a REST-ish API service the provides the ability to interact with the end user's data in other 3rd party services (themselves REST APIs) via OAuth. A common example might be publishing data from my service to a third-party service such as Facebook or Twitter.
Suppose, for example, I perform an OAuth dance with the end user and Facebook, resulting in some short-term access token that my service can use to interact with the user's Facebook account. If that access token expires and the user attempts to use my service to publish to Facebook, what sort of error do I return to the user?
401 doesn't seem quite right to me; it seems that 401 would apply to the user's auth state with MY service. 403 seems much more appropriate, but also quite generic.
401 is the way to go. Two excerpts from the RFC2616 which defines the HTTP protocol:
Section 10.4.2 (about 401):
If the request already included Authorization credentials, then the 401
response indicates that authorization has been refused for those
credentials.
This seems to be appropriate for expired tokens. There are authentication credentials, but they're refused, so the user agent must re-authenticate.
Section 10.4.4 (about 403):
The server understood the request, but is refusing to fulfill it.
Authorization will not help and the request SHOULD NOT be repeated.
This should be used when the resource can't be accessed despite the user credentials. Could be a website/API that works only on US being hit by a asian IP or a webpage that has been declared harmful and was deactivated (so the content WAS found, but the server is denying serving it).
On OAuth2, the recommended workflow depends on how the token is being passed. If passed by the Authorization header, the server may return a 401. When passed via query string parameter, the most appropriate response is a 400 Bad Request (unfortunately, the most generic one HTTP has). This is defined by section 5.2 of the OAuth2 spec https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26
There's nothing wrong with being generic, and it sounds like a 403 status would be relevant - there is nothing stopping you from providing a more human readable version that elaborates in a bit more detail why.
I think the following is a comprehensive list if you have some level of ambition when it comes to error responses.
400 Bad Request
For requests that are malformed, for example if a parameter requires an int between 0-9 and 11 has been sent. You can return this, and in the response body specify parameter x requires a value between 0 and 9
401 Unauthorized
Used only for authorization issues. The signature may be wrong, the nonce may have been used before, the timestamp that was sent is not within an acceptable time window, again, use the response body to specify more exactly why you respond with this. For the sake of clarify use this only for OAuth related errors.
403 Forbidden
Expressly to signify that an operation that is well formed, and authorized, is not possible at all (either right now, or ever). Take for example if a resource has been locked for editing by another user: use the response body to say something along the lines of Another person is editing this right now, you'll have to wait mmkay?.
403 Forbidden can also have to do with trying to reach resources. Say for example that a user has access to a resource /resource/101212/properties.json but not to /resource/999/properties.json, then you can simply state: Forbidden due to access rights in the response body.
404 Not Found
The requested resource does not exist. Or the URL simply does not successfully map to an API in your service. Specify in response body.
405 Method Not Allowed
This is to represent that the API can not be called with for example GET but another method must be used. When this is returned also you MUST return the extra response header Allow: POST, PUT, etc.