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:
parent
91b464d0de
commit
fb8e718c44
3 changed files with 35 additions and 14 deletions
|
@ -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
|
||||
|
|
|
@ -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
16
util/headers.py
Normal 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
|
Reference in a new issue