(making no attempt to fix messed up quoting, please take a look at your mail client configuration)
On Wed, Sep 26, 2012 at 1:52 AM, Littlefield, Tyler <ty...@tysdomain.com> wrote: > On 9/25/2012 2:05 PM, Demian Brecht wrote: > > This is a shameless plug, but if you want a much easier to understand method > of accessing protected resources via OAuth2, I have a 55 LOC client > implementation with docs and examples here: > https://github.com/demianbrecht/sanction (Google is one of the tested > providers with an access example). > > > No complaints from me if it works. Honestly I was a bit discouraged at > Google's decent lack of documentation and the quality of the code. > If you are writing a desktop application, read this: https://developers.google.com/accounts/docs/OAuth2#clientside > > Are you trying to access resources client side (through Javascript) or > server side? Either way, the redirect URI *is* important. The first step is > to have your user authorize your application using Google's authorization > page. As one of the query parameters, you must specify the redirect URI > (which must match those registered through Google's app console). > > I'm trying to access it through a desktop Python application, which made me > really confused. There was something else that talked about returning the > tokens in a different way, but it talked about returning them in the title > of the webpage, and since I'd be spawning a browser to request > authorization, I'd have to write something that would pull the window > information and then parse out the token from the title, which doesn't sound > to stable. > After authenticating with google from a web browser, and authorizing your application, the user will be presented with a web page from which they can copy the access token (from the url or a text field presented in the web page) and input into your application using whatever mechanism you choose. If you are a simple text-based app, you can just use input() to ask for the token. If you are a GUI app, you can do other things, such as embedding a web browser into your application and reading the redirected URL. > > Once the user has authorized your application, they're redirected back to > your site (via the specified redirect URI), with a "code" attached as a > query param. Once you get that code, you must exchange that with Google's > token endpoint to retrieve the access and refresh tokens. > > Awesome. I could theoretically just create a webpage on my server to > redirect people to with the query, but I'm still not quite sure how I'd > retrieve that from the desktop application. > > > No, it doesn't matter which library you use. Google's (imho) is overly > verbose and difficult to grok (especially for someone new to either OAuth > 2.0 or Python, or both). The client ID doesn't need to be kept private, but > the secret does. You should *never* put this anywhere that can be read > publicly. > > I plan on storing them both in variables. It's not going to be the best > solution, but I plan to use python -O to create pyo files, which from what I > understand are harder to decompile, and it'll be in a py2exe executable. > Still not to hard to get at, but it's not right there either. > Don't worry about it. There is no way to keep it secret for desktop applications. It is exactly the same as the DRM problem. The google documentation itself admits: "... These applications, in general, cannot keep secrets.". You should store the given tokens persistently, though. > <snipped> In the past, I made a half-hearted attempt to writing something to upload stuff to google calendar from .ics files. Here's the google-authentication part of the code, if you can use it. I cannot, at the moment, recall why I did not use the oauth2client library from google. #!/usr/bin/env python3 import datetime import json import os.path import configparser import requests import webbrowser import urllib.parse def _store_tokens(auth_data, filename): expiry_time = (datetime.datetime.now() + datetime.timedelta(seconds=int(auth_data['expires_in']))) auth_data['expiry_time'] = expiry_time.isoformat() with open(filename, 'w') as file_stream: json.dump(auth_data, file_stream) def _get_new_token(config, token_file): payload = { 'response_type' : 'code', 'client_id' : config['api']['client_id'], 'redirect_uri' : config['api']['redirect_uri'], 'scope' : 'https://www.googleapis.com/auth/calendar', 'state' : 'init' } url = 'https://accounts.google.com/o/oauth2/auth' params = ['{}={}'.format(key, urllib.parse.quote(value)) for (key, value) in payload.items()] webbrowser.open('{}?{}'.format(url, '&'.join(params)), new=2) auth_code = input('Enter authorization code obtained: ') payload = { 'code' : auth_code, 'client_id' : config['api']['client_id'], 'client_secret' : config['api']['client_secret'], 'redirect_uri' : config['api']['redirect_uri'], 'grant_type' : 'authorization_code' } r = requests.post('https://accounts.google.com/o/oauth2/token', data=payload) auth_data = json.loads(r.text) _store_tokens(auth_data, token_file) return auth_data['access_token'] def _refreshed_token(config, auth_data, token_file): payload = { 'refresh_token' : auth_data['refresh_token'], 'client_id' : config['api']['client_id'], 'client_secret' : config['api']['client_secret'], 'grant_type' : 'refresh_token' } r = requests.post('https://accounts.google.com/o/oauth2/token', data=payload) auth_data = json.loads(r.text) _store_tokens(auth_data, token_file) return auth_data['access_token'] def _get_existing_token(config, token_file): with open(token_file) as token_stream: auth_data = json.load(token_stream) now = datetime.datetime.now().isoformat() if auth_data['expiry_time'] < now: return _refreshed_token(config, auth_data, token_file) return auth_data['access_token'] def auth(config): token_file = os.path.expanduser(config['api']['token_file']) if os.path.exists(token_file): return _get_existing_token(config, token_file) else: return _get_new_token(config, token_file) The "config" object required by the auth function is from a config file that looks like this: [api] client_id = <get your client id from google> client_secret = <get your client secret from google> redirect_uri = urn:ietf:wg:oauth:2.0:oob token_file = ~/.google-tokens The "scope" value in the _get_new_token function will need to be changed to whatever scope your application needs. This code uses the python requests module for http(s) requests. It will start up a web browser to let the user authenticate and then asks for the token. The google api will issue two tokens: an "access" token and a "refresh" token. The access token will have a short expiry time, after which you need to get another access token issued. You use the refresh token for that reissue request. -- regards, kushal -- http://mail.python.org/mailman/listinfo/python-list