def access_token(): oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: return oauth_error_response(OAuthError('Invalid request parameters.')) try: # Create our access token token = oauth_server.fetch_access_token(oauth_request) if not token: return oauth_error_response(OAuthError("Cannot find corresponding access token.")) # Grab the mapping of access tokens to our identity providers oauth_map = OAuthMap.get_from_request_token(oauth_request.get_parameter("oauth_token")) if not oauth_map: return oauth_error_response(OAuthError("Cannot find oauth mapping for request token.")) oauth_map.access_token = token.key_ oauth_map.access_token_secret = token.secret oauth_map.put() # Flush the "apply phase" of the above put() to ensure that subsequent # retrievals of this OAuthmap returns fresh data. GAE's HRD can # otherwise take a second or two to propagate the data, and the # client may use the access token quicker than that. oauth_map = OAuthMap.get(oauth_map.key()) except OAuthError, e: return oauth_error_response(e)
def access_token(): oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: return oauth_error_response(OAuthError('Invalid request parameters.')) try: # Create our access token token = oauth_server.fetch_access_token(oauth_request) if not token: return oauth_error_response( OAuthError("Cannot find corresponding " "access token.")) # Grab the mapping of access tokens to our identity providers oauth_map = OAuthMap.get_from_request_token( oauth_request.get_parameter("oauth_token")) if not oauth_map: return oauth_error_response( OAuthError("Cannot find oauth mapping " "for request token.")) oauth_map.access_token = token.key_ oauth_map.access_token_secret = token.secret oauth_map.put() except OAuthError, e: return oauth_error_response(e)
def wrapper(*args, **kwargs): if is_valid_request(request): try: consumer, token, parameters = validate_token(request) if (not consumer) or (not token): return oauth_error_response(OAuthError( "Not valid consumer or token")) # If this API method requires an anointed consumer, # restrict any that haven't been manually approved. if require_anointed_consumer and not consumer.anointed: return oauth_error_response(OAuthError( "Consumer access denied.")) # Store the OAuthMap containing all auth info in the request # global for easy access during the rest of this request. flask.g.oauth_map = OAuthMap.get_from_access_token(token.key_) if not util.get_current_user_id(): # If our OAuth provider thinks you're logged in but the # identity providers we consume (Google/Facebook) # disagree, we act as if our token is no longer valid. return oauth_error_response(OAuthError( "Unable to get current user from oauth token")) except OAuthError, e: return oauth_error_response(e)
def wrapper(*args, **kwargs): if is_valid_request(request): try: consumer, token, parameters = validate_token(request) if consumer and token: # Store the OAuthMap containing all auth info in the request global # for easy access during the rest of this request. flask.g.oauth_map = OAuthMap.get_from_access_token( token.key_) # If this API method requires an anointed consumer, # restrict any that haven't been manually approved. if require_anointed_consumer and not consumer.anointed: flask.g.oauth_map = None if not util.get_current_user_id(): # If our OAuth provider thinks you're logged in but the # identity providers we consume (Google/Facebook) disagree, # we act as if our token is no longer valid. flask.g.oauth_map = None except OAuthError, e: # OAuthErrors are ignored, treated as user that's just not logged in pass
def current_oauth_map_from_session_unsafe(): # We have to use plain 'ole cookie handling before we switch over to a Flask-only # app, at which point we can strictly rely on Flask sessions. session_cookie_name = "session" session_cookie_value = cookie_util.get_cookie_value(session_cookie_name) if session_cookie_value and App.flask_secret_key: # Strip double quotes if session_cookie_value.startswith("\""): session_cookie_value = session_cookie_value[1:-1] # Fake little Flask request object to load up the Flask session cookie. fake_request = RequestMock(cookies={session_cookie_name: unicode(session_cookie_value)}) # Flask's sessions are secured by the secret key. session_cookie = Session.load_cookie(fake_request, session_cookie_name, secret_key=App.flask_secret_key) if session_cookie and session_cookie.has_key("oam"): oauth_map_id = session_cookie["oam"] oauth_map = OAuthMap.get_by_id_safe(oauth_map_id) if oauth_map: return oauth_map return None
def current_oauth_map_from_session_unsafe(): # We have to use plain 'ole cookie handling before we switch over to a Flask-only # app, at which point we can strictly rely on Flask sessions. session_cookie_name = "session" session_cookie_value = cookie_util.get_cookie_value(session_cookie_name) if session_cookie_value and App.flask_secret_key: # Strip double quotes if session_cookie_value.startswith("\""): session_cookie_value = session_cookie_value[1:-1] # Fake little Flask request object to load up the Flask session cookie. fake_request = RequestMock( cookies={session_cookie_name: unicode(session_cookie_value)}) # Flask's sessions are secured by the secret key. session_cookie = Session.load_cookie(fake_request, session_cookie_name, secret_key=App.flask_secret_key) if session_cookie and session_cookie.has_key("oam"): oauth_map_id = session_cookie["oam"] oauth_map = OAuthMap.get_by_id_safe(oauth_map_id) if oauth_map: return oauth_map return None
def access_token(): oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: return oauth_error_response(OAuthError('Invalid request parameters.')) try: # Create our access token token = oauth_server.fetch_access_token(oauth_request) if not token: return oauth_error_response(OAuthError("Cannot find corresponding " "access token.")) # Grab the mapping of access tokens to our identity providers oauth_map = OAuthMap.get_from_request_token( oauth_request.get_parameter("oauth_token")) if not oauth_map: return oauth_error_response(OAuthError("Cannot find oauth mapping " "for request token.")) oauth_map.access_token = token.key_ oauth_map.access_token_secret = token.secret oauth_map.put() except OAuthError, e: return oauth_error_response(e)
def authorize_token(): try: oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: raise OAuthError('Invalid request parameters.') # get the request token token = oauth_server.fetch_request_token(oauth_request) oauth_map = OAuthMap.get_from_request_token(token.key_) if not oauth_map: raise OAuthError("Unable to find oauth_map from request token " "during authorization.") # Get user from oauth map using either FB or Google access token user_data = oauth_map.get_user_data() if not user_data: raise OAuthError("User not logged in during authorize_token " "process.") # For now we don't require user intervention to authorize our tokens, # since the user already authorized FB/Google. If we need to do this # for security reasons later, there's no reason we can't. token = oauth_server.authorize_token(token, user_data.user) oauth_map.verifier = token.verifier oauth_map.put() return custom_scheme_redirect( oauth_map.callback_url_with_request_token_params( include_verifier=True)) except OAuthError, e: return oauth_error_response(e)
def request_token_callback(provider, oauth_map_id): oauth_map = OAuthMap.get_by_id_safe(oauth_map_id) if not oauth_map: return oauth_error_response(OAuthError("Unable to find OAuthMap by id during request token callback.")) if provider == "google": return google_request_token_handler(oauth_map) elif provider == "facebook": return facebook_request_token_handler(oauth_map)
def post(self): """POST submissions are for username/password based logins to acquire an OAuth access token. """ identifier = self.request_string('identifier') password = self.request_string('password') if not identifier or not password: self.render_login_page("Please enter your username and password.") return user_data = UserData.get_from_username_or_email(identifier.strip()) if not user_data or not user_data.validate_password(password): # TODO(benkomalo): IP-based throttling of failed logins? self.render_login_page("Your login or password is incorrect.") return # Successful login - convert to an OAuth access_token oauth_map_id = self.request_string("oauth_map_id", default="") oauth_map = OAuthMap.get_by_id_safe(oauth_map_id) if not oauth_map: self.render_login_page("Unable to find OAuthMap by id.") return # Mint the token and persist to the oauth_map oauth_map.khan_auth_token = AuthToken.for_user(user_data).value oauth_map.put() # Flush the "apply phase" of the above put() to ensure that subsequent # retrievals of this OAuthmap returns fresh data. GAE's HRD can # otherwise take a second or two to propagate the data, and the # following authorize endpoint redirect below could happen quicker # than that in some cases. oauth_map = OAuthMap.get(oauth_map.key()) # Need to redirect back to the http authorize endpoint return auth_util.authorize_token_redirect(oauth_map, force_http=True)
def google_token_callback(): oauth_map = OAuthMap.get_by_id_safe(request.values.get("oauth_map_id")) if not oauth_map: return oauth_error_response(OAuthError("Unable to find OAuthMap by id.")) if oauth_map.google_verification_code: return oauth_error_response(OAuthError("Request token already has google verification code.")) oauth_map.google_verification_code = request.values.get("oauth_verifier") try: oauth_map = retrieve_google_access_token(oauth_map) except OAuthError, e: return oauth_error_response(e)
def facebook_token_callback(): oauth_map = OAuthMap.get_by_id_safe(request.values.get("oauth_map_id")) if not oauth_map: return oauth_error_response(OAuthError("Unable to find OAuthMap by id.")) if oauth_map.facebook_authorization_code: return oauth_error_response(OAuthError("Request token already has facebook authorization code.")) oauth_map.facebook_authorization_code = request.values.get("code") try: oauth_map = retrieve_facebook_access_token(oauth_map) except OAuthError, e: return oauth_error_response(e)
def facebook_token_callback(): oauth_map = OAuthMap.get_by_id_safe(request.values.get("oauth_map_id")) if not oauth_map: return oauth_error_response(OAuthError( "Unable to find OAuthMap by id.")) if oauth_map.facebook_authorization_code: return oauth_error_response(OAuthError( "Request token already has facebook authorization code.")) oauth_map.facebook_authorization_code = request.values.get("code") try: oauth_map = retrieve_facebook_access_token(oauth_map) except OAuthBadRequestError, e: return pretty_error_response('Unable to log in with Facebook.')
def wrapper(*args, **kwargs): if is_valid_request(request): try: consumer, token, parameters = validate_token(request) if consumer and token: # Store the OAuthMap containing all auth info in the request global # for easy access during the rest of this request. flask.g.oauth_map = OAuthMap.get_from_access_token(token.key_) if not util.get_current_user_id(): # If our OAuth provider thinks you're logged in but the # identity providers we consume (Google/Facebook) disagree, # we act as if our token is no longer valid. flask.g.oauth_map = None except OAuthError, e: # OAuthErrors are ignored, treated as user that's just not logged in pass
def verify_and_cache_oauth_or_cookie(request): """ For a given request, try to oauth-verify or cookie-verify it. If the request has a valid oauth token, we store all the auth info in a per-request global (for easy access) and return. If the request does not have a valid oauth token, but has a valid http cookie *and* a valid xsrf token, return. Otherwise -- including the cases where there is an oauth token or cookie but they're not valid, raise an OAuthError of one form or another. This function is designed to be idempotent: it's safe (and fast) to call multiple times on the same request. It caches enough per-request information to avoid repeating expensive work. Arguments: request: A 'global' flask var holding the current active request. Raises: OAuthError: are not able to authenticate the current user. (Note we give OAuthError even when we're failing the cookie-based request, which is a bit of abuse of terminology since there's no oauth involved in that step.) """ if hasattr(flask.g, "oauth_map"): # Already called this routine and succeeded, so no need to call again. return # is_valid_request() verifies this request has something in it # that looks like an oauth token. if is_valid_request(request): consumer, token, parameters = validate_token(request) if (not consumer) or (not token): raise OAuthError("Not valid consumer or token") # Store the OAuthMap containing all auth info in the request # global for easy access during the rest of this request. # We do this now because current_req_has_auth_credentials() # accesses oauth_map. flask.g.oauth_map = OAuthMap.get_from_access_token(token.key_) if not util.current_req_has_auth_credentials(): # If our OAuth provider thinks you're logged in but the # identity providers we consume (Google/Facebook) # disagree, we act as if our token is no longer valid. del flask.g.oauth_map raise NotLoggedInError("verifying oauth token") # (We can do all the other global-setting after # current_req_has_auth_credentials.) # Store enough information from the consumer token that we can # do anointed checks. # TODO(csilvers): is it better to just store all of # 'consumer'? Seems too big given we just need to cache this # one piece of information right now. flask.g.is_anointed = consumer.anointed elif util.allow_cookie_based_auth(): # TODO(csilvers): simplify; this duplicates a lot of calls # (current_req_has_auth_credentials calls allow_cookie_based_auth too). # Would suffice to call util._get_current_user_id_from_cookies_unsafe() # and maybe auth_util.current_oauth_map_from_session_unsafe() as well. if not util.current_req_has_auth_credentials(): raise NotLoggedInError("verifying cookie values") else: raise NotLoggedInError("looking at oauth headers and cookies")
@route("/api/auth/request_token", methods=["GET", "POST"]) @decorators.manual_access_checking def request_token(): oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: return oauth_error_response(OAuthError('Invalid request parameters.')) try: # Create our request token token = oauth_server.fetch_request_token(oauth_request) except OAuthError, e: return oauth_error_response(e) if OAuthMap.get_from_request_token(token.key_): logging.error("OAuth key %s already used" % token.key_) params = dict([(key, request.get(key)) for key in request.arguments()]) logging.info("params: %r" % params) logging.info("Authorization: %s", request.headers.get('Authorization')) return oauth_error_response(OAuthError("OAuth parameters already used.")) # Start a new OAuth mapping oauth_map = OAuthMap() oauth_map.request_token_secret = token.secret oauth_map.request_token = token.key_ oauth_map.callback_url = requested_oauth_callback() if request.values.get("view") == "mobile": oauth_map.view = "mobile"
# hands off to Google/Facebook to gather the appropriate request tokens. @route("/api/auth/request_token", methods=["GET", "POST"]) def request_token(): oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: return oauth_error_response(OAuthError('Invalid request parameters.')) try: # Create our request token token = oauth_server.fetch_request_token(oauth_request) except OAuthError, e: return oauth_error_response(e) if OAuthMap.get_from_request_token(token.key_): return oauth_error_response(OAuthError("OAuth parameters already used.")) # Start a new OAuth mapping oauth_map = OAuthMap() oauth_map.request_token_secret = token.secret oauth_map.request_token = token.key_ oauth_map.callback_url = requested_oauth_callback() if request.values.get("view") == "mobile": oauth_map.view = "mobile" oauth_map.put() chooser_url = "/login/mobileoauth?oauth_map_id=%s&view=%s" % (oauth_map.key().id(), oauth_map.view)
@route("/api/auth/request_token", methods=["GET", "POST"]) @decorators.manual_access_checking def request_token(): oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: return oauth_error_response(OAuthError('Invalid request parameters.')) try: # Create our request token token = oauth_server.fetch_request_token(oauth_request) except OAuthError, e: return oauth_error_response(e) if OAuthMap.get_from_request_token(token.key_): logging.error("OAuth key %s already used" % token.key_) params = dict([(key, request.get(key)) for key in request.arguments()]) logging.info("params: %r" % params) logging.info("Authorization: %s", request.headers.get('Authorization')) return oauth_error_response(OAuthError("OAuth parameters already " "used.")) # Start a new OAuth mapping oauth_map = OAuthMap() oauth_map.request_token_secret = token.secret oauth_map.request_token = token.key_ oauth_map.callback_url = requested_oauth_callback() if request.values.get("view") == "mobile": oauth_map.view = "mobile"
def verify_and_cache_oauth_or_cookie(request): """ For a given request, try to oauth-verify or cookie-verify it. If the request has a valid oauth token, we store all the auth info in a per-request global (for easy access) and return. If the request does not have a valid oauth token, but has a valid http cookie *and* a valid xsrf token, return. Otherwise -- including the cases where there is an oauth token or cookie but they're not valid, raise an OAuthError of one form or another. This function is designed to be idempotent: it's safe (and fast) to call multiple times on the same request. It caches enough per-request information to avoid repeating expensive work. Arguments: request: A 'global' flask var holding the current active request. Raises: OAuthError: are not able to authenticate the current user. (Note we give OAuthError even when we're failing the cookie-based request, which is a bit of abuse of terminology since there's no oauth involved in that step.) """ if hasattr(flask.g, "oauth_map"): # Already called this routine and succeeded, so no need to call again. return # is_valid_request() verifies this request has something in it # that looks like an oauth token. if is_valid_request(request): consumer, token, parameters = validate_token(request) if (not consumer) or (not token): raise OAuthError("Not valid consumer or token") # Store the OAuthMap containing all auth info in the request # global for easy access during the rest of this request. # We do this now because current_req_has_auth_credentials() # accesses oauth_map. flask.g.oauth_map = OAuthMap.get_from_access_token(token.key_) if not util.current_req_has_auth_credentials(): # If our OAuth provider thinks you're logged in but the # identity providers we consume (Google/Facebook) # disagree, we act as if our token is no longer valid. del flask.g.oauth_map raise NotLoggedInError("verifying oauth token") # Child users cannot authenticate against our API via oauth unless the # oauth consumer is anointed by us. This stops children from approving # third party app data access. if not consumer.anointed and user_util.is_current_user_child(): del flask.g.oauth_map raise NotLoggedInError("under-13 accounts are denied API access") # (We can do all the other global-setting after # current_req_has_auth_credentials.) # Store enough information from the consumer token that we can # do anointed checks. # TODO(csilvers): is it better to just store all of # 'consumer'? Seems too big given we just need to cache this # one piece of information right now. flask.g.is_anointed = consumer.anointed elif util.allow_cookie_based_auth(): # TODO(csilvers): simplify; this duplicates a lot of calls # (current_req_has_auth_credentials calls allow_cookie_based_auth too). # Would suffice to call util._get_current_user_id_from_cookies_unsafe() # and maybe auth_util.current_oauth_map_from_session_unsafe() as well. if not util.current_req_has_auth_credentials(): raise NotLoggedInError("verifying cookie values") else: raise NotLoggedInError("looking at oauth headers and cookies")
@route("/api/auth/request_token", methods=["GET", "POST"]) @decorators.manual_access_checking def request_token(): oauth_server, oauth_request = initialize_server_request(request) if oauth_server is None: return oauth_error_response(OAuthError('Invalid request parameters.')) try: # Create our request token token = oauth_server.fetch_request_token(oauth_request) except OAuthError, e: return oauth_error_response(e) if OAuthMap.get_from_request_token(token.key_): logging.error("OAuth key %s already used" % token.key_) params = dict([(key, request.get(key)) for key in request.arguments()]) logging.info("params: %r" % params) logging.info("Authorization: %s", request.headers.get('Authorization')) return oauth_error_response( OAuthError("OAuth parameters already " "used.")) # Start a new OAuth mapping oauth_map = OAuthMap() oauth_map.request_token_secret = token.secret oauth_map.request_token = token.key_ oauth_map.callback_url = requested_oauth_callback() if request.values.get("view") == "mobile":