Have Quay always use an OAuth-specific CSRF token
This change ensures that we always store and then check the contents of the OAuth `state` argument against a session-stored CSRF token. Fixes https://www.pivotaltracker.com/story/show/135803615
This commit is contained in:
parent
34f2ddce87
commit
ff52fde8a5
8 changed files with 72 additions and 48 deletions
|
|
@ -12,6 +12,7 @@ from auth.process import require_session_login
|
|||
from data import model
|
||||
from endpoints.common import common_login, route_show_if
|
||||
from endpoints.web import index
|
||||
from endpoints.csrf import csrf_protect, OAUTH_CSRF_TOKEN_NAME
|
||||
from util.security.jwtutil import decode, InvalidTokenError
|
||||
from util.validation import generate_valid_usernames
|
||||
|
||||
|
|
@ -19,6 +20,7 @@ logger = logging.getLogger(__name__)
|
|||
client = app.config['HTTPCLIENT']
|
||||
oauthlogin = Blueprint('oauthlogin', __name__)
|
||||
|
||||
oauthlogin_csrf_protect = csrf_protect(OAUTH_CSRF_TOKEN_NAME, 'state', all_methods=True)
|
||||
|
||||
def render_ologin_error(service_name, error_message=None, register_redirect=False):
|
||||
user_creation = bool(features.USER_CREATION and features.DIRECT_LOGIN)
|
||||
|
|
@ -32,6 +34,7 @@ def render_ologin_error(service_name, error_message=None, register_redirect=Fals
|
|||
}
|
||||
return index('', error_info=error_info)
|
||||
|
||||
|
||||
def get_user(service, token):
|
||||
token_param = {
|
||||
'access_token': token,
|
||||
|
|
@ -44,7 +47,7 @@ def get_user(service, token):
|
|||
return got_user.json()
|
||||
|
||||
|
||||
def conduct_oauth_login(service, user_id, username, email, metadata={}):
|
||||
def conduct_oauth_login(service, user_id, username, email, metadata=None):
|
||||
service_name = service.service_name()
|
||||
to_login = model.user.verify_federated_login(service_name.lower(), user_id)
|
||||
if not to_login:
|
||||
|
|
@ -66,17 +69,12 @@ def conduct_oauth_login(service, user_id, username, email, metadata={}):
|
|||
prompts = model.user.get_default_user_prompts(features)
|
||||
to_login = model.user.create_federated_user(new_username, email, service_name.lower(),
|
||||
user_id, set_password_notification=True,
|
||||
metadata=metadata,
|
||||
metadata=metadata or {},
|
||||
prompts=prompts)
|
||||
|
||||
# Success, tell analytics
|
||||
analytics.track(to_login.username, 'register', {'service': service_name.lower()})
|
||||
|
||||
state = request.args.get('state', None)
|
||||
if state:
|
||||
logger.debug('Aliasing with state: %s', state)
|
||||
analytics.alias(to_login.username, state)
|
||||
|
||||
except model.InvalidEmailAddressException:
|
||||
message = "The e-mail address %s is already associated " % (email, )
|
||||
message = message + "with an existing %s account." % (app.config['REGISTRY_TITLE_SHORT'], )
|
||||
|
|
@ -96,6 +94,7 @@ def conduct_oauth_login(service, user_id, username, email, metadata={}):
|
|||
|
||||
return render_ologin_error(service_name)
|
||||
|
||||
|
||||
def get_email_username(user_data):
|
||||
username = user_data['email']
|
||||
at = username.find('@')
|
||||
|
|
@ -107,6 +106,7 @@ def get_email_username(user_data):
|
|||
|
||||
@oauthlogin.route('/google/callback', methods=['GET'])
|
||||
@route_show_if(features.GOOGLE_LOGIN)
|
||||
@oauthlogin_csrf_protect
|
||||
def google_oauth_callback():
|
||||
error = request.args.get('error', None)
|
||||
if error:
|
||||
|
|
@ -136,6 +136,7 @@ def google_oauth_callback():
|
|||
|
||||
@oauthlogin.route('/github/callback', methods=['GET'])
|
||||
@route_show_if(features.GITHUB_LOGIN)
|
||||
@oauthlogin_csrf_protect
|
||||
def github_oauth_callback():
|
||||
error = request.args.get('error', None)
|
||||
if error:
|
||||
|
|
@ -199,6 +200,7 @@ def github_oauth_callback():
|
|||
@oauthlogin.route('/google/callback/attach', methods=['GET'])
|
||||
@route_show_if(features.GOOGLE_LOGIN)
|
||||
@require_session_login
|
||||
@oauthlogin_csrf_protect
|
||||
def google_oauth_attach():
|
||||
code = request.args.get('code')
|
||||
token = google_login.exchange_code_for_token(app.config, client, code,
|
||||
|
|
@ -236,6 +238,7 @@ def google_oauth_attach():
|
|||
@oauthlogin.route('/github/callback/attach', methods=['GET'])
|
||||
@route_show_if(features.GITHUB_LOGIN)
|
||||
@require_session_login
|
||||
@oauthlogin_csrf_protect
|
||||
def github_oauth_attach():
|
||||
code = request.args.get('code')
|
||||
token = github_login.exchange_code_for_token(app.config, client, code)
|
||||
|
|
@ -276,6 +279,7 @@ def decode_user_jwt(token, oidc_provider):
|
|||
|
||||
@oauthlogin.route('/dex/callback', methods=['GET', 'POST'])
|
||||
@route_show_if(features.DEX_LOGIN)
|
||||
@oauthlogin_csrf_protect
|
||||
def dex_oauth_callback():
|
||||
error = request.values.get('error', None)
|
||||
if error:
|
||||
|
|
@ -318,6 +322,7 @@ def dex_oauth_callback():
|
|||
@oauthlogin.route('/dex/callback/attach', methods=['GET', 'POST'])
|
||||
@route_show_if(features.DEX_LOGIN)
|
||||
@require_session_login
|
||||
@oauthlogin_csrf_protect
|
||||
def dex_oauth_attach():
|
||||
code = request.args.get('code')
|
||||
token = dex_login.exchange_code_for_token(app.config, client, code, redirect_suffix='/attach',
|
||||
|
|
|
|||
Reference in a new issue