Skip to content
This repository has been archived by the owner on Jun 4, 2019. It is now read-only.

add Flickr authentication #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
================

Expand Down
5 changes: 5 additions & 0 deletions example/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
18 changes: 16 additions & 2 deletions example/secrets.py.template
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
}
1 change: 1 addition & 0 deletions example/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
<a href="/auth/linkedin" class="btn">LinkedIn</a>
<a href="/auth/windows_live" class="btn">Windows Live</a>
<a href="/auth/foursquare" class="btn">foursquare</a>
<a href="/auth/flickr" class="btn">Flickr</a>
{% endif %}
{% endblock %}
47 changes: 39 additions & 8 deletions simpleauth/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down