AttributeError: 'OAuthRemoteApp' object has no attribute 'authorized_response' - python

I'm trying to authorize my application with twitter login authentication but after login into Twitter, It is not redirecting into my main page. It shows error:
SCREENSHOT
Here is my source code:
from flask import Flask
from flask import g, session, request, url_for, flash
from flask import redirect, render_template
from flask_oauth import OAuth
app = Flask(__name__)
app.debug = True
app.secret_key = 'development'
oauth = OAuth()
# Use Twitter as example remote application
twitter = oauth.remote_app('twitter',
base_url='https://api.twitter.com/1/',
request_token_url='https://api.twitter.com/oauth/request_token',
access_token_url='https://api.twitter.com/oauth/access_token',
authorize_url='https://api.twitter.com/oauth/authorize',
consumer_key='xxxxxxx',
consumer_secret='xxxxxxx'
)
#twitter.tokengetter
def get_twitter_token():
if 'twitter_oauth' in session:
resp = session['twitter_oauth']
return resp['oauth_token'], resp['oauth_token_secret']
#app.before_request
def before_request():
g.user = None
if 'twitter_oauth' in session:
g.user = session['twitter_oauth']
#app.route('/')
def index():
tweets = None
if g.user is not None:
resp = twitter.request('statuses/home_timeline.json')
if resp.status == 200:
tweets = resp.data
else:
flash('Unable to load tweets from Twitter.')
return render_template('index.html', tweets=tweets)
#app.route('/tweet', methods=['POST'])
def tweet():
if g.user is None:
return redirect(url_for('login', next=request.url))
status = request.form['tweet']
if not status:
return redirect(url_for('index'))
resp = twitter.post('statuses/update.json', data={
'status': status
})
if resp.status == 403:
flash('Your tweet was too long.')
elif resp.status == 401:
flash('Authorization error with Twitter.')
else:
flash('Successfully tweeted your tweet (ID: #%s)' % resp.data['id'])
return redirect(url_for('index'))
#app.route('/login')
def login():
callback_url = url_for('oauthorized', next=request.args.get('next'))
return twitter.authorize(callback=callback_url or request.referrer or None)
#app.route('/logout')
def logout():
session.pop('twitter_oauth', None)
return redirect(url_for('index'))
#app.route('/oauthorized')
def oauthorized():
resp = twitter.authorized_response()
if resp is None:
flash('You denied the request to sign in.')
else:
session['twitter_oauth'] = resp
return redirect(url_for('index'))
if __name__ == '__main__':
app.run()
PLEASE HELP ME...
ANY KIND OF HELP WOULD BE APPRECIATED!!

in your oauthorized function remove resp = twitter.authorized_response() statement and add a resp parameter to the function. it would be something like this:
#app.route('/oauthorized')
#twitter.authorized_response
def oauthorized(resp):
if resp is None:
flash('You denied the request to sign in.')
else:
session['twitter_oauth'] = resp
return redirect(url_for('index'))

Related

Token based authorization results in an Unauthorized 401

Package versioning
Flask 1.0.2
Flask-HTTPAuth 3.2.4
Flask-RESTful 0.3.8
itsdangerous 0.24
I'm working on a API project where a POST request to a Todo resource requires an user to have a token. Upon trying to test for this scenario, I'm getting the following assertion error: AssertionError: 401 != 201. Both BasicHTTPAuth and TokenHTTPAutth from flask-HTTPAuth are handling Authorization credentials.
Based on a User having a token to access this resource, I'm not clear on why I'm getting an Unauthorized error.
tests.py
class TestAuthenicatedUserPostTodo(ApiTestCase):
'''Verify that an API user successfully adds a Todo'''
def setUp(self):
super().setUp()
previous_todo_count = Todo.select().count()
user = User.get(User.id == 1)
token_serializer = Serializer(SECRET_KEY)
self.token = token_serializer.dumps({'id': user.id})
def test_todo_collection_post_todo_success(self):
with app.test_client() as client:
http_response = client.post(
"/api/v1/todos/",
headers={
'Authorization': f"Bearer {self.token}"
},
content_type="application/json",
data={
"name": "Must do a todo",
"user": 1
}
)
current_todo_count = Todo.select().count()
self.assertEqual(http_response.status_code, 201)
self.assertGreater(current_todo_count, previous_todo_count)
auth.py
basic_auth = HTTPBasicAuth()
token_auth = HTTPTokenAuth(scheme="Bearer")
auth = MultiAuth(token_auth, basic_auth)
#basic_auth.verify_password
def verify_password(username, password):
try:
api_user = User.get(User.username == username)
except User.DoesNotExist:
return False
user_verified = api_user.check_password(password)
if user_verified:
g.user = api_user
return True
return False
#token_auth.verify_token
def verify_token(token):
timed_serializer = Serializer(SECRET_KEY)
try:
user = timed_serializer.loads(token)
api_user = User.get_by_id(user['id'])
except (SignatureExpired, BadSignature) as e:
abort(400, description=str(e))
return True
todo.py
#auth.error_handler
def errorhandler():
return jsonify(unauthorized="Cannot add Todo. Login required."), 401
class TodoCollection(Resource):
#auth.login_required
def post(self):
import pdb; pdb.set_trace()
args = self.request_parser.parse_args()
if not args['name']:
return make_response(
{'invalid_request': "Invalid todo provided"}, 400
)
new_todo = Todo.create(**args)
return (
marshal(set_todo_creator(new_todo), todo_fields, 'new_todo'),
201, {'Location': f'{new_todo.location}'}
)

Flask-Login logout_user does not change is_authenticated

I have a test for register/login/logout endpoints and it fails when I register a user, logout and try to login as registered user. For some reason in endpoint current_user.is_authenticated is still True after logout.
Endpoints code:
from flask import Blueprint, Response, request, current_app
from flask_security.core import current_user
from flask_security.utils import logout_user, login_user, verify_password
from flask_api import status
from core.database.user_models import User, USER_DATASTORE
from utils.responses import SUCCESS, BAD_REQUEST, NOT_FOUND
ACCOUNT_BP = Blueprint("account", __name__)
EMAIL_IS_REGISTERED = Response("Email Is Registered", status=status.HTTP_401_UNAUTHORIZED)
USER_INACTIVE = Response("User Is Inactive", status=status.HTTP_403_FORBIDDEN)
WRONG_CREDENTIALS = Response("Wrong Credentials", status=status.HTTP_401_UNAUTHORIZED)
#ACCOUNT_BP.route("/register", methods=['POST'])
def register_endpoint() -> Response:
"""
# TODO: Fill this docstring.
"""
if current_user.is_authenticated:
return NOT_FOUND
if "email" in request.form and "password" in request.form:
if USER_DATASTORE.create_new_user(request.form["email"], request.form["password"]):
user = User.find_by_email(request.form["email"])
login_user(user, remember=True)
return SUCCESS
return EMAIL_IS_REGISTERED
return BAD_REQUEST
#ACCOUNT_BP.route("/signin", methods=['POST'])
def signin_endpoint() -> Response:
"""
# TODO: Fill this docstring.
"""
if current_user.is_authenticated: # IT SHOULD BE False
return NOT_FOUND
if "email" in request.form and "password" in request.form:
user = User.find_by_email(request.form["email"])
if user and verify_password(request.form["password"], user.password):
if user.active:
login_user(user, remember=True)
return SUCCESS
return USER_INACTIVE
return WRONG_CREDENTIALS
return BAD_REQUEST
#ACCOUNT_BP.route("/logout")
def logout_endpoint() -> Response:
if current_user.is_authenticated:
logout_user()
return SUCCESS
return NOT_FOUND
Code for test:
import unittest
from flask import Response
from flask.testing import FlaskClient
from flask_security.core import current_user
from main import SERVER
def register(client: FlaskClient, email: str, password: str) -> Response:
"""Fast method for using ``/account/register`` endpoint"""
form_data = 'email=' + email +'&password=' + password
return client.post('/account/register', data=form_data, content_type='application/x-www-form-urlencoded')
def signin(client: FlaskClient, email: str, password: str) -> Response:
"""Fast method for using ``/account/signin`` endpoint"""
form_data = 'email=' + email +'&password=' + password
return client.post('/account/signin', data=form_data, content_type='application/x-www-form-urlencoded')
def logout(client: FlaskClient) -> Response:
"""Fast method for using ``/account/logout`` endpoint"""
return client.get('/account/logout')
class UsersAccountTestCase(unittest.TestCase):
"""
# TODO: Fill this docstring.
"""
__REGISTER_SUCCESS_EMAIL = 'success#example.com'
__RANDOM_PASSWORD = 'RandomPassword'
def test_register_success(self):
"""
# TODO: Fill this docstring.
"""
with SERVER.test_client() as client:
register_result = register(client, self.__REGISTER_SUCCESS_EMAIL, self.__RANDOM_PASSWORD)
self.assertEqual(register_result.status_code, 200)
self.assertEqual(register_result.get_data(as_text=True), "Success")
self.assertTrue(current_user.is_authenticated)
self.assertEqual(current_user.email, self.__REGISTER_SUCCESS_EMAIL)
logout_result = logout(client)
self.assertEqual(logout_result.status_code, 200)
self.assertEqual(logout_result.get_data(as_text=True), "Success")
self.assertFalse(current_user.is_authenticated) # THIS PASSES!
check_result = signin(client, self.__REGISTER_SUCCESS_EMAIL, self.__RANDOM_PASSWORD)
self.assertEqual(check_result.status_code, 200) # THIS RETURNS 404
self.assertEqual(check_result.get_data(as_text=True), "Success")
self.assertTrue(current_user.is_authenticated)
self.assertEqual(current_user.email, self.__REGISTER_SUCCESS_EMAIL)
logout(client)
What can possibly lead to this behavior?
UPDATE:
Just tested endpoints with Postman - everything works as intended.
This is the strangest issue I have ever seen. I changed return SUCCESS in registration endpoint to something else and it just worked.

Python Token based authentication error

I am using token based authentication to restrict the access to user for my site, I am getting following error
{"_status": "ERR", "_error": {"message": "Please provide proper credentials", "code": 401}}weber#weber-desktop:/var/www/lunar-cloud-web-ui/kukunako$
my sample code shown below.
class TokenAuth(TokenAuth):
def check_auth(self, token, allowed_roles, resource, method):
accounts = app.data.driver.db['people']
return accounts.find_one({'token': token})
app = Eve(__name__,static_url_path='/static', auth = TokenAuth)
app.debug = True,
app.config.update(
DEBUG=True,
#EMAIL SETTINGS
MAIL_SERVER='smtp.gmail.com',
MAIL_PORT=465,
MAIL_USE_SSL=True,
MAIL_USERNAME = '<username>',
MAIL_PASSWORD = '<password>'
)
mail=Mail(app)
socketio = SocketIO(app)
def create_token(user):
payload = {
'sub': str(user['_id']),
'iat': datetime.now(),
'exp': datetime.now() + timedelta(days=14)
}
token = jwt.encode(payload, TOKEN_SECRET)
return token.decode('unicode_escape')
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if not request.headers.get('Authorization'):
response = jsonify(error='Missing authorization header')
response.status_code = 401
return response
payload = parse_token(request)
if datetime.fromtimestamp(payload['exp']) < datetime.now():
response = jsonify(error='Token has expired')
response.status_code = 401
return response
g.user_id = payload['sub']
return f(*args, **kwargs)
return decorated_function
#app.route('/auth/login', methods=['POST'])
def login():
accounts = app.data.driver.db['people']
user = accounts.find_one({'email': request.json['email']})
if not user:
response = jsonify(error='Your email does not exist')
response.status_code = 401
return response
if not user['email_confirmed'] == True:
response = jsonify(error='Email is not confirmed')
response.status_code = 401
return response
if not user or not check_password_hash(user['password']['password'], request.json['password']):
response = jsonify(error='Wrong Email or Password')
response.status_code = 401
return response
token = create_token(user)
return jsonify(token=token)
my all code is show in following for settings file and server code file
settings file
server code file
How are you testing it?
I can think of two possible problems.
JWT token needs to be base64 encoded
You may have forgotten : at the end
e.g. If your token is as follows (Taken from jwt.io site)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
You need to do the following:
$ echo 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ:' | base64
ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnpkV0lpT2lJeE1qTTBOVFkzT0Rrd0lpd2libUZ0WlNJNklrcHZhRzRnUkc5bElpd2lZV1J0YVc0aU9uUnlkV1Y5LlRKVkE5NU9yTTdFMmNCYWIzMFJNSHJIRGNFZnhqb1laZ2VGT05GaDdIZ1E6Cg==
Now use this as follows (with curl)
curl -H "Authorization Basic ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SnpkV0lpT2lJeE1qTTBOVFkzT0Rrd0lpd2libUZ0WlNJNklrcHZhRzRnUkc5bElpd2lZV1J0YVc0aU9uUnlkV1Y5LlRKVkE5NU9yTTdFMmNCYWIzMFJNSHJIRGNFZnhqb1laZ2VGT05GaDdIZ1E6Cg==" http://127.0.0.1:5000/my_secure_endpoint

Flask-OAuthlib OAuth2 client Error: "Missing access credentials."

I'm using Flask OauthLib following this tutorial, trying to make a basic OAuth2 client to use with Foursquare.com: https://flask-oauthlib.readthedocs.org/en/latest/client.html#oauth2-client
After I grant permission to use the app, I get redirected to a page with this text:
{
"meta": {
"code": 400,
"errorDetail": "Missing access credentials. See https://developer.foursquare.com/docs/oauth.html for details.",
"errorType": "invalid_auth"
},
"response": {}
}
What is wrong? How do I fix this? Thanks.
Foursquare app settings:
Redirect URI(s):
https://127.0.0.1:5000/login/authorized
github.py (A slightly modified version of https://github.com/lepture/flask-oauthlib/blob/master/example/github.py )
from flask import Flask, redirect, url_for, session, request, jsonify
from flask_oauthlib.client import OAuth
app = Flask(__name__)
app.debug = True
app.secret_key = 'development'
oauth = OAuth()
foursquare = oauth.remote_app(
'foursquare',
app_key='FOURSQUARE',
consumer_key='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
consumer_secret='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
)
app.config['FOURSQUARE'] = dict(
consumer_key='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
consumer_secret='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
base_url='https://api.foursquare.com/',
request_token_url=None,
access_token_url='https://foursquare.com/oauth2/access_token',
authorize_url='https://foursquare.com/oauth2/authenticate',
)
oauth.init_app(app)
#app.route('/')
def index():
if 'foursquare_token' in session:
me = foursquare.get('v2/users/self')
return jsonify(me.data)
return redirect(url_for('login'))
#app.route('/login')
def login():
return foursquare.authorize(callback=url_for('authorized', _external=True))
#app.route('/logout')
def logout():
session.pop('foursquare_token', None)
return redirect(url_for('index'))
#app.route('/login/authorized')
def authorized():
resp = foursquare.authorized_response()
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error'],
request.args['error_description']
)
session['foursquare_token'] = (resp['access_token'], '')
me = foursquare.get('v2/users/self')
return jsonify(me.data)
#foursquare.tokengetter
def get_foursquare_oauth_token():
return session.get('foursquare_token')
if __name__ == '__main__':
app.run('127.0.0.1', debug=True, port=5000, ssl_context=('/Users/XXXXX/Development/Certificates/server.crt', '/Users/XXXXX/Development/Certificates/server.key'))
You need to define a scope too: for example:
request_token_params={
'scope': [
'https://mail.google.com/',
'https://www.googleapis.com/auth/admin.directory.user.readonly',
'https://www.googleapis.com/auth/admin.directory.orgunit.readonly',
'https://www.googleapis.com/auth/admin.directory.group.readonly',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.profile.emails.read',
]

flask http-auth and unittesting

Hi!
I have a route that I have protected using HTTP Basic authentication, which is implemented by Flask-HTTPAuth. Everything works fine (i can access the route) if i use curl, but when unit testing, the route can't be accessed, even though i provide it with the right username and password.
Here are the relevant code snippets in my testing module:
class TestClient(object):
def __init__(self, app):
self.client = app.test_client()
def send(self, url, method, data=None, headers={}):
if data:
data = json.dumps(data)
rv = method(url, data=data, headers=headers)
return rv, json.loads(rv.data.decode('utf-8'))
def delete(self, url, headers={}):
return self.send(url, self.client.delete, headers)
class TestCase(unittest.TestCase):
def setUp(self):
app.config.from_object('test_config')
self.app = app
self.app_context = self.app.app_context()
self.app_context.push()
db.create_all()
self.client = TestClient(self.app)
def test_delete_user(self):
# create new user
data = {'username': 'john', 'password': 'doe'}
self.client.post('/users', data=data)
# delete previously created user
headers = {}
headers['Authorization'] = 'Basic ' + b64encode((data['username'] + ':' + data['password'])
.encode('utf-8')).decode('utf-8')
headers['Content-Type'] = 'application/json'
headers['Accept'] = 'application/json'
rv, json = self.client.delete('/users', headers=headers)
self.assertTrue(rv.status_code == 200) # Returns 401 instead
Here are the callback methods required by Flask-HTTPAuth:
auth = HTTPBasicAuth()
#auth.verify_password
def verify_password(username, password):
# THIS METHOD NEVER GETS CALLED
user = User.query.filter_by(username=username).first()
if not user or not user.verify_password(password):
return False
g.user = user
return True
#auth.error_handler
def unauthorized():
response = jsonify({'status': 401, 'error': 'unauthorized', 'message': 'Please authenticate to access this API.'})
response.status_code = 401
return response
Any my route:
#app.route('/users', methods=['DELETE'])
#auth.login_required
def delete_user():
db.session.delete(g.user)
db.session.commit()
return jsonify({})
The unit test throws the following exception:
Traceback (most recent call last):
File "test_api.py", line 89, in test_delete_user
self.assertTrue(rv.status_code == 200) # Returns 401 instead
AssertionError: False is not true
I want to emphazise once more that everything works fine when i run curl with exactly the same arguments i provide for my test client, but when i run the test, verify_password method doesn't even get called.
Thank you very much for your help!
Here is an example how this could be done with pytest and the inbuilt monkeypatch fixture.
If I have this API function in some_flask_app:
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
auth = HTTPBasicAuth()
#app.route('/api/v1/version')
#auth.login_required
def api_get_version():
return jsonify({'version': get_version()})
I can create a fixture that returns a flask test client and patches the authenticate function in HTTPBasicAuth to always return True:
import pytest
from some_flask_app import app, auth
#pytest.fixture(name='client')
def initialize_authorized_test_client(monkeypatch):
app.testing = True
client = app.test_client()
monkeypatch.setattr(auth, 'authenticate', lambda x, y: True)
yield client
app.testing = False
def test_settings_tracking(client):
r = client.get("/api/v1/version")
assert r.status_code == 200
You are going to love this.
Your send method:
def send(self, url, method, data=None, headers={}):
pass
Your delete method:
def delete(self, url, headers={}):
return self.send(url, self.client.delete, headers)
Note you are passing headers as third positional argument, so it's going as data into send().

Categories