wakatime / wakatime-cli

Command line interface used by all WakaTime text editor plugins
https://wakatime.com/plugins
BSD 3-Clause "New" or "Revised" License
262 stars 40 forks source link

API autthenticated requests with user name in path broken #1038

Closed IzStriker closed 3 months ago

IzStriker commented 3 months ago

I don’t know if this is the correct place to post this issue, but there seems to be an issue with the https://wakatime.com/api/v1/users/:user/stats/:range endpoint when making authenticated requests. I have a Discord bot that ranks server members based on the amount of programming they’ve done in the previous week/month, etc. The bot makes authenticated requests to https://wakatime.com/api/v1/users/:user/stats/:range, but it seems this endpoint has broken in the past few months as it always returns:

{
    "error": "Time range not matching user's public stats range."
}

When I change :user to be current in the URL, authenticated requests work. I would make this change in the code, but I use the unique URLs for each user for caching purposes. This definitely worked previously, as I was using this endpoint for a while before it broke.

alanhamlett commented 3 months ago

That means you're not correctly authenticated as the user that you're requesting stats for. You can check the user you're authenticated as by making the same request to this url:

https://api.wakatime.com/api/v1/users/current

alanhamlett commented 3 months ago

When I change :user to be current in the URL, authenticated requests work.

That means you're requesting stats for a user different to the authenticated user? Check that from the stats response user_id attribute to make sure it's the same as you're expecting. Meaning, it should be the same as the :user part from the url, after you switch :user out for current.

IzStriker commented 3 months ago

When I was investigating the issue orignially, I only tested with a single user, my user. Also this broke one day without me making any code changes after it having worked for a year or so. If I was authenticated to wrong user shouldn't I receive a 403 I instead of a 400.

I did as you suggested and checked the user I was authenticated as, shows IzStriker.

Interestingly, only making authenticated requests. https://api.wakatime.com/api/v1/users/IzStriker works.

https://api.wakatime.com/api/v1/users/IzStriker/stats works, but doesn't return the fields that are only available when authenticated (e.g. projects).

https://api.wakatime.com/api/v1/users/IzStriker/stats/last_7_days returns a 400 as described in the original post.

https://api.wakatime.com/api/v1/users/current/stats works, but doesn't return the fields that are only available when authenticated (e.g. projects).

Note: The scope for the access token is "email,read_stats".

alanhamlett commented 3 months ago

What about https://api.wakatime.com/api/v1/users/current? My guess is you've been using the public endpoints this whole time without authentication. These both work without authentication because they're publicly accessible:

But the change we made recently was returning 400 instead of redirecting to the public view when you request any non-public range of stats for a user while not authenticated as that user:

djpiper28 commented 3 months ago

It is possible that is was redirecting to the public endpoints, however due to rolling logs I cannot see if that is the case with iStriker and I's software.

alanhamlett commented 3 months ago

We changed the api to show an error when requesting an invalid range instead of redirecting about 3 months ago, to prevent api consumers accidentally using a response for X range when they requested Y range.

IzStriker commented 3 months ago

What about https://api.wakatime.com/api/v1/users/current? My guess is you've been using the public endpoints this whole time without authentication.

This returns the expected data when I make the request with the access token and doesn't works without. It seems unlikely that I was using the public endpoints the whole time because I was accessing the projects field and using the different time ranges which aren't available from the public endpoint.

To just to elimiate any confusion this is the request I'm doing.

curl --location 'https://api.wakatime.com/api/v1/users/IzStriker/stats/last_7_days' \
--header 'Authorization: Bearer <redacted> \
--header 'Cookie: csrftoken=<redacted>; session=<redacted>'

If you're familar with dotnet here is the code that broke 3 (or so) months ago https://github.com/IzStriker/WakaBot/blob/509eed9b8e837fb1bcb7d15da20a49b2f60c1046/WakaBot.Core/WakaTime/WakaTime.cs#L100C9-L100C116 https://github.com/IzStriker/WakaBot/blob/master/WakaBot.Core/Extensions/HttpClientExtension.cs

djpiper28 commented 3 months ago

What I'll do when I get I back from the pub is make a code change to use /current and see if that works.

IzStriker commented 3 months ago

Current works, it just will break caching because all users will use the same url.

alanhamlett commented 3 months ago

Looks like there's a bug on our end, I'll get it fixed soon.

alanhamlett commented 3 months ago

It's fixed. Turns out you were doing everything correctly, but we broke parsing the username from api urls, only when checking OAuth scopes. It worked with current and when using the user's id but not username.

alanhamlett commented 3 months ago

Also related but not required, usernames change so it's better to use user.id in the api url. It works with your caching, and doesn't risk a 404 if the user changes their username.

IzStriker commented 3 months ago

Great, thanks for your time. I'll change it to use user id.