I am starting a small python script (not an application) that can upload my *.fit activity files on Strava whenever they are created in a desired folder.
The main steps I plan to do are:
1. monitor *.fit file system modifications
2. access authentication to Strava to enable my program to upload files
(This tool will be personal use only, thus I expect no need to authenticate every time uploading)
3. upload the file to my Strava account
4. automatically doing this fixed routine with the help of Windows Task Scheduler
(For example, there will be 4-5 new riding activities generated in my computer folder, I expect this tool can automatically upload all of them once a week so that I do not need to manually complete the task.)
For step2, I really have no ideas how to implement even though reading through Strava Authentication Documentation and several source codes other peoples have developed (e.g. toravir's "rk2s (RunKeeper 2 Strava)" project on GitHub). I grabbed that some of the python modules like stravalib, swagger_client, request, json, etc. as well as concepts like OAuth2 may be related to step2 but I still can not put everything together...
Can any experienced give me some advice for the implementations of step2? or any related readings will be perfect!
Advice for other parts of this project will also be very welcomed and appreciated.
I thank you very much in advance:)
This is a code example on how you can access the Strava API, check out this gist or use the code below:
import time
import pickle
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from stravalib.client import Client
CLIENT_ID = 'GET FROM STRAVA API SITE'
CLIENT_SECRET = 'GET FROM STRAVA API SITE'
REDIRECT_URL = 'http://localhost:8000/authorized'
app = FastAPI()
client = Client()
def save_object(obj, filename):
with open(filename, 'wb') as output: # Overwrites any existing file.
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
def load_object(filename):
with open(filename, 'rb') as input:
loaded_object = pickle.load(input)
return loaded_object
def check_token():
if time.time() > client.token_expires_at:
refresh_response = client.refresh_access_token(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, refresh_token=client.refresh_token)
access_token = refresh_response['access_token']
refresh_token = refresh_response['refresh_token']
expires_at = refresh_response['expires_at']
client.access_token = access_token
client.refresh_token = refresh_token
client.token_expires_at = expires_at
#app.get("/")
def read_root():
authorize_url = client.authorization_url(client_id=CLIENT_ID, redirect_uri=REDIRECT_URL)
return RedirectResponse(authorize_url)
#app.get("/authorized/")
def get_code(state=None, code=None, scope=None):
token_response = client.exchange_code_for_token(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, code=code)
access_token = token_response['access_token']
refresh_token = token_response['refresh_token']
expires_at = token_response['expires_at']
client.access_token = access_token
client.refresh_token = refresh_token
client.token_expires_at = expires_at
save_object(client, 'client.pkl')
return {"state": state, "code": code, "scope": scope}
try:
client = load_object('client.pkl')
check_token()
athlete = client.get_athlete()
print("For {id}, I now have an access token {token}".format(id=athlete.id, token=client.access_token))
# To upload an activity
# client.upload_activity(activity_file, data_type, name=None, description=None, activity_type=None, private=None, external_id=None)
except FileNotFoundError:
print("No access token stored yet, visit http://localhost:8000/ to get it")
print("After visiting that url, a pickle file is stored, run this file again to upload your activity")
Download that file, install the requirements, and run it (assuming the filename is main):
pip install stravalib
pip install fastapi
pip install uvicorn
uvicorn main:app --reload
I believe you need to authenticate using OAuth in order to upload your activity, which pretty much requires you to have a web server setup that Strava can post back to after you "Authorize". I just set the authentication piece up using Rails & Heroku.
This link has a pretty good flowchart of what needs to happen.
https://developers.strava.com/docs/authentication/
Actually it looks like if you go to API Settings you can get your access token and refresh token there. I would also check out the Python Strava Library but it looks like you could do something like:
from stravalib.client import Client
access_token = 'your_access_token_from_your_api_application_settings_page'
refresh_token = 'your_refresh_token_from_your_api_application_settings_page'
client = Client()
athlete = client.get_athlete()
You may need to dig in a little more to that library to figure out the upload piece.
Related
I'm trying to run a query on bigquery in a Django project and get results. While working successfully in localhost, it does not redirect to the verification link at all when I take it to the live server.
I think I need to change the redirect_uri value as I read it. I added this in Da appflow variable but the url doesn't change. I am using the same query below with the example query in google's document, I am submitting my own query because it contains private information, but it is exactly the same query.
I have added to Authorized redirect URIs, and I put the api in production mode.;
The resulting redirect url is output as localhost in this way;
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=123-nml31ekr2n0didomei5.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fbigquery&state=XF1WdtCoR4HaICwzSKk9A1giBrSzBv&access_type=offline
def query_stackoverflow():
launch_browser = True
project = 'xx-prod'
appflow = flow.InstalledAppFlow.from_client_secrets_file("static/client_secret_518684-nmpoqtgo5flvcgnl31ekr2ni5.apps.googleusercontent.com.json", scopes=["https://www.googleapis.com/auth/bigquery"], redirect_uri=["https://xx.com/"])
if launch_browser:
appflow.run_local_server()
else:
appflow.run_console()
credentials = appflow.credentials
client = bigquery.Client(project=project, credentials=credentials)
client = bigquery.Client()
query_job = client.query(
"""
SELECT
CONCAT(
'https://stackoverflow.com/questions/',
CAST(id as STRING)) as url,
view_count
FROM `bigquery-public-data.stackoverflow.posts_questions`
WHERE tags like '%google-bigquery%'
ORDER BY view_count DESC
LIMIT 10"""
)
results = query_job.result() # Waits for job to complete.
for row in results:
print("{} : {} views".format(row.url, row.view_count))
On live server google return auth url like this;
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=51864584-nmpoqtgo5flvcgnln0didomei5.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fbigquery&state=W2uMZwzaYMEpFzExodRCf2wA4&access_type=offline
The first problem is that it does not automatically redirect to the link as in localhost, the second problem is that when I open this link manually, the link cannot be reached after mail verification.
From what i can see your code is using installed app flow. This means that the consent screen is going to open up on the machine its running on. If you have this running on a server, are you logging into the server and running it or are you in fact creating a web application?
flow.InstalledAppFlow
web app
If you are making a web application then you should be following this sample.
API access on behalf of your clients (web flow)
You will need to convert it to work with big query.
import google.oauth2.credentials
import google_auth_oauthlib.flow
# Initialize the flow using the client ID and secret downloaded earlier.
# Note: You can use the GetAPIScope helper function to retrieve the
# appropriate scope for AdWords or Ad Manager.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
'client_secret.json',
scope=[oauth2.GetAPIScope('adwords')])
# Indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required.
flow.redirect_uri = 'https://www.example.com/oauth2callback'
The code for a web application is slightly different then that of an installed application.
I built a REST API by creating an Azure Functions App and a Python console application should be able to authenticate and make requests to the API.
Here are the steps I have taken so far:
I created the REST API as an Azure Functions App (in C#) where I used AuthorizationLevel.Anonymous.
In the Azure Active Directory I created an app registration (Azure Active Directory > App registrations) where I added a client secret under Certificates & secrets > Client secrets.
In the Azure Functions App I added the registration from step 2 under Authentication > Identity provider where I provided the registration's client ID as well as the value of the registration's client secret.
Here is the code of the Python console application (as described here: https://github.com/AzureAD/microsoft-authentication-library-for-python):
import requests
import msal
import json
with open('configuration.json') as json_file:
configuration = json.load(json_file)
app = msal.ConfidentialClientApplication(
configuration["client_id"],
authority=configuration["authority"],
client_credential=configuration["secret"]
)
result = None
result = app.acquire_token_silent(configuration["scope"],account=None)
if not result:
result = app.acquire_token_for_client(scopes=configuration["scope"])
if "access_token" in result:
url = ... # the URL of a specific function of the Azure Functions App
parameters = {...}
response = requests.get(url=url, params=parameters, headers={'Authorization': 'Bearer ' + result['access_token']})
print("Status code: {}".format(response.status_code))
print("Message: {}".format(response.text))
registration["scope"] equals ["https://graph.microsoft.com/.default"] and registration["secret"] equals the value of the client secret created in step 2.
The code returns:
Status code: 401
Message: You do not have permission to view this directory or page.
What am I missing? I know there are similar issues on Stackoverflow but they did not solve my problem.
If you use the newest authentication, please check the issue url:
Check your Issuer URL:
Or to change the accessTokenAcceptedVersion to 2:
Any way, make sure the issue url version is the same.
(I found there are still many problems with Authentication, and many things will not be automatically configured.)
We are creating an application for use in our organization, but we only want people in our organization to be able to use the app. We had the idea of using Microsoft's OAuth endpoint in order to authenticate whether a user is part of our org or not. The idea is to bring up a sign in screen where the user can enter their Office 365 username and password, which will then allow them to use our app upon submission of their credentials.
Our app is running on Django, and I've only found a solution to this problem using Flask and Microsoft's Graph API connect sample for Python (See code snippet below). This sample uses a similar idea to the one above to log in to the app. Are there any similar methods of authentication for Django?
import requests
from flask import Flask, redirect, url_for, session, request, render_template
from flask_oauthlib.client import OAuth
# read private credentials from text file
client_id, client_secret, *_ = open('_PRIVATE.txt').read().split('\n')
if (client_id.startswith('*') and client_id.endswith('*')) or \
(client_secret.startswith('*') and client_secret.endswith('*')):
print('MISSING CONFIGURATION: the _PRIVATE.txt file needs to be edited ' + \
'to add client ID and secret.')
sys.exit(1)
app = Flask(__name__)
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)
# since this sample runs locally without HTTPS, disable InsecureRequestWarning
requests.packages.urllib3.disable_warnings()
msgraphapi = oauth.remote_app( \
'microsoft',
consumer_key=client_id,
consumer_secret=client_secret,
request_token_params={'scope': 'User.Read Mail.Send'},
base_url='https://graph.microsoft.com/v1.0/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://login.microsoftonline.com/common/oauth2/v2.0/token',
authorize_url='https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
)
#app.route('/login')
def login():
"""Handler for login route."""
guid = uuid.uuid4() # guid used to only accept initiated logins
session['state'] = guid
return msgraphapi.authorize(callback=url_for('authorized', _external=True), state=guid)
#app.route('/login/authorized')
def authorized():
"""Handler for login/authorized route."""
response = msgraphapi.authorized_response()
if response is None:
return "Access Denied: Reason={0}\nError={1}".format( \
request.args['error'], request.args['error_description'])
# Check response for state
if str(session['state']) != str(request.args['state']):
raise Exception('State has been messed with, end authentication')
session['state'] = '' # reset session state to prevent re-use
# Okay to store this in a local variable, encrypt if it's going to client
# machine or database. Treat as a password.
session['microsoft_token'] = (response['access_token'], '')
# Store the token in another session variable for easy access
session['access_token'] = response['access_token']
me_response = msgraphapi.get('me')
me_data = json.loads(json.dumps(me_response.data))
username = me_data['displayName']
email_address = me_data['userPrincipalName']
session['alias'] = username
session['userEmailAddress'] = email_address
return redirect('main')
You should be able to use just about any OAUTH 2.0 library for Python. I've not worked with Django but I know there are several out there for Python.
I came across django-azure-ad-auth which seems to be exactly what you're looking for.
I also found a general OAUTH library called django-allauth which seems to have a lot of activity. It doesn't have a built-in provider but the model they use for providers seems simple enough that you may be able to extend it without too much trouble.
Anyone know if this is possible?
I just want to automate dropping some documents into my onedrive for business account.
I tried
import onedrivesdk
from onedrivesdk.helpers import GetAuthCodeServer
from onedrivesdk.helpers.resource_discovery import ResourceDiscoveryRequest
redirect_uri = 'http://localhost:8080'
client_id = 'appid'
client_secret = 'mysecret'
discovery_uri = 'https://api.office.com/discovery/'
auth_server_url='https://login.live.com/oauth20_authorize.srf?scope=wl.skydrive_update'
#auth_server_url='https://login.microsoftonline.com/common/oauth2/authorize',
auth_token_url='https://login.microsoftonline.com/common/oauth2/token'
http = onedrivesdk.HttpProvider()
auth = onedrivesdk.AuthProvider(http,
client_id,
auth_server_url=auth_server_url,
auth_token_url=auth_token_url)
auth_url = auth.get_auth_url(redirect_uri)
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
auth.authenticate(code, redirect_uri, client_secret, resource=resource)
# If you have access to more than one service, you'll need to decide
# which ServiceInfo to use instead of just using the first one, as below.
service_info = ResourceDiscoveryRequest().get_service_info(auth.access_token)[0]
auth.redeem_refresh_token(service_info.service_resource_id)
client = onedrivesdk.OneDriveClient(service_info.service_resource_id + '/_api/v2.0/', auth, http)
I registered an APP and got a secret and id. But when I ran this I got scope is invalid errors. Plus it tries to launch a webpage which isn't great for a command line kinda environment. I think this SDK might be outdated as well because originally this script had login.microsoftonline, but that wasn't reachable so I changed it to login.live.com.
I wrote this sample code you posted. You replaced the auth_server_URLwith the authentication URL for Microsoft Account authentication, which can only be used to access OneDrive (the consumer product). You need to continue using the login.microsoftonline.com URL to log into your OneDrive for Business account.
You are correct that this pops up a dialog. However, you can write a little supporting code so that only happens the first time you log into a particular app. Follow these steps (assuming you are using the default implementation of AuthProvider:
Use the sample code above up through the line auth.redeem_refresh_token()
The AuthProvider will now have a Session object, which caches the credentials of the current user and session. Use AuthProvider.save_session() to save the credentials for later.
Next time you start your app, use AuthProvider.load_session() and AuthProvider.refresh_token() to retrieve the previous session and refresh the auth token. This will all be headless.
Take note that the default implementation of SessionBase (found here) uses Pickle and is not safe for product use. Make sure to create a new implementation of Session if you intend to deploy this app to other users.
Onerive's website shows "Not Yet" on "OneDrive SDK for Python" to "OneDrive for Business"
https://dev.onedrive.com/SDKs.htm
The github sample codes did not work for me either, it tried to popup a window of authentication, but IE can not find the address:
http://('https//login.microsoftonline.com/common/oauth2/authorize',)?redirect_uri=http%3A%2F%2Flocalhost%3A8080&client_id=034xxxx9-9xx8-4xxf-bexx-1bc5xxxxbd0c&response_type=code
or removed all the "-" in client id
http://('https//login.microsoftonline.com/common/oauth2/authorize',)?redirect_uri=http%3A%2F%2Flocalhost%3A8080&client_id=034xxxx99xx84xxfbexx1bc5xxxxbd0c&response_type=code
Either way, I got the same result, IE did not show the popup with a line "This page can’t be displayed"
Authenticating requests, especially with Google's API's is so incredibly confusing!
I'd like to make authorized HTTP POST requests through python in order to query data from the datastore. I've got a service account and p12 file all ready to go. I've looked at the examples, but it seems no matter which one I try, I'm always unauthorized to make requests.
Everything works fine from the browser, so I know my permissions are all in order. So I suppose my question is, how do I authenticate, and request data securely from the Datastore API through python?
I am so lost...
You probably should not be using raw POST requests to use Datastore, instead use the gcloud library to do the heavy lifting for you.
I would also recommend the Python getting started page, as it has some good tutorials.
Finally, I recorded a podcast where I go over the basics of using Datastore with Python, check it out!
Here is the code, and here is an example:
#Import libraries
from gcloud import datastore
import os
#The next few lines will set up your environment variables
#Replace "YOUR_RPOEJCT_ID_HERE" with the correct value in code.py
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "key.json"
projectID = "YOUR_RPOEJCT_ID_HERE"
os.environ["GCLOUD_TESTS_PROJECT_ID"] = projectID
os.environ["GCLOUD_TESTS_DATASET_ID"] = projectID
datastore.set_default_dataset_id(projectID)
#Let us build a message board / news website
#First, create a fake email for our fake user
email = "me#fake.com"
#Now, create a 'key' for that user using the email
user_key = datastore.Key('User', email)
#Now create a entity using that key
new_user = datastore.Entity( key=user_key )
#Add some fields to the entity
new_user["name"] = unicode("Iam Fake")
new_user["email"] = unicode(email)
#Push entity to the Cloud Datastore
datastore.put( new_user )
#Get the user from datastore and print
print( datastore.get(user_key) )
This code is licensed under Apache v2