Helix: Incorrect HTTP response on authorization failure

The authentication documentation states:

When you make a request with expired or incorrect authorization credentials, the API returns a WWW-Authenticate header (with an invalid_token error) and a 401 Unauthorized status

When sending the request (where invalid_token is either an incorrect or an expired auth token) …

curl -X GET \
    https://api.twitch.tv/helix/users \
    -H 'Authorization: Bearer <invalid_token>' \
    -H 'Client-ID: <client_id>'

… I don’t get a 401 Unauthorized response, but rather 400 Bad Request with this message in the JSON body: Must provide an ID, Login or OAuth Token.

Is this a bug or am I doing something wrong with my request? When using a correct auth token, everything works as expected. Also, the Kraken /user endpoint does work as expected and returns HTTP status 401 Unauthorized. However, the WWW-Authenticate header is missing for me in both cases.

Auth token for that endpoint is optional, so the error is not that you’re not authorized; it’s that you’ve not specified at least one valid parameter (one of which is a valid oauth token).

Thanks, I understand.
Imho this is still somehow poor API design.
The ToS require to always check whether a user’s auth token is still valid (to prevent a user from appearing logged-in after he has revoked his token).
Also, an application probably wants to check if the user’s information is still up-to-date.
Using this endpoint, it’s possible to combine these two things.
However, the API does respond either with 400 Bad Request (= you did something wrong syntactically) or with the user (corresponding to the auth token) depending on whether the authorization is valid or not, only because there are “multiple ways” to talk to the endpoint (misusing an authorization token as one of the input parameters).
Imho, a better solution to this would be to use two different resources: One for fetching arbitrary users (no authorization required) and one for fetching the auth token’s user (authorization required).
That would make the design of an API client a lot easier.
Just a quick thought, I might be wrong with this.

Also, when using PUT on the same resource (user editing), but without the Client-ID and the Authorization request header, the 401 Unauthorized response does also not contain a WWW-Authenticate response header. (Wrong or outdated documentation?)

Furthermore, when a user revokes his token by disconnecting the application and then this token is used within a request, a 403 Forbidden is returned with this message in the body: Missing user:edit scope
That makes absolutely no sense to me (neither the response status, nor the message).

Also, the 401 Unauthorized is only returned if both the Client-ID and the Authorization request header are missing. If one of them is present, 403 Forbidden is returned.
Keeping in mind that sending Client-ID is considered mandatory, how can a request without it be authorized, but doing something forbidden due to missing permissions?

Oh and btw, I couldn’t test if a valid token actually alters the description, because 500 Internal Server Error.

If you have an oAuth, the ClientID is optional, as Twitch works out the ClientID from the oAuth. So if you have an oAuth don’t send the ClientID for this sort of test. (I’d have to dig the docs for the relevant docs which I can’t do just this second)

Thanks, that makes sense to me.
I saw here that you are allowed to replace the client ID with the app access token, but the same wasn’t mentioned for the user access tokens above.

App access tokens get client credentials (not user credentials). […] Client credentials also may be used in place of client ID headers to securely identify your application.

However, each example here seems to only use the Auhorization header, except the multiple-purpose endpoint GET /users which ultimately confused me enough to make me open this thread.

Still getting internal server errors when requesting with PUT correctly though, so I assume that the API is not yet producation-ready on weekends, haha.

Any endpoint that requires authorization must use a Bearer token and nothing else. This is due to the fact that you are making requests on behalf of a user, and since App Access Tokens don’t reflect user credentials they cannot be used in lieu of them.

If you hit the /users endpoint with no query parameters, the API assumes you are requesting information for the user associated with the Bearer token. As such, if you don’t provide a Bearer token without requesting specific user-ids, it will give you an error.

Since you are performing an operation on behalf of a user which requires authorization, you need to provide a Bearer token with the appropriate scopes. If you are still getting status 500, something might be borked on Twitch’s end.

Nobody here said that this was true.

I know. I tried to explain why I still consider the endpoint to be designed poorly when keeping in mind how the client is going to use it.

That’s how this status code works.

No shit, that’s why I said it…

Or you know, you could just read the documentation and design wrappers for different scenarios.

Thank you Sherlock Holmes, never would have figured that out without you.

Please don’t be offended.

By writing wrappers for the endpoint I had been complaining about, I assume you mean a wrapper containing something like …

if (response.status == 401 || (response.status == 400 && request.resource == '/users' && request.noSyntaxError))

Well, my whole point can be somewhat translated to saying that it would be cooler to just be able to do …

if (response.status == 401)

… and I solely blame the API design chosen for this specific endpoint as the reason why I’m not able to do that.

Sure, opinions about designs being good or bad are always subjective. But that’s exactly why I thought it’s worth sharing my thought process from above in this thread, because there might be different opinions on that.
So if “just write wrappers for it” is such an opinion, I must say I myself wouldn’t consider this a good suggestion. Because it pretty much fits for every design choice (good or bad) I guess. It sounds like “just deal with it”.

I also quickly looked at your wrapper library which (if I haven’t overlooked it) does not do automatic refreshing of expired access tokens. But I think that exactly my pseudo code example above shows why I consider different HTTP response status for the same problem/exception (= authentication failed) bad practice.

There is also this wrapper library which is a bit more popular, and it even does consider different exception types, but the handling of exactly the authentication exception I am talking about is just wrong. As you can see here, the TokenExpiredException is only thrown in case 401 Unauthorized is returned, which does not cover the 400 Bad Request response form the /users endpoint. It’s just another example of someone obviously not paying attention to what I call a poorly designed API endpoint.

1 Like

I was far from offended from your comment.

Concerning my library, have you even considered the idea that maybe it’s still heavily under development? I marked the API as “implemented”, but only as a way to show that wrappers for all endpoints have been implemented in some manner.

But since you’re on the topic of bad practices, here’s to never do: never, ever, perform an operation on behalf of a user even if it is something that should be done. Only notify them of what occurred, give them the tools on how to handle it, and let the user make the decision on what to do.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.