attached is our auth code. i made a few minor adjustments to remove some things that are specific to our app - hopefully it still runs.
note that we use this as a decorator to controller methods. On Saturday, April 26, 2014 6:09:13 PM UTC-7, samuel bonill wrote: > > Yes Christian, I'd like take a look... > > > 2014-04-26 17:24 GMT-05:00 Christian Foster Howes: > >> i have an oauth implementation that i used on app engine. i can try and >> clean it up a touch and share it if you would like. >> >> cfh >> >> >> On Saturday, April 26, 2014 7:05:55 AM UTC-7, samuel bonill wrote: >>> >>> thanks Marks, i'm using phonegap(android, iOS) as my client and >>> angularjs consume the API Rest. >>> x509 its grate but, work x509 on app engine ?, or what do you think >>> about use Oauth 2.0 <http://oauth.net/2/> ? >>> >>> >>> 2014-04-25 21:41 GMT-05:00 Samuel Marks: >>> >>>> Sure, take a look at x509 at http://web2py.com/books/ >>>> default/chapter/29/09/access-control >>>> >>>> >>>> Samuel Marks >>>> http://linkedin.com/in/samuelmarks >>>> >>>> >>>> On Sat, Apr 26, 2014 at 12:33 PM, samuel bonill wrote: >>>> >>>>> is there an example of API Rest authentication based in private/public >>>>> key with web2py?? >>>>> i don't want use username and password tokens for each request >>>>> >>>>> -- >>>>> Resources: >>>>> - http://web2py.com >>>>> - http://web2py.com/book (Documentation) >>>>> - http://github.com/web2py/web2py (Source code) >>>>> - https://code.google.com/p/web2py/issues/list (Report Issues) >>>>> --- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "web2py-users" group. >>>>> To unsubscribe from this group and stop receiving emails from it, send >>>>> an email to web2py+unsubscr...@googlegroups.com. >>>>> >>>>> For more options, visit https://groups.google.com/d/optout. >>>>> >>>> >>>> -- >>>> Resources: >>>> - http://web2py.com >>>> - http://web2py.com/book (Documentation) >>>> - http://github.com/web2py/web2py (Source code) >>>> - https://code.google.com/p/web2py/issues/list (Report Issues) >>>> --- >>>> You received this message because you are subscribed to a topic in the >>>> Google Groups "web2py-users" group. >>>> To unsubscribe from this topic, visit https://groups.google.com/d/ >>>> topic/web2py/lXfe0tpGi8U/unsubscribe. >>>> To unsubscribe from this group and all its topics, send an email to >>>> web2py+unsubscr...@googlegroups.com. >>>> For more options, visit https://groups.google.com/d/optout. >>>> >>> >>> -- >> Resources: >> - http://web2py.com >> - http://web2py.com/book (Documentation) >> - http://github.com/web2py/web2py (Source code) >> - https://code.google.com/p/web2py/issues/list (Report Issues) >> --- >> You received this message because you are subscribed to a topic in the >> Google Groups "web2py-users" group. >> To unsubscribe from this topic, visit >> https://groups.google.com/d/topic/web2py/lXfe0tpGi8U/unsubscribe. >> To unsubscribe from this group and all its topics, send an email to >> web2py+unsubscr...@googlegroups.com. >> For more options, visit https://groups.google.com/d/optout. >> > > -- Resources: - http://web2py.com - http://web2py.com/book (Documentation) - http://github.com/web2py/web2py (Source code) - https://code.google.com/p/web2py/issues/list (Report Issues) --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
import logging import json # using oauth2 lib from https://github.com/simplegeo/python-oauth2 import oauth2 as oauth import uuid from functools import wraps from gluon.http import HTTP from gluon.globals import current from google.appengine.api import memcache # before conditional models we came up with our own way to do this # you probably have a different implementation from datamodel import models # a utitlity for figuring out client versions etc from a user agent # specific to our app from apprequest import parse_user_agent class Consumer(dict): pass class XAuthServer(oauth.Server): timestamp_threshold = 86400 # In seconds, 1 day def generate_consumer_token(self): key = str(uuid.uuid4()) secret = str(uuid.uuid4()) return oauth.Token(key, secret) def generate_access_token(self): key = str(uuid.uuid4()) secret = str(uuid.uuid4()) return oauth.Token(key, secret) class XAuthProvider(object): def __init__(self, *args, **kwargs): self._server = XAuthServer() self._server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) def get_client(self, request=None): """Return the client from the OAuth parameters.""" if not isinstance(request, oauth.Request): raise ValueError('Request is not an oauth request.') client_key = request.get_parameter('oauth_consumer_key') if not client_key: raise Exception('Missing "oauth_consumer_key" parameter in ' \ 'OAuth "Authorization" header') client = models.client.get_client_by_oauth_key(client_key) if not client: raise Exception('Client "%s" not found.' % client_key) client = Consumer(client) client.secret = client['oauth_secret'] return client def is_valid(self, request): """Returns a Client object if this is a valid OAuth request.""" try: client = self.get_client(request) user_token = None if 'oauth_token' in request: # get_end_user_by_oauth_token is a utility method that wraps # the DB query. it stores the result in memcache for # next lookup. user = models.end_user.get_end_user_by_oauth_token( request['oauth_token']) if not user: raise Exception('Invalid oauth token') user_token = get_user_token(user) params = self._server.verify_request(request, client, user_token) except Exception, e: raise e return params def authenticate_user(self, request, client): """Authenticates a user.""" try: if 'oauth_token' not in request or not request['oauth_token']: raise Exception('Missing oauth token') # get_end_user_by_oauth_token is a utility method that wraps # the DB query. it stores the result in memcache for # next lookup. user = models.end_user.get_end_user_by_oauth_token( request['oauth_token']) if not user: raise Exception('Invalid oauth token') params = self._server.verify_request( request, client, get_user_token(user)) except Exception, e: raise e return user def get_oauth_request(request): """Return an OAuth Request object for the current web2py request.""" method = request.env.request_method if not request.env.http_authorization: raise Exception('Missing http authorization header.') postdata = None if method in ('POST', 'PUT') and request.env.content_type and \ request.env.content_type.startswith('application/x-www-form-urlencoded'): # In case the request body has already been read, reset the # position to the beginning request.body.reset() postdata = request.body.read() host = request.env.http_host url = '%s://%s%s' % (request.env.wsgi_url_scheme, host, request.env.web2py_original_uri) return oauth.Request.from_request( method, url, headers={'Authorization': request.env.http_authorization}, query_string=postdata) def get_user_token(user): """Get a user's oauth token.""" if not user.oauth_key or not user.oauth_secret: return None return oauth.Token(user.oauth_key, user.oauth_secret) def xauth_request(required=True, forget=True): def decorator(f): """ Decorator to authenticate xauth token and inject user object into keyword arguments. """ @wraps(f) def wrapper(*args, **kwargs): if forget: current.session.forget() # Get the OAuth headers from the request oauth_token = current.request.env.http_app_oauth_token oauth_secret = current.request.env.http_app_oauth_secret if oauth_token and oauth_secret: # Parse the client name, version and platform from the headers app_client, app_version, app_platform = parse_user_agent( current.request.env.http_user_agent ) if not app_client: raise HTTP( 400, json.dumps({ 'error': { 'message': 'invalid user agent', 'code': 14532, } }), **{'Content-Type': 'application/json'} ) # Set the app client in the request current.request.xauth_client = {'code': app_client} # Try to get the user from ID from memcache oauth_token_namespace = 'authorization_token_for_user_id' user_id = memcache.get( oauth_token, namespace=oauth_token_namespace, ) # Get the user with a key query if user_id: user = models.end_user[user_id] if not user: # The user stored in memcache was invalid and should be # deleted memcache.delete( oauth_token, namespace=oauth_token_namespace, ) if required: raise HTTP( 401, json.dumps({ 'error': { 'message': 'bad credentials', 'code': 14533, } }), **{'Content-Type': 'application/json'} ) else: current.request.xauth_user = None return f(*args, **kwargs) else: # Query the user based on the OAuth token user = models.end_user( models.end_user.oauth_key==oauth_token).select().first() if user: # Save the user into memcache to speed up the future # authenticated requests memcache.set( oauth_token, user.id, namespace=oauth_token_namespace, ) else: if required: # The user does not exist logging.info('NO USER') raise HTTP( 401, json.dumps({ 'error': { 'message': 'bad credentials', 'code': 14533, } }), **{'Content-Type': 'application/json'} ) else: current.request.xauth_user = None return f(*args, **kwargs) # Check the user's token+secret if user.oauth_key != oauth_token or \ user.oauth_secret != oauth_secret: if required: raise HTTP( 401, json.dumps({ 'error': { 'message': 'bad credentials', 'code': 14533, } }), **{'Content-Type': 'application/json'} ) else: current.request.xauth_user = None return f(*args, **kwargs) current.request.xauth_user = user # Use the old web2py authentication method else: xauth_provider = XAuthProvider() try: request = get_oauth_request(current.request) # @TODO: check difference in oauth_timestamp from our clock client = xauth_provider.get_client(request) current.request.xauth_client = client auth_user = xauth_provider.authenticate_user( request, client) current.request.xauth_user = auth_user except Exception, e: if required: logging.warn(e) # @TODO: figure out a better way to clean the oauth # error msg if there is a colon then it's user # specific info after trim that for logging msg = str(e).split(':')[0] googleanalytics.log_error_event(msg, 401) if msg.startswith('Expired timestamp'): raise HTTP(400, 'Expired timestamp') else: raise HTTP(401, 'Bad Credentials') return f(*args, **kwargs) return wrapper return decorator