HTTP 401 for restricted tokens

Hey everyone!

I am Artem, Co-Founder of Bardeen.

We got an error report from a user where they are not able to use the integration with restricted API tokens. It seems like attempting to perform a GET call to /docs/ with a restricted token results in HTTP 401. It happens with a freshly generated token that is still visible in the user dashboard.

401 suggests that the token itself is invalid and can not be used anymore. If the user didn’t have access to any docs I would expect to see an empty list as a response, or at least they should be able to see the doc containing the table that they’ve got access to (assuming they don’t have access to any docs). At any rate, 401 seems a bit bizarre to me.

The same exact code with the same exact workflow from the user works perfectly when using unrestricted tokens.

What am I missing?

Thanks and Happy New Year!

Hi Artem!

The purpose of an API token with restricted scopes is to restrict which endpoints you can use and what data you can get with that token, to minimize the risk of misuse or leakage of the token. The scope restrictions we offer today are to restrict a token to a single doc or to a single table within a doc. By definition, a token with such a restriction would be unable to see other docs, so that token is not usable with the /docs endpoint. The API returns a 401 to indicate that a scoped token is an invalid credential for that specific endpoint. The same token should work on other endpoints, depending on what scopes are enabled.

Hi Jonathan,

Thanks for your reply! The token in question was supposed to give full access to a specific Coda Doc.

My expectation is that it would work exactly as an unrestricted token except the ‘visibility’ would be limited to the single doc for which the token was created (so I could still query /docs/ but would only get one specific doc in the response object).

Do I understand it right that the token would work if instead of doing a GET on /docs/ one did GET on /docs/{docId}? Assuming my understanding is correct, how would the integration ‘know’ which exact resources does this token have access to, and how would it differentiate between ‘legitimately invalid’ and ‘valid but restricted’ tokens?

Also, I would argue that it’d be correct to actually return a 403 there :). According to the documentation 401 is ‘The API token is invalid or expired’ and 403 is ‘The API token doesn’t grant access to this resource’ which I think is very reasonable.

Yeah, a token scoped to a single doc should work on /docs/{docId}, and on any of the endpoints that are scoped to a single doc, /docs/{docId}/.... We don’t accept such a token for endpoints that are “wider” than a doc, rather than restricting visibility to what’s in the scope, which may have hard-to-define behavior in many cases.

You’re right that a 403 may be a more reasonable response code in this case.

If you hit the /whoami token, it will report whether the token you used is scoped. Though it will not currently which scopes it contains. But if you got a 401 error and you suspect it might be a scope-related issue, you could hit /whoami and confirm if the token is indeed scoped.

Thanks so much for clarifying!

I am still a bit lost on how an integration would use a scoped token. If an application is handed a token that is restricted to a single document, what would be the mechanism for determining the ID of the document (or a page/table) that the token provides access to? Normally an application would do GET on the docs/endpoint but with the scoped token that seems to be impossible?

There isn’t currently a mechanism to examine a token and determine which scopes it was created with. The way to do that would be via the /whoami endpoint, but for better or worse we only expose that the token is scoped but not what its actual scopes are.

Hitting the /docs endpoint wouldn’t be a reliable way to determine scopes even if it accepted scoped tokens. If a token is scoped to a single object like a table within a doc, that endpoint would still reject it, and similarly if a token used a non-doc scope, for instance if it was scoped to a single Pack, the /docs endpoint would also reject it because the token is unrelated to docs.

An application using a scoped token would need to accept both the token as well as some indication of how it should be used. A simple example might be a script that grabs the current rows of a table once an hour–you would run the script with the doc id and table id as inputs, as well as a token that could either be unscoped, scoped to the doc, or scoped to the table. But those entity ids need to be inputs to the application as well. If an application is meant to self-discover a user’s entities, like list some of their docs, then a scoped token would not be appropriate, but you might consider a read-only token that is otherwise un-scoped.