Fix OAuth 2 handler to support retrieving parameters from other places; various OAuth client (such as the Go library) send the values in the request body or even the Auth header

This commit is contained in:
Joseph Schorr 2015-05-18 12:38:39 -04:00
parent 91b464d0de
commit fb8e718c44
3 changed files with 35 additions and 14 deletions

View file

@ -89,12 +89,13 @@ def truthy_param(param):
return param not in {False, 'false', 'False', '0', 'FALSE', '', 'null'}
def param_required(param_name):
def param_required(param_name, allow_body=False):
def wrapper(wrapped):
@wraps(wrapped)
def decorated(*args, **kwargs):
if param_name not in request.args:
abort(make_response('Required param: %s' % param_name, 400))
if not allow_body and param_name not in request.values:
abort(make_response('Required param: %s' % param_name, 400))
return wrapped(*args, **kwargs)
return decorated
return wrapper

View file

@ -25,6 +25,7 @@ from endpoints.trigger import (CustomBuildTrigger, BitbucketBuildTrigger, Trigge
from util.names import parse_repository_name, parse_repository_name_and_tag
from util.useremails import send_email_changed
from util.systemlogs import build_logs_archive
from util.headers import parse_basic_auth
from auth import scopes
import features
@ -462,19 +463,22 @@ def request_authorization_code():
@web.route('/oauth/access_token', methods=['POST'])
@no_cache
@param_required('grant_type')
@param_required('client_id')
@param_required('client_secret')
@param_required('redirect_uri')
@param_required('code')
@param_required('scope')
@param_required('grant_type', allow_body=True)
@param_required('client_id', allow_body=True)
@param_required('redirect_uri', allow_body=True)
@param_required('code', allow_body=True)
@param_required('scope', allow_body=True)
def exchange_code_for_token():
grant_type = request.form.get('grant_type', None)
client_id = request.form.get('client_id', None)
client_secret = request.form.get('client_secret', None)
redirect_uri = request.form.get('redirect_uri', None)
code = request.form.get('code', None)
scope = request.form.get('scope', None)
grant_type = request.values.get('grant_type', None)
client_id = request.values.get('client_id', None)
redirect_uri = request.values.get('redirect_uri', None)
code = request.values.get('code', None)
scope = request.values.get('scope', None)
client_secret = request.values.get('client_secret', None)
if client_secret is None:
# Sometimes OAuth2 clients place the client secret in the Auth header.
client_secret = parse_basic_auth(request.headers.get('Authorization'))
provider = FlaskAuthorizationProvider()
return provider.get_token(grant_type, client_id, client_secret, redirect_uri, code, scope=scope)

16
util/headers.py Normal file
View file

@ -0,0 +1,16 @@
import base64
def parse_basic_auth(header_value):
""" Attempts to parse the given header value as a Base64-encoded Basic auth header. """
if not header_value:
return None
parts = header_value.split(' ')
if len(parts) != 2 or parts[0].lower() != 'basic':
return None
try:
return base64.b64decode(parts[1])
except ValueError:
return None