The Hub is now in read-only mode as we make improvements to the Hub experience. More information is available here.
05-03-2023 06:09 AM - last edited on 07-06-2023 05:10 PM by LouB
Hi Folks,
Trying to use the API to upload activities recorded by a Chinese Bicycle Computer, that junk product cannot automatically upload data to Strava, so I have to write a script to get those fit files via adb, and then upload them to strava via api.
However I got response like this:
Upload failed: {"message":"Authorization Error","errors":[{"resource":"AccessToken","field":"activity:write_permission","code":"missing"}]}
I used
scope = ['activity:write']
when getting an access_token, and I found out I always get the same access token whatever I set the scope to be.
That access token is also the one showed on https://www.strava.com/settings/api, under that I can see scope: read, just like https://communityhub.strava.com/t5/developer-discussions/authentication-scope/m-p/6357. In order to upload fit files I need a token with "activity:write", but I cannot find anywhere to change the scope of my access token.
Of course I tried to solved this by https://developers.strava.com/docs/authentication/ , however its not helping well.
here is my demo written by python
from requests_oauthlib import OAuth2Session
import requests
def get_access_token():
client_id = "client_id"
client_secret = "client_secret"
refresh_token = "refresh_token "
scope = ["activity:write"]
oauth = OAuth2Session(client_id=client_id, redirect_uri="http://localhost", scope=scope)
token_url = "https://www.strava.com/oauth/token"
extra = {
"client_id": client_id,
"client_secret": client_secret,
}
token = oauth.refresh_token(token_url, refresh_token=refresh_token, **extra)
print(token)
return token["access_token"]
access_token = get_access_token()
headers = {'Authorization': 'Bearer ' + access_token}
activity_type = 'ride' # or 'run', 'swim', etc.
name = 'activity20230503172414'
description = 'upload by api'
commute = False # whether this activity is a commute
trainer = False # whether this activity was done on a trainer
data_type = 'fit'
url = 'https://www.strava.com/api/v3/uploads'
fit_file = open('20230503172414.fit', 'rb')
files = {'file': ('activity.fit', fit_file, 'application/octet-stream')}
params = {
'data_type': data_type,
'activity_type': activity_type,
'name': name,
'description': description,
'commute': commute,
'trainer': trainer
}
response = requests.post(url, headers=headers, data=params, files=files)
if response.status_code == 200:
print('Upload successful')
else:
print('Upload failed:', response.text)
Solved! Go to Solution.
05-04-2023 03:34 AM - edited 05-04-2023 03:34 AM
Here, I reproduced it:
from requests_oauthlib import OAuth2Session
client_id = 'xxxx'
client_secret = 'yyyy'
redirect_uri = 'http://localhost'
scope = ['activity:write']
oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scope)
print('Please go to %s and authorize access.' % authorization_url)
Please go to https://www.strava.com/oauth/authorize?response_type=code&client_id=xxxx&redirect_uri=http%3A%2F%2Flocalhost&scope=activity%3Awrite&state=... and authorize access.
This led me to the following screen in my browser (you should see some info. about your app there instead):
And clicking in "Authorize" gives me the following URI: http://localhost/?state=...&code=zzzz&scope=read,activity:write
Which I can now exchange for an access_token. I couldn't do this part with the library you're using, as it told me the response by the authentication endpoint was wrong:
token = oauth.fetch_token('https://www.strava.com/oauth/token', authorization_response='https://localhost?state=...&code=zzzz&scope=read,activity:write', client_secret=client_secret)
Traceback (most recent call last):
[...]
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.
However, I managed to do this with cURL:
curl -X POST https://www.strava.com/api/v3/oauth/token \
-d client_id=xxxx \
-d client_secret=yyyy \
-d code=zzzz \
-d grant_type=authorization_code
{"token_type":"Bearer","expires_at":1683217836,"expires_in":21600,"refresh_token":"...","athlete":{"id":25332098,"username":"pcaraballollorente","resource_state":2,"firstname":"Pablo","lastname":"Caraballo Llorente","bio":"","city":"Madrid","state":"ESPAÑA","country":null,"**bleep**":"M","premium":false,"summit":false,"created_at":"2017-09-28T07:50:11Z","updated_at":"2023-04-25T15:40:29Z","badge_type_id":0,"weight":75.0,"profile_medium":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/25332098/24670087/1/medium.jpg","profile":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/25332098/24670087/1/large.jpg","friend":null,"follower":null}}
Hope that helps.
05-17-2023 08:38 AM
Thanks a lot for for all the suggestions. However, I ended up discarding that unreliable bike computer and purchasing a new one from a different brand. The previous model I had, the Xoss Nav, didn't function well with the Sigeyi power meter. It had frequent issues with data loss and connectivity. Therefore, I wouldn't recommend buying the Xoss computer if you have a dual-side power meter. Xoss recently released a new model called Nav Plus, but I am not impressed with it. Fortunately, my new computer has resolved all the aforementioned problems, so I no longer need to develop any scripts.
05-04-2023 03:15 AM
First of all, I want to make clear that I'm not very experienced with OAuth2.
According to the authentication flow described in this diagram. In order to get an authorization code, which can be later exchanged by an access_token, a client/user firstly needs to let your app perform requests on their behalf. Even if that client/user is you, the app owner.
Thus, I'd say you may want to follow something as described in your library for the Web Application Flow:
05-04-2023 03:34 AM - edited 05-04-2023 03:34 AM
Here, I reproduced it:
from requests_oauthlib import OAuth2Session
client_id = 'xxxx'
client_secret = 'yyyy'
redirect_uri = 'http://localhost'
scope = ['activity:write']
oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scope)
print('Please go to %s and authorize access.' % authorization_url)
Please go to https://www.strava.com/oauth/authorize?response_type=code&client_id=xxxx&redirect_uri=http%3A%2F%2Flocalhost&scope=activity%3Awrite&state=... and authorize access.
This led me to the following screen in my browser (you should see some info. about your app there instead):
And clicking in "Authorize" gives me the following URI: http://localhost/?state=...&code=zzzz&scope=read,activity:write
Which I can now exchange for an access_token. I couldn't do this part with the library you're using, as it told me the response by the authentication endpoint was wrong:
token = oauth.fetch_token('https://www.strava.com/oauth/token', authorization_response='https://localhost?state=...&code=zzzz&scope=read,activity:write', client_secret=client_secret)
Traceback (most recent call last):
[...]
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.
However, I managed to do this with cURL:
curl -X POST https://www.strava.com/api/v3/oauth/token \
-d client_id=xxxx \
-d client_secret=yyyy \
-d code=zzzz \
-d grant_type=authorization_code
{"token_type":"Bearer","expires_at":1683217836,"expires_in":21600,"refresh_token":"...","athlete":{"id":25332098,"username":"pcaraballollorente","resource_state":2,"firstname":"Pablo","lastname":"Caraballo Llorente","bio":"","city":"Madrid","state":"ESPAÑA","country":null,"**bleep**":"M","premium":false,"summit":false,"created_at":"2017-09-28T07:50:11Z","updated_at":"2023-04-25T15:40:29Z","badge_type_id":0,"weight":75.0,"profile_medium":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/25332098/24670087/1/medium.jpg","profile":"https://dgalywyr863hv.cloudfront.net/pictures/athletes/25332098/24670087/1/large.jpg","friend":null,"follower":null}}
Hope that helps.
05-17-2023 08:39 AM
Thank you so much for all the suggestions. However, I ended up discarding that unreliable bike computer and purchasing a new one from a different brand. The previous model I had, the Xoss Nav, didn't function well with the Sigeyi power meter. It had frequent issues with data loss and connectivity. Therefore, I wouldn't recommend buying the Xoss computer if you have a dual-side power meter. Xoss recently released a new model called Nav Plus, but I am not impressed with it. Fortunately, my new computer has resolved all the aforementioned problems, so I no longer need to develop any scripts.
05-04-2023 12:57 AM
Maybe the fault lies within using an array to specify the scope. The API documentation states
"Requested scopes, as a comma delimited string, e.g. "activity:read_all,activity:write". Applications should request" https://developers.strava.com/docs/authentication/
Take a look at what your request URL looks like. I would try using
scope = "activity:write"
instead of
scope = ["activity:write"]
05-03-2023 06:27 AM
Or maybe I should try Swagger Playground?
Welcome to the Community - here is your guide to help you get started!