Authentication and Authorization
======================================================================
For authentication and authorization Flickr uses
`OAuth 1.0a `_. This ensures that in one
flow, the user is authenticated via the Flickr website, and the application
is authorized by the user to act in its name.
The user's photos may be private. Access to her account is private for sure.
A lot of Flickr API calls require the application to be authorized.
This means that the user has to tell Flickr that the application is
allowed to do whatever it needs to do.
The Flickr document `User Authentication`_ explains the authentication
process; it's good to know what's in there before you go on. The Python
Flickr API takes care of most of the OAuth details, but still it is
important to know the authentication flow. In short:
1. Get a request token from Flickr.
2. Send the user's web browser to Flickr to log in and authorize the
application.
3. The browser is redirected to the application's callback URL to
pass the application a verification code.
4. Use the verification code to trade the request token for an access
token.
5. The access token can be stored by the application, so that future
calls don't have to follow these steps.
Here is a simple example that does all the above in one simple call to
``authenticate_via_browser``::
import flickrapi
api_key = u'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
api_secret = u'YYYYYYYYYYYYYYYY'
flickr = flickrapi.FlickrAPI(api_key, api_secret)
flickr.authenticate_via_browser(perms='read')
The ``api_key`` and ``api_secret`` can be obtained from
http://www.flickr.com/services/api/keys/. Every application should use
its own key and secret.
The call to ``flickr.authenticate_via_browser(...)`` does a lot of
things. First, it checks the on-disk token cache. After all, the
application may be authenticated already. If a token is found, it is
checked with Flickr for validity. If it is valid, it is used for all
following calls and the authentication process is complete.
If the application isn't authenticated yet, a browser opens the Flickr
page, on which the user can grant the application the appropriate
access. When the user presses the "OK, I'LL AUTHORIZE IT" button, the
browser will be redirected to a callback URL or display a verification
code. The code is passed then to the application. When this code has
been received, the token is stored in the token cache and the
authentication process is complete.
.. _`User Authentication`: http://www.flickr.com/services/api/auth.oauth.html
Non-web applications
--------------------------------------------------
OAuth was designed for web-based applications. After authorizing the
application the browser is sent to an papplication-specific callback
URL with a verification code appended. When your application is not
web-based, you have two options:
1. Use "oob" as the callback URL. Flickr displays the verification
code, which the user has to copy-and-paste to your application.
This is described in `Authenticating without local web server`_.
2. Use Python Flickr API's local webserver. It is only available on
the machine the application is running on, and listens on a
random port. This is described in the rest of this section.
Python Flickr API uses a local web server by default, and this is by
far the easiest way to authorize desktop applications.
.. todo:: more explanation; include timeout and GUI examples.
Authenticating without local web server
----------------------------------------------------------------------
By default a webbrowser is started to let the user perform the
authentication. A local web server is then started to receive the OAuth
verifier code. Upon authorizing the application the browser is sent to this
web server, where ``FlickrAPI`` obtains the verifier code.
However, this may not be appropriate or even possible in your application.
When a local web server is not used, you can use "out of band" passing of
the verifier code::
import flickrapi
import webbrowser
api_key = u'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
api_secret = u'YYYYYYYYYYYYYYYY'
flickr = flickrapi.FlickrAPI(api_key, api_secret)
print('Step 1: authenticate')
# Only do this if we don't have a valid token already
if not flickr.token_valid(perms='read'):
# Get a request token
flickr.get_request_token(oauth_callback='oob')
# Open a browser at the authentication URL. Do this however
# you want, as long as the user visits that URL.
authorize_url = flickr.auth_url(perms='read')
webbrowser.open_new_tab(authorize_url)
# Get the verifier code from the user. Do this however you
# want, as long as the user gives the application the code.
verifier = str(input('Verifier code: '))
# Trade the request token for an access token
flickr.get_access_token(verifier)
print('Step 2: use Flickr')
resp = flickr.photos.getInfo(photo_id='7658567128')
Authenticating web applications
----------------------------------------------------------------------
When working with web applications, things are a bit different. The
user using the application (through a browser) is likely to be
different from the user running the server-side software. You can pass
a username to the ``FlickrAPI`` constructor, so that access tokens
from different users won't be mixed up.
.. todo:: web flow
Token handling in web applications
----------------------------------------------------------------------
Web applications have two kinds of users: identified and anonymous
users. If your users are identified, you can pass their name (or other
means of identification) as the ``username`` parameter to the
``FlickrAPI`` constructor, and get a FlickrAPI instance that's bound
to that user. It will keep track of the authentication token for that
user, and there's nothing special you'll have to do.
When working with anonymous users, you'll have to store their access
token in a cookie.
.. todo:: concrete examples
like this::
flickr = flickrapi.FlickrAPI(api_key, api_secret, token=token)
It won't be stored in the on-disk token cache - which is a good thing,
since
A. you don't know who the user is, so you wouldn't be able to
retrieve the appropriate tokens for visiting users.
B. the tokens are stored in cookies, so there is no need to store
them in another place.
Preventing usage of on-disk token cache
----------------------------------------------------------------------
If for any reason you want to make sure the access token is not
stored, pass ``store_token=False`` as constructor parameter. Use this
if you want to be absolutely sure that the FlickrAPI instance doesn't
use any previously stored tokens, nor that it will store new tokens.
Configuring location of on-disk token cache
----------------------------------------------------------------------
By default the authentication tokens are stored in ``~/.flickr``. If
you want to change this, just pass
``token_cache_location='/path/to/token/cache/dir'`` as constructor
parameter.
Multiple processes using the same key
----------------------------------------------------------------------
The token database uses SQLite3, so it should be safe to access using
mutiple processes at the same time.
Example using Django
----------------------------------------------------------------------
.. todo:: Update this example.
Here is a simple example in `Django `_::
import flickrapi
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponse
import logging
logging.basicConfig()
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
def require_flickr_auth(view):
'''View decorator, redirects users to Flickr when no valid
authentication token is available.
'''
def protected_view(request, *args, **kwargs):
if 'token' in request.session:
token = request.session['token']
log.info('Getting token from session: %s' % token)
else:
token = None
log.info('No token in session')
f = flickrapi.FlickrAPI(settings.FLICKR_API_KEY,
settings.FLICKR_API_SECRET, token=token,
store_token=False)
if token:
# We have a token, but it might not be valid
log.info('Verifying token')
try:
f.auth_checkToken()
except flickrapi.FlickrError:
token = None
del request.session['token']
if not token:
# No valid token, so redirect to Flickr
log.info('Redirecting user to Flickr to get frob')
url = f.auth_url(perms='read')
return HttpResponseRedirect(url)
# If the token is valid, we can call the decorated view.
log.info('Token is valid')
return view(request, *args, **kwargs)
return protected_view
def callback(request):
log.info('We got a callback from Flickr, store the token')
f = flickrapi.FlickrAPI(settings.FLICKR_API_KEY,
settings.FLICKR_API_SECRET, store_token=False)
frob = request.GET['frob']
token = f.get_token(frob)
request.session['token'] = token
return HttpResponseRedirect('/content')
@require_flickr_auth
def content(request):
return HttpResponse('Welcome, oh authenticated user!')
Every view that calls an authenticated Flickr method should be
decorated with ``@require_flickr_auth``. For more information on
function decorators, see `PEP 318 `_.
The ``callback`` view should be called when the user is sent to the
callback URL as defined in your Flickr API key. The key and secret
should be configured in your settings.py, in the properties
``FLICKR_API_KEY`` and ``FLICKR_API_SECRET``.