gdata-python-api + Analytics with simple auth - python

I'm working on converting a Python script using the Google gdata API client + user/pass authentication to something more suitable for production (an API key). I am pretty frustrated with the muddled state of their documentation on authentication. I admittedly don't have a great grasp of OAuth2, but it seems like it's way more complicated for my usage case, which is: Hit Google Analytics every 24 hours to get the X most popular articles on our site.
In this scenario, we're not dealing with modifying someone's personal data, and all activity is centered on one account. It doesn't seem like OAuth2 is worth the complexity for something so simple.
I see that on the Google API Console (https://code.google.com/apis/console/), I've registered there and notice that there's a "Simple API Access" section with one key beneath the "Client ID for web applications" (which appears to be OAuth2). There's also the Google domain update page, https://www.google.com/accounts/UpdateDomain, but that appears to be OAuth related.
Is there any way to use this Simple API Access key (not OAuth) for retrieving analytics data with the Python gdata client, and if so, does anyone have any authentication examples? I already have the data retrieval stuff working once authenticated, but I'm using the user/pass approach, which is not appropriate for production.

Greg,
If you are already using the library gdata-python-client, this is relatively easy to do if you are the only user that your application will be authorizing.
The general mechanisms were detailed in a blog post in September, 2011, but I'll describe them here for completeness.
Part 1: Go to the APIs console and start a new project.
Part 2: From the project, go to "Services" and enable "Analytics API"
Part 3: From the project, go to "API Access" and click "Create an OAuth 2.0 client ID..." (you'll need to provide a product name, though the value you provide won't matter). When asked for the application type, select "Installed Application" and then "Create client ID". Since you will be the only user, you will only need one refresh token, and you can get this by authorizing from a desktop application a single time.
Part 4: Get your client id and client secret from the APIs console and then create an empty token:
import gdata.gauth
CLIENT_ID = 'id-from-apis-console'
CLIENT_SECRET = 'secret-from-apis-console'
SCOPE = 'https://www.google.com/analytics/feeds/' # Default scope for analytics
token = gdata.gauth.OAuth2Token(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=SCOPE,
user_agent='application-name-goes-here')
I got the scope from GData FAQ, though I'm not sure if it is correct.
Part 5: Use the token to create authorization URL for you to visit:
url = token.generate_authorize_url(redirect_uri='urn:ietf:wg:oauth:2.0:oob')
Since your application is an "Installed Application", your redirect URI is the default 'urn:ietf:wg:oauth:2.0:oob'. (Also note, the blog post had a typo and used the keyword argument redirect_url.)
Part 6: Visit the url and authorize your application to make requests on behalf of your account. After authorizing, you'll be redirected to a page with a code on it. This code will be used to exchange for an access token and a long-lived refresh token. The code has a life of 10 minutes and the access token has a life of an hour. The refresh token will allow you to get new access tokens for signing requests in perpetuity (or until you revoke the permission from your account).
Part 7: Use the code to get an access token:
code = 'random-string-from-redirected-page'
token.get_access_token(code) # This returns the token, but also changes the state
This again differs slightly from the blog post, because we are using an installed application.
Part 8: With the token you can now make all requests you want to make to the analytics client:
import gdata.analytics.client
client = gdata.analytics.client.AnalyticsClient()
token.authorize(client)
This is the big money right here. When an access token expires, the API requests signed with that token are rejected. However, by authorizing the client as above, when the said requests fail, the token attempts to use the refresh token to obtain a new access token. If it successfully obtains a new access token, the client resends the original API request, signed with the new access token.
I don't know anything about the Analytics API so I won't provide any more details there.
Future Use Note 1: Saving information for future use. You can re-use this from different places and after this use very easily. There are methods called token_to_blob and token_from_blob provided by the library that allow turning a token into a string and converting out of a string:
saved_blob_string = gdata.gauth.token_to_blob(token)
Once you have done this, you can store the string in a file and kill your running Python process. When you'd like to use it again:
saved_blob_string = retrieve_string_from_file() # You'll need to implement this
token = gdata.gauth.token_from_blob(saved_blob_string)
Future Use Note 2: This token will be able to be used to authorize a client and perform all your magic again and again, so long as you have the refresh token around. If for some reason you would like to get an access token again without calling token.generate_authorize_url, you'll need to manually set this on the object:
token.redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
Future Use Note 3: Also, if you lose your refresh token and would like to get another one without having to go to the browser to revoke the original, you can use the approval_prompt parameter to get a new refresh token by visiting the url generated by:
url = token.generate_authorize_url(
redirect_uri='urn:ietf:wg:oauth:2.0:oob',
approval_prompt='force')

Related

Token expires in a certain two hours

Good afternoon, I am making an API in which it will connect to the dropbox API, the problem stems from the fact that the token does not last long, which is unclear in the documentation, does anyone of you know how to obtain the token through the endpoint or That it does not expire, I would appreciate it.
I looked in the documentation and I don't understand it well and some video tutorials don't mention it.
Dropbox is in the process of switching to only issuing short-lived access tokens (and optional refresh tokens) instead of long-lived access tokens. You can find more information on this migration here.
Apps can still get long-term access by requesting "offline" access though, in which case the app receives a "refresh token" that can be used to retrieve new short-lived access tokens as needed, without further manual user intervention. You can find more information in the OAuth Guide and authorization documentation.
You can find examples of using the OAuth app authorization flow in the Dropbox Python SDK here

Do I need to manually get new tokens for OAuth in a Django webapp?

I am a little confused right now. I am using googleapiclient to call the Docs API, google_auth_oauthlib.flow to handle the authorization flow, and google.oauth2.credentials only for, from what I can tell, the Credentials class.
I need to authorize users for my app for non-short periods of time (days-months). I need to know if I need to manually refresh their tokens should they expire.
The example Flask implementation here does not seem to manually need to refresh tokens if/when they expire. It says
# Save credentials back to session in case access token was refreshed.
in the test_api_request view as if credentials is automatically updated with a new token when the API is called by the object returned by build. Is this the case? A lot of the docs regarding these libraries have limited/vague information about how the token refresh works.
If not, how do I know when the token has expired? Does the Credentials instance have an expiry field? How do I get a new token using the refresh token?
Thanks
Looking at the source code from the three libraries involved, it seems like they manage refreshing the token for you and that's why they don't give you an expires_in field from the access token.
It's actually the drive.files().list().execute() expression that updates the access token because the refreshing is done per request (in case it's necessary), but it's the build method that makes it possible to do so. When it's called the following occurs:
build calls build_from_document (here)
build_from_document creates an instance of AuthorizedHttp (here)
The AuthorizedHttp instance manages refreshing the token when it's necessary, by calling the refresh method on Credentials (here)
You can see that the refresh method works with an expiry field (here)
And it parses the expires_in field given by the API. (here)
The three libraries involved in this process are google-auth-library-python, google-api-python-client and google-auth-library-python-httplib2.
Pretty convoluted process. If you want to read a simpler implementation of an OAuth client, you could try reading the Spotipy client implementation of the Authorization Code flow. It has nothing to do with the google libraries, but it might give you an idea of how it manages the token.
The gist of it is: You make a request to the token endpoint which gives you back an expires_in field along with the access_token, based on this field, you need to calculate the time in the future when this token will expire. When you make a request to the resource server, you need to check if the token is expired, if it is, you make a new request sending the refresh_token to the token endpoint.
I've omitted some steps, but if you want to know more, you can read about it here and in the RFC.

Spotify authorization code (not access token) is expiring - how can I circumvent this?

I am developing an app that creates a public Spotify playlist for a user who has given proper authorization to do so.
I am using Flask and Python requests library to accomplish this, but after I've sent a few consecutive POST requests to get an access token from Spotify (using authorization code obtained from previous logic), it begins to fail. I am referring to Step 4 of Authorization Code Flow from this link: https://developer.spotify.com/web-api/authorization-guide/#authorization_code_flow
I know the authorization code is valid, because it doesn't fail for the first few times I run the request (maybe 5-10 times).
When I print the response from the POST I get the following:
{'error_description': 'Authorization code expired', 'error': 'invalid_grant'}
I assume I am not using the authorization code fast enough to get an access token (after repeatedly failing on code logic before the access token POST request, I guess?) but how am I supposed to reset and refresh the authorization code so I can keep making requests repeatedly? Any info on how long I am disabled and generally good programming practice to avoid this scenario?
When you use the authorization code to get your access token, you will also get a refresh token back in the same message. Use that refresh token to request new access tokens, when the access tokens expire.
How to use the refresh token is written on the same page you linked to, just a bit further down: https://developer.spotify.com/web-api/authorization-guide/#request-access-token-from-refresh-token
I agree that this is not the easiest to understand, but there are good reasons for all these things. It is also a standard called OAuth2, which many websites use to let users authorize apps to access their data, so it is useful in a lot of places.
In this specific case: "why do I need a refresh token to get an access token, I already have an authorization code to get an access token?", it is because the authorization code has leaked to the outside because it was returned to you via the user's browser.
An authorization code is obtained when the user grants permission for the third-party application (the Client). As per OAuth's 2.0 specification the authorization code must be used once and it's recommended that it have a maximum lifetime of 10 minutes to mitigate security flaws.
Read more about authorization code here:
https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2
On the other hand, instead of boring the user to go through all the OAuth 2.0 dance again (to authenticate and to grant permissions), the server side of Client application can use Refresh Tokens to ask for a new token when it's expired.
More about refresh tokens can be found at section 10.4 of OAuth 2.0 spec.
https://www.rfc-editor.org/rfc/rfc6749#section-10.4

Python 3: Spotify Add Song To Public Playlist

So currently I have in place a system using Flask running on my localhost:8080 to add 1 song at a time to a public Spotify playlist. Unfortunately how I have implemented this it requires a browser to add a song. What I want to be able to do is URLLIB or possibly the REQUESTS library to do this without a browser. I have been unsuccessful in my attempts to do this bot URLLIB and REQUESTS only see the first page of my local host it is never redirected to my call back in the code that I linked below.
My Implementation
How would I go about implemented a non browser interface to add a song to a playlist? (Mind you this is python 3)
In order to add a song to a playlist, you need the playlist's owner to grant access to your application, so you will need a web interface at some point to carry out this step.
It's important that you obtain the access token using the Authorization Code Flow since that's how you will get an access token and a refresh token. Use that refresh token in your app to obtain access tokens without having the user to re-authorize your app.
So in brief:
Implement a web site that uses the Authorization Code. A user logs in and you obtain an access token and refresh token. Using the refresh token you will be able to generate access tokens without the user having to input their credentials.
Take the refresh token and include it in your script. Before making the request to add a track, obtain a fresh access token using the refresh token.
It's a bit cumbersome but that's the only way to use OAuth2 without exposing the user's username+password to an app.

Using other Google API's in a Google cloud endpoints project (Python)

I'm using a Google Cloud Backends Project in Python to support an Android App. I want the server to be able to make some API calls on the user's accounts
(like Contacts, Maps, etc) rather than have the Android client take this overhead.
I am not using Webapp at all. I'd prefer not to - I don't see the need per se, unless it is the only way to make this work.
Wondering how I'd go forward:
#endpoints.api(name='data_api',
version='v1',
description='Access, create or delete data for meetups.',
audiences=client_ids.allowed_client_ids,
scopes=[ # Get email and Details
endpoints.EMAIL_SCOPE,
# Get Contacts
client_ids.CONTACTS_SCOPE
])
class DataApi(remote.Service):
#UserModel.method(name='user.fetch',
user_required=True)
def get_all_contacts(self, query):
#Now what?
I suppose need to relay the Auth token onto another custom request from the server to Google API's, but I don't know how to extract it and push it forward. Couldn't find it in the documentation or any questions here.
Thanks for your help.
EDIT:
I do know how to check if a user is authenticated, but that doesn't tell me how to hit the Google API's with the same auth token.
if endpoints.get_current_user() is not None:
#Do secret authenticated business...
The final step of the OAuth2 flow, the retrieved token sits in the API request headers. We can access it so:
import os
self.bearer = os.getenv('HTTP_AUTHORIZATION')
This provides the bearer string that can be relayed to hit other APIs. With the right scopes, when done using the atom and gdata Python libraries, we need to dress this up in a new object found here: https://github.com/ca9/meetup-backend/blob/master/atom/auth.py
in the EndpointsAuth class.
Use with gdata as follows:
if e_user:
gd_client = gdata.contacts.client.ContactsClient(source='<var>intense-terra-821</var>', auth_token=EndpointsAuth())
under any endpoint function.

Categories