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:
Joseph Schorr 2016-12-08 16:11:57 -05:00
parent 34f2ddce87
commit ff52fde8a5
8 changed files with 72 additions and 48 deletions

View file

@ -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',