From e32b410c797fbc0a0fadc588ad6ed7569af6939f Mon Sep 17 00:00:00 2001 From: Tony Lim Date: Mon, 25 Feb 2013 14:26:12 +0700 Subject: [PATCH] add Flickr authentication add Flickr authentication --- README | 13 ++++++++++ example/handlers.py | 5 ++++ example/secrets.py.template | 18 ++++++++++++-- example/templates/home.html | 1 + simpleauth/handler.py | 47 ++++++++++++++++++++++++++++++------- 5 files changed, 74 insertions(+), 10 deletions(-) diff --git a/README b/README index 8b1af68..5590886 100644 --- a/README +++ b/README @@ -13,6 +13,7 @@ Supported providers out of the box: - foursquare (OAuth 2.0) - Twitter (OAuth 1.0a) - LinkedIn (OAuth 1.0a) + - Flickr (OAuth 1.0a) - OpenID, using App Engine users module API Dependencies: @@ -186,6 +187,18 @@ versions, say one on localhost and another on example.org, you'll probably want to register two applications (e.g. "dev" and "production") and use appropriate set of key/secret accordingly. +== Flickr +Docs: http://www.flickr.com/services/api/auth.oauth.html +Get client/secret: http://www.flickr.com/services/apps/create/apply/ + +Scopes are not supported. This is OAuth 1.0a. However, Flickr requires a 'perms' parameter +read/write/delete which is set in secrets.py + +Callback URL is required when setting up your flickr app. So, if you have two +versions, say one on localhost and another on example.org, you'll probably +want to register two applications (e.g. "dev" and "production") and use +appropriate set of key/secret accordingly. + CSRF protection ================ diff --git a/example/handlers.py b/example/handlers.py index 0912ec6..a04e41a 100644 --- a/example/handlers.py +++ b/example/handlers.py @@ -119,6 +119,11 @@ class AuthHandler(BaseRequestHandler, SimpleAuthHandler): 'first-name' : 'name', 'public-profile-url': 'link' }, + 'flickr' : { + 'buddy_icon_url' : 'avatar_url', + 'username' : 'name', + 'link': 'link' + }, 'foursquare' : { 'photo' : lambda photo: ('avatar_url', photo.get('prefix') + '100x100' + photo.get('suffix')), 'firstName': 'firstName', diff --git a/example/secrets.py.template b/example/secrets.py.template index 1250f30..d49de55 100644 --- a/example/secrets.py.template +++ b/example/secrets.py.template @@ -1,3 +1,9 @@ +import os +import logging + +DEBUG = os.environ['SERVER_SOFTWARE'].startswith('Dev') +logging.info("Starting application in DEBUG mode: %s", DEBUG) + # Copy this file into secrets.py and set keys, secrets and scopes. # This is a session secret key used by webapp2 framework. @@ -31,6 +37,13 @@ TWITTER_CONSUMER_SECRET = 'oauth1.0a consumer secret' FOURSQUARE_CLIENT_ID = 'client id' FOURSQUARE_CLIENT_SECRET = 'client secret' +# Flickr APIs +#http://www.flickr.com/services/apps/create/apply/ +FLICKR_CONSUMER_KEY = 'oauth1.0a consumer dev key' if DEBUG else 'oauth1.0a consumer key' +FLICKR_CONSUMER_SECRET = 'oauth1.0a consumer dev secret' if DEBUG else 'oauth1.0a consumer secret' +#read, write, or delete +FLICKR_CONSUMER_PERMS = 'read' + # config that summarizes the above AUTH_CONFIG = { # OAuth 2.0 providers @@ -44,8 +57,9 @@ AUTH_CONFIG = { 'authorization_code'), # OAuth 1.0 providers don't have scopes - 'twitter' : (TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET), - 'linkedin' : (LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET), + 'twitter' : (TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, ''), + 'flickr' : (FLICKR_CONSUMER_KEY, FLICKR_CONSUMER_SECRET, FLICKR_CONSUMER_PERMS), + 'linkedin' : (LINKEDIN_CONSUMER_KEY, LINKEDIN_CONSUMER_SECRET, ''), # OpenID doesn't need any key/secret } diff --git a/example/templates/home.html b/example/templates/home.html index a3f64e2..58a912d 100644 --- a/example/templates/home.html +++ b/example/templates/home.html @@ -15,5 +15,6 @@ LinkedIn Windows Live foursquare + Flickr {% endif %} {% endblock %} diff --git a/simpleauth/handler.py b/simpleauth/handler.py index 9cb0ccb..cf9e4e0 100644 --- a/simpleauth/handler.py +++ b/simpleauth/handler.py @@ -89,6 +89,10 @@ class SimpleAuthHandler(object): 'request': 'https://api.twitter.com/oauth/request_token', 'auth' : 'https://api.twitter.com/oauth/authenticate?{0}' }, 'https://api.twitter.com/oauth/access_token'), + 'flickr' : ('oauth1', { + 'request': 'http://www.flickr.com/services/oauth/request_token', + 'auth' : 'http://www.flickr.com/services/oauth/authorize?{0}' + }, 'http://www.flickr.com/services/oauth/access_token'), 'foursquare': ('oauth2', 'https://foursquare.com/oauth2/authenticate?{0}', 'https://foursquare.com/oauth2/access_token'), @@ -102,7 +106,8 @@ class SimpleAuthHandler(object): 'foursquare' : '_json_parser', 'facebook' : '_query_string_parser', 'linkedin' : '_query_string_parser', - 'twitter' : '_query_string_parser' + 'twitter' : '_query_string_parser', + 'flickr' : '_query_string_parser' } # Set this to True in your handler if you want to use @@ -233,7 +238,7 @@ def _oauth2_callback(self, provider, access_token_url): def _oauth1_init(self, provider, auth_urls): """Initiates OAuth 1.0 dance""" - key, secret = self._get_consumer_info_for(provider) + key, secret, perms = self._get_consumer_info_for(provider) callback_url = self._callback_uri_for(provider) token_request_url = auth_urls.get('request', None) auth_url = auth_urls.get('auth', None) @@ -252,11 +257,17 @@ def _oauth1_init(self, provider, auth_urls): if not request_token.get('oauth_token', None): raise AuthProviderResponseError( "Couldn't get a request token from %s" % str(request_token), provider) - - target_url = auth_urls['auth'].format(urlencode({ - 'oauth_token': request_token.get('oauth_token', None), - 'oauth_callback': callback_url - })) + + params = { + 'oauth_token': request_token.get('oauth_token', None), + 'oauth_callback': callback_url, + 'perms': (perms, None) + } + + if perms: + params.update(perms=perms) + + target_url = auth_urls['auth'].format(urlencode(params)) logging.debug('Redirecting user to %s', target_url) @@ -276,7 +287,7 @@ def _oauth1_callback(self, provider, access_token_url): raise AuthProviderResponseError( "No OAuth verifier was provided", provider) - consumer_key, consumer_secret = self._get_consumer_info_for(provider) + consumer_key, consumer_secret, consumer_perms = self._get_consumer_info_for(provider) token = oauth1.Token(request_token['oauth_token'], request_token['oauth_token_secret']) token.set_verifier(verifier) @@ -448,6 +459,26 @@ def _get_twitter_user_info(self, auth_info, key=None, secret=None): uinfo = json.loads(content) uinfo.setdefault('link', 'http://twitter.com/%s' % uinfo['screen_name']) return uinfo + + def _get_flickr_user_info(self, auth_info, key=None, secret=None): + """Returns a dict of twitter user using + https://api.twitter.com/1/account/verify_credentials.json + """ + token = oauth1.Token(key=auth_info['oauth_token'], + secret=auth_info['oauth_token_secret']) + client = self._oauth1_client(token, key, secret) + + resp, content = client.request( + 'http://api.flickr.com/services/rest?format=json&nojsoncallback=1&method=flickr.people.getInfo&user_id=%s' % auth_info['user_nsid'] + ) + uinfo = json.loads(content) + uinfo.setdefault('link', uinfo['person']['profileurl']['_content']) + uinfo.setdefault('id', uinfo['person']['id']) + uinfo.setdefault('username', uinfo['person']['username']['_content']) + uinfo.setdefault('buddy_icon_url', 'http://farm%s.staticflickr.com/%s/buddyicons/%s.jpg' + % (uinfo['person']['iconfarm'],uinfo['person']['iconserver'], uinfo['person']['id'])) + + return uinfo # # aux methods