Updating Your Strava Access Tokens With The Strava API

in STEMGeeks9 months ago

Welcome back. With our last article we started setting up your API Application and used the curl command work with the API. In this article we are going to set up some Python code to match these curl commands, so we can start putting some code down to get the work done, instead of working manually with the API.

In this article we are not going to show you how to authenticate with the Strava API and allow users to register with your API Application, as we covered that in the last article, so if you are interested in how to get these initial steps done, head over to our first article for all the information you need: https://hive.blog/hive-163521/@strava2hive/getting-started-using-the-strava-api

We are going to use AWS to store the values we have generated on behalf of our user to access the Strava API. Unfortunately, we are not going to go too much into detail of how to set up a DynamoDB to store this data, but if there is enough people needing a refresher, we may do it at a later date.

So the basic structure of the work we are going to do today, will be testing the values stored in our DynamoDB, and if they are needed, renewing the access_token, if it has expired.

The work flow we are going to set up will be:

  1. Access the DynamoDB
  2. Get the details of the specific athlete
  3. Check if the access_token has expired
  4. If the access_token has expired, refresh the access token

In our last article we allowed users to sign up to our Api Appliction. We need to store these details somewhere. For the time being, we are going to keep these details in a DynamoDB, but you could keep this information anywhere that is easy for your to access, even a config file, if this is the best option to you.

We have our user that has already athenticated with the Strava API and we have used the one time key to get all the relevant information we need to start working with the API.

This is the information we were assigned from Strava is:

{
  "token_type": "Bearer",
  "expires_at": 1692274839,
  "expires_in": 21600,
  "refresh_token": "ff62b0e8379ab75dc7e97fc6208f99ddXXXXXXX",
  "access_token": "f416cf4ab22a6b0ecd78ce8d0c2a9cdbXXXXXXX",
  "athlete": {
    "id": 17789XXX,
    "username": "vsesto",

And this has been added to our DynamoDB as follows below:

{
  "athleteId": {
    "S": "17789XXX"
  },
  "strava_access_token": {
    "S": "f416cf4ab22a6b0ecd78ce8d0c2a9cdbXXXXXXX"
  },
  "strava_one_time": {
    "S": "8dd35e770d855cc73792f50423fcd90cXXXXXXX"
  },
  "strava_refresh_token": {
    "S": "ff62b0e8379ab75dc7e97fc6208f99ddXXXXXXX"
  },
  "strava_token_expires": {
    "N": "1692274839"
  }
}

1. Access the DynamoDB


To get access to the DynamoDB, you will first need to set up your DynamoDB and allow access via AWS Access Keys. We have set up a table called 'test_athletes' and we have exported both the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY as environment variables, so our Python code can access the data.

Before we create our Python function, we need to import so modules into our code as we have below to access the environment variables(os) and interact with the DynamoDB(boto3).

import os
import requests
import boto3
from boto3.dynamodb.conditions import Key

Now we can start to set up our function, simply called 'dynamo_access()'. The code below sets up a client to interface with AWS and then to access the dynamodb resource.

def dynamo_access():
    client = boto3.client('dynamodb', region_name='ap-southeast-2', aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),)
    dynamodb = boto3.resource('dynamodb', region_name='ap-southeast-2', aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),)
    ddb_exceptions = client.exceptions
    return dynamodb

We can now set up some code to use this function. Live 1 below, simply sets the value of the table we want to use as 'test_athletes'. We then use the function and assign it to a value called 'dynamodb'.

dynamoTable = 'test_athletes'

dynamodb = dynamo_access()
table = dynamodb.Table(dynamoTable)

2. Get the details of the specific athlete


We can use the table values from the code above to then get specific values for our athlete. All the values in the test_athletes database are assigned to the 'table' value, which we can then query for the Key('athleteId').eq('1778778'). The details from the query are added into the athletedb_response variable. We can then print these values our to screen and hopefully see what has happenned.

athletedb_response = table.query(KeyConditionExpression=Key('athleteId').eq('17789XXX'))
print(athletedb_response['Items'][0])

We should see an output similar to the one below:

{'strava_one_time': '8dd35e770d855cc73792f50423fcd90cXXXXXXX', 'athleteId': '17789XXX', 'strava_access_token': 'f416cf4ab22a6b0ecd78ce8d0c2a9cdbXXXXXXX', 'strava_refresh_token': 'ff62b0e8379ab75dc7e97fc6208f99ddXXXXXXX', 'strava_token_expires': Decimal('1692274839')}

3. Check if the access_token has expired


We discussed in our previous article, that the information you will be provided from Strava when you authenticate your athlete with Strava, will include an expires_at in the form of a unix time stamp. Each time the loop runs, we need to check that the access_code has not expired, but if it has expired, before connecting to Strava again, you will need to refresh the athletes access_code. We do this using the refresh_token provided by Strava, and if we are only running the code once a day, you would simply need to perform the refresh each time, as the access_code will expire in six hours.

In the code abover we added all our values into the athletedb_response variable. We can get the value for the expire time with the 'strava_token_expires' in our variable. This part of the code, will also use the time module, so you will need to do that first.

import time

strava_expire_date = athletedb_response['Items'][0]['strava_token_expires']
print(strava_expire_date)

We can now compare the value in the DynamoDB with the current time, by subtracting the current_time from the expired_time. If the value is a negative number, or "less than 0", we know we need to refresh the access_token.

expire_time = int(strava_expire_date)
current_time = time.time()
expired_value = expire_time - int(current_time)
if expired_value > 0:
    print("Strava Token Still Valid")
else:
    print("Strava Token Needs To Be Updated")

4. If the access_token has expired, refresh the access token


We can now set up a function to refresh our access_token via the Strava API. As we have with our AWS access keys, we have also exported the STRAVA_CLIENT_ID and STRAVA_SECRET values, so we don't need to store the values in the code.

The function asks for the athlete and as we have in the earlier added in all the database values into the variable called athletedb_response. We need to send a request to Strava with the App client_id, the App client_secret, the athletes refres_token and the athletes strava_one_time.

You can see in line 6 of the function below, we send the request to www.strava.com/api/v3/oauth/token. From the response we then gather the new access_token and expire_at value to be returned back to the main section of the code, ready for the DynamoDB to be updated.

def refresh_access_token(athlete):
    # Update the strava access token every six hours
    athlete_vals = athlete[0]
    code_val = athlete_vals['strava_one_time']
    try:
        response = requests.post("https://www.strava.com/api/v3/oauth/token", params={'client_id': os.getenv('STRAVA_CLIENT_ID'), 'client_secret': os.getenv('STRAVA_SECRET'), 'code': code_val, 'grant_type': 'refresh_token', 'refresh_token': athlete_vals['strava_refresh_token']})
        access_info = dict()
        activity_data = response.json()
        access_info['access_token'] = activity_data['access_token']
        access_info['expires_at'] = activity_data['expires_at']
        access_info['refresh_token'] = activity_data['refresh_token']
        return access_info['access_token'], access_info['expires_at']
    except:
        print("Something went wrong trying to refresh the access token")
        return False

The following code will do the work to use this function and then update the DynamoDB for the athlete. We access the DynamDB table again for the athlete, and then set up two requests to update the strava_access_token and then strava_token_expires

    new_access_token, new_expire_date = refresh_access_token(athletedb_response['Items'])
    table = dynamodb.Table(dynamoTable)
    athletedb_response = table.query(KeyConditionExpression=Key('athleteId').eq('1778XXX'))
    response = table.update_item(
            Key={ 'athleteId': int('1778XXX')},
            UpdateExpression='SET strava_access_token = :newStravaToken', 
            ExpressionAttributeValues={':newStravaToken': new_access_token}, 
            ReturnValues="UPDATED_NEW")
    response = table.update_item(
            Key={'athleteId': int('1778778')}, 
            UpdateExpression='SET strava_token_expires = :newStravaExpire', 
            ExpressionAttributeValues={':newStravaExpire': new_expire_date},           
            ReturnValues="UPDATED_NEW")

The full code from start to finish is listed below

#!/usr/bin/env python

import os
import requests
import time
import re
import random
import string
import boto3
from boto3.dynamodb.conditions import Key

def dynamo_access():
    client = boto3.client('dynamodb', region_name='ap-southeast-2', aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),)
    dynamodb = boto3.resource('dynamodb', region_name='ap-southeast-2', aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'),)
    ddb_exceptions = client.exceptions
    return dynamodb

def refresh_access_token(athlete):
    # Update the strava access token every six hours
    athlete_vals = athlete[0]
    code_val = athlete_vals['strava_one_time']
    try:
        response = requests.post("https://www.strava.com/api/v3/oauth/token", params={'client_id': os.getenv('STRAVA_CLIENT_ID'), 'client_secret': os.getenv('STRAVA_SECRET'), 'code': code_val, 'grant_type': 'refresh_token', 'refresh_token': athlete_vals['strava_refresh_token']})
        access_info = dict()
        activity_data = response.json()
        access_info['access_token'] = activity_data['access_token']
        access_info['expires_at'] = activity_data['expires_at']
        access_info['refresh_token'] = activity_data['refresh_token']
        return access_info['access_token'], access_info['expires_at']
    except:
        print("Something went wrong trying to refresh the access token")
        return False


dynamoTable = 'ai_test_athletes'

dynamodb = dynamo_access()
print("Scanning table")
table = dynamodb.Table(dynamoTable)
athletedb_response = table.query(KeyConditionExpression=Key('athleteId').eq('1778778'))
strava_expire_date = athletedb_response['Items'][0]['strava_token_expires']
print(strava_expire_date)

expire_time = int(strava_expire_date)
current_time = time.time()
expired_value = expire_time - int(current_time)
print(expired_value)
if expired_value > 0:
    print("Strava Token Still Valid")
else:
    print("Strava Token Needs To Be Updated")
    new_access_token, new_expire_date = refresh_access_token(athletedb_response['Items'])
    print("Now we have to update the database")
    print(new_access_token)
    table = dynamodb.Table(dynamoTable)
    athletedb_response = table.query(KeyConditionExpression=Key('athleteId').eq('1778778'))
    print(athletedb_response['Items'][0])
    print("Update Strava Token")
    response = table.update_item(
            Key={ 'athleteId': int('1778778')},
            UpdateExpression='SET strava_access_token = :newStravaToken', 
            ExpressionAttributeValues={':newStravaToken': new_access_token}, 
            ReturnValues="UPDATED_NEW")
    response = table.update_item(Key={'athleteId': int('1778778')}, UpdateExpression='SET strava_token_expires = :newStravaExpire', ExpressionAttributeValues={':newStravaExpire': new_expire_date}, ReturnValues="UPDATED_NEW")

If you have any questions or comments, please let me know.

Sort:  

Congratulations @strava2hive! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You received more than 5000 upvotes.
Your next target is to reach 6000 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out our last posts:

Women's World Cup Contest - Recap of the play-off for third place
Women's World Cup Contest - Recap of the second Semi-Final
Women's World Cup Contest - Recap of the first Semi-Final

Horray, thank you so much

That's great @strava2hive! We're here to encourage you to achieve your next goals on Hive!

BTW, the HiveBuzz project needs your support! Help us continue bringing gamification, entertainment, and fun to the Hive blockchain. Check out our funding proposal and consider supporting us!
All you need to do is to click on the "support" button on this page: https://peakd.com/proposals/248.
Thank you!

This is great
I just reblogged for all of my friends to see it

Thank you so much for your support, appreciate it !LUV

@rafzat, @strava2hive(1/1) sent LUV. | connect | community | HiveWiki | NFT | <>< daily

Join in Hive General chat | Type ! help (no space) to get help on Hive. Info

Made with LUV by crrdlx