repoze-oauth-plugin consists of two parts - a plugin for repoze.who and a plugin for repoze.what.
The repoze.who plugin is responsible for:
In the 2-legged flow it identifies and authenticates the client using the consumer key. In the 3-legged flow it creates request and access tokens. Request token authorization is left to token_authorization predicate.
See repoze.who Plugin Usage for usage information.
The repoze.who plugin needs help with request token verification. Request tokens have to be verified by users. It usually involves displaying the client information to the user and handling the verification form submission. The repoze.what plugin provides a token_authorization predicate to use in the form generation and handling.
There are three more predicates to recognize OAuth usage: is_consumer, is_oauth_user and not_oauth.
See repoze.what Plugin Usage for usage information.
You can create the plugin with:
>>> from repoze.who.plugins.oauth import OAuthPlugin
>>> oauth_plugin = OAuthPlugin(engine='sqlite:///:memory:',
... Manager=MyManager,
... realm='my-realm',
... url_request_token='/request_token',
... url_access_token='/access_token')
where:
A class responsible for client management in the database. The Manager has to take the engine as an initialization parameter.
If you are configuring the plugin through the PasteDeploy configuration file this can be an entry point, e.g. myproject.lib:MyManager.
The repoze.who plugin acts as an Identifier, Authenticator and Challenger. Therefore in order to get OAuth support you need to provide it as identifier, authenticator and challenger to the repoze.who middleware, similar to this (here we create it using repoze.what provided helper):
>>> oauth_plugin = OAuthPlugin('sqlite:///:memory:', realm='MyRealm')
>>> from repoze.what.middleware import setup_auth
>>> app = setup_auth(my_app,
... group_adapters=my_group_adapters,
... permission_adapters=my_permission_adapters,
... identifiers=[('oauth', plugin)],
... authenticators=[('oauth', plugin)],
... challengers=[('oauth', plugin)],
... **other_kwargs)
However, usually you would use some higher level middleware maker. Let’s take repoze.what-quickstart as an example:
>>> oauth_plugin = OAuthPlugin('sqlite:///:memory:', realm='MyRealm')
>>> from repoze.what.plugins.quickstart import setup_sql_auth
>>> app = setup_sql_auth(app, User, Group, Permission, Session,
... identifiers=[('oauth', oauth_plugin)],
... authenticators=[('oauth', oauth_plugin)],
... challengers=[('oauth', oauth_plugin)])
repoze-oauth-plugin uses oauth2 for OAuth specific functionality and plays well with restkit.
If you have set the OAuthPlugin with setup_sql_auth (or any other way that includes repoze.what support) you can use OAuth specific predicates provided by repoze-oauth-plugin.
This predicate is required for OAuth flow. Its role is to authorize a request token and generate a verification code.
Here is the procedure for token authorization:
As this procedure might seem a bit complex here is an example action for the imaginary OAuth using webapp:
from exampleapp.model import Session
from repoze.what.plugins.oauth import token_authorization
token_auth = token_authorization(DBSession=Session)
def authorize(environ):
"Perform token authorization"
if not token_auth.is_met(environ):
# The request token not found
abort_request(401)
if environ['REQUEST_METHOD'] == 'GET':
# Step 2. On GET token_authorization finds and stores a token in the
# environment
token = environ['repoze.what.oauth']['token']
# Step 3. We can now return a page showing the client name and token
# authorization form
return display('token_authorization.html',
client_name=token.consumer.name,
form=TokenAuthorizationForm)
elif environ['REQUEST_METHOD'] == 'POST':
# Step 4. The user POSTs the form. Take the token_key from the POST
# parameters
token_key = environ.POST['oauth_token']
# The userid usually lives in repoze identity
userid = environ['repoze.who.identity']['repoze.who.userid']
# token_authorization stores a request verification and callback
# maker function in the environment
make_callback = environ['repoze.what.oauth']['make_callback']
# This function takes a request token key and a userid. It verifies
# the request token
callback = make_callback(token_key, userid)
# Step 5.
if callback.url == 'oob':
# If the client application is not a web application the user
# will have to enter the verification code by hand
return 'Verification code: %s' % callback['verifier']
else:
# If the client application is a web application we can redirect
# to it
redirect(callback.url)
is_consumer is a predicate that checks whether the current user is a consumer acting on behalf of itself (2-legged flow):
>>> from repoze.what.plugins.oauth import is_consumer
>>> p = is_consumer()
>>> p.check_authorization(environ)
Traceback (most recent call last):
...
repoze.what.predicates.NotAuthorizedError: The current user must be a consumer
You can ask for a consumer with a particular key:
>>> p = is_consumer(consumer_key='my-app')
This predicate will not allow consumers to pass in a 3-legged flow (use is_oauth_user).
is_oauth_user is a predicate that checks whether the current user is a consumer acting on behalf of a user (3-legged flow):
>>> from repoze.what.plugins.oauth import is_oauth_user
>>> p = is_oauth_user()
>>> p.check_authorization(environ)
Traceback (most recent call last):
...
repoze.what.predicates.NotAuthorizedError: The current user must be a consumer acting on behalf of a user
You can ask for a particular consumer and/or particular user:
>>> p = is_consumer(userid='some-user', consumer_key='my-app')
not_oauth is a predicate that denies access through OAuth. All other methods are allowed (even anonymous!):
>>> from repoze.what.plugins.oauth import not_oauth
>>> p = not_oauth()
>>> p.check_authorization(environ_with_oauth)
Traceback (most recent call last):
...
repoze.what.predicates.NotAuthorizedError: Access through OAuth forbidden
>>> p.check_authorization({}) # Empty environ, no user - ok!