In my main.py, I have the following code-
app = FastAPI(docs_url="",)
app.add_middleware(SessionMiddleware, secret_key=os.getenv('SECRET'))
config = Config('.env')
oauth = OAuth(config)
CONF_URL = 'http://localhost:9090/.well-known/openid-configuration'
oauth.register(
name='google',
server_metadata_url=CONF_URL,
client_id=os.getenv('ID'),
client_secret=os.getenv('SECRET'),
client_kwargs={
'scope': 'openid email profile'
}
)
api_url = None
#app.get('/')
async def homepage(request: Request):
user = request.session.get('user')
if user:
data = json.dumps(user)
html = (
f'<pre>{data}</pre>'
'logout'
)
return HTMLResponse(html)
return HTMLResponse('login')
#app.get('/login')
async def login(request: Request):
redirect_uri = request.url_for('auth')
return await oauth.google.authorize_redirect(request, redirect_uri)
#app.get('/auth')
async def auth(request: Request):
try:
token = await oauth.google.authorize_access_token(request)
except OAuthError as error:
return HTMLResponse(f'<h1>{error.error}</h1>')
user = await oauth.google.parse_id_token(request, token)
request.session['user'] = dict(user)
if api_url:
return RedirectResponse(url=api_url)
return RedirectResponse(url='/')
#app.get('/logout')
async def logout(request: Request):
request.session.pop('user', None)
request.cookies.clear()
return RedirectResponse(url='/')
#app.get("/api")
async def get_swagger_ui(request: Request):
global api_url
api_url = request.url
user = request.session.get('user')
if user:
return get_swagger_ui_html(
openapi_url="/openapi.json", title="Webapp",)
else:
return RedirectResponse(
url="/login"
)
# routes
PROTECTED = [Depends(login)]
app.include_router(get_api.router, dependencies=PROTECTED)
In the get_api.py file, I have the following conf -
router = APIRouter()
#router.get("/api/higs", tags=["higs"])
def get_higs(db: Session = Depends(get_db),
)
try:
<something>
return x
except Exception as err:
raise HTTPException(
status_code=400, detail="Invalid parameter : {}".format(err),
)
There are similar other endpoints in the get_api.py file. I wanted to block access to these endpoints without authentication. So in the app.include_router method, I added dependencies. But its not working. I am able to access the endpoint data. For e.g. localhost:8000/api/higs - displays all the data in text that I would get from calling executing the GET endpoint in swagger UI. How can I fix this issue. Thanks.
I have the following app.py:
from flask import Flask
from waitress import serve
from bprint import api_blueprint
from errors import invalid_id, not_found, invalid_input, internal_server_error, unauthorized_access
app = Flask(__name__)
app.register_blueprint(api_blueprint)
app.register_error_handler(400, invalid_id)
app.register_error_handler(401, unauthorized_access)
app.register_error_handler(404, not_found)
app.register_error_handler(405, invalid_input)
app.register_error_handler(500, internal_server_error)
if __name__ == "__main__":
serve(app, host='localhost')
And the following code in bprint.py:
from flask import Blueprint, jsonify, request
import dbu
from models import Session, user_table, car_table, order_table
from schema import UserDetails, UserQuery, OrderDetails, OrderQuery, CarDetails, CarQuery, LoginData, \
ListUsersReq, Response
from contextlib import contextmanager
from flask_jwt_extended import jwt_required, create_access_token, get_jwt_identity
import datetime
api_blueprint = Blueprint('api', __name__)
#contextmanager
def session_scope():
session = Session()
try:
yield session
session.commit()
except:
session.rollback()
raise
else:
try:
session.commit()
except:
session.rollback()
raise
#api_blueprint.route("/login", methods=["POST"])
def login():
from app import bcrypt
data = LoginData().load(request.json)
if data:
user = dbu.get_entry_by_username(user_table, username=data["username"])
hpw = bcrypt.generate_password_hash(data["password"])
if not user:
return jsonify({"message": "Couldn't find user!"})
if bcrypt.check_password_hash(hpw, data["password"]):
access_token = create_access_token(identity=data["username"], expires_delta=datetime.timedelta(days=365))
return jsonify(access_token=access_token, id=user.id), 200
#api_blueprint.route("/user", methods=["GET"])
def list_users():
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
args = ListUsersReq().load(request.args)
userlist = dbu.list_users(args.get("email"), args.get("username"))
return jsonify(UserDetails(many=True).dump(userlist))
else:
return jsonify(code=401, type='UNAUTHORIZED_ACCESS'), 401
#api_blueprint.route("/user", methods=["POST"])
def create_user():
with session_scope():
from app import bcrypt
user_details = UserQuery().load(request.get_json(force=True))
user_details["password"] = bcrypt.generate_password_hash(user_details["password"]).decode('UTF-8')
user = dbu.create_entry(user_table, **user_details)
access_token = create_access_token(identity=user.username, expires_delta=datetime.timedelta(days=365))
return jsonify(access_token=access_token, id=UserDetails().dump(user)["id"]), 200
#api_blueprint.route("/user/<int:id>", methods=["GET"])
def user_by_id(id):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
user = dbu.get_entry_by_id(user_table, id)
return jsonify(UserDetails().dump(user))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/user/<int:id>", methods=["PUT"])
def update_user(id):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin or user.id == id:
user_details = UserQuery().load(request.json)
user = dbu.get_entry_by_id(user_table, id)
dbu.update_entry(user, **user_details)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/user/<int:id>", methods=["DELETE"])
def delete_user(id):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin or user.id == id:
dbu.delete_entry(user_table, id)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars", methods=["GET"])
def get_inventory():
with session_scope():
cars = dbu.list_cars()
return jsonify(CarDetails(many=True).dump(cars))
#api_blueprint.route("/cars/car/<int:carId>", methods=["GET"])
def get_car_by_id(carId):
with session_scope():
car = dbu.get_car_by_id(car_table, carId)
return jsonify(CarDetails().dump(car))
#api_blueprint.route("/cars/car", methods=["POST"])
def create_car():
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
car_details = CarQuery().load(request.json)
car = dbu.create_entry(car_table, **car_details)
return jsonify({"carId": CarDetails().dump(car)["carId"]})
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>", methods=["PUT"])
def update_car(carId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
car_details = CarQuery().load(request.json)
car = dbu.get_car_by_id(car_table, carId)
dbu.update_entry(car, **car_details)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>", methods=["DELETE"])
def delete_car(carId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
dbu.delete_car(car_table, carId)
return jsonify(Response().dump({"code": "200"}))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>/order", methods=["POST"])
def place_order(carId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user:
order_data = OrderQuery().load(request.json)
order = dbu.create_entry(order_table,
userId=user.id,
carId=carId,
shipDate=order_data["shipDate"],
returnDate=order_data["returnDate"],
status="placed",
complete=False)
return jsonify({"id": OrderDetails().dump(order)["id"]})
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/orders", methods=["GET"])
def get_orders():
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user.admin:
orders = dbu.list_orders()
return jsonify(OrderDetails(many=True).dump(orders))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>/order/<int:orderId>", methods=["GET"])
def get_order_by_id(carId, orderId):
with session_scope():
current_user = get_jwt_identity()
user = dbu.get_entry_by_username(user_table, current_user)
if user:
order = dbu.get_entry_by_id(order_table, id=orderId)
return jsonify(OrderDetails().dump(order))
else:
return jsonify(code=401, type="UNAUTHORIZED_ACCESS"), 401
#api_blueprint.route("/cars/car/<int:carId>/order/<int:orderId>", methods=["DELETE"])
def delete_order(carId, orderId):
with session_scope():
dbu.delete_entry(order_table, id=orderId)
return jsonify(Response().dump({"code": "200"}))
When I try to run waitress-serve --port=5000 app:app I get the following error:
app.register_blueprint(api_blueprint)
raise AssertionError(
AssertionError: View function mapping is overwriting an existing endpoint function: api.wrapper
What may be the problem?
I'm almost sure it worked in December and now after reinstalling my Windows it doesn't
Now it also says I have too much code in my question and I don't know how to explain my problem with more words, so I have to add a few useless lines, sorry
Check if you have some custom decorators on views. Because flask take endpoint name either from #route parameter or from function name. In your case there're no endpoint parameter in any of functions.
Error says api.wrapper and it means that you have 2 or more function with name wrapper. Usually we see such name inside decorators. So you probably have decorator that looks like
def decorator(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
And some views are decorated with such decorator. And flask take "wrapper" as a view name
Try by commenting few lines of code - It may help you to resolve your issue.
from flask import Flask
from waitress import serve
from bprint import api_blueprint
# from errors import invalid_id, not_found, invalid_input, internal_server_error, unauthorized_access
app = Flask(__name__)
app.register_blueprint(api_blueprint)
# app.register_error_handler(400, invalid_id)
# app.register_error_handler(401, unauthorized_access)
# app.register_error_handler(404, not_found)
# app.register_error_handler(405, invalid_input)
# app.register_error_handler(500, internal_server_error)
if __name__ == "__main__":
serve(app, host='localhost')
Given both FACEBOOK_APP_ID and FACEBOOK_APP_SECRET, what sound I change in the following code (or even on the FB control panel) so as to be able to read email, public_profile and user_friends of a user (me)?
from flask import Flask, redirect, url_for, session, request
from flask_oauth import OAuth
SECRET_KEY = ''
DEBUG = True
FACEBOOK_APP_ID = ''
FACEBOOK_APP_SECRET = ''
app = Flask(__name__)
app.debug = DEBUG
app.secret_key = SECRET_KEY
oauth = OAuth()
facebook = oauth.remote_app('facebook',
base_url='https://graph.facebook.com/',
request_token_url=None,
access_token_url='/oauth/access_token',
authorize_url='https://www.facebook.com/dialog/oauth',
consumer_key=FACEBOOK_APP_ID,
consumer_secret=FACEBOOK_APP_SECRET,
request_token_params={'scope': ["email", "public_profile", "user_friends"]}
)
#app.route('/')
def index():
return redirect(url_for('login'))
#app.route('/login')
def login():
return facebook.authorize(callback=url_for('facebook_authorized',
next=request.args.get('next') or request.referrer or None,
_external=True))
#app.route('/login/authorized')
#facebook.authorized_handler
def facebook_authorized(resp):
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error_reason'],
request.args['error_description']
)
session['oauth_token'] = (resp['access_token'], '')
me = facebook.get('/me')
return 'type %s, data %s, headers %s, raw_data %s, status %s' % (type(me), str(me.data), str(me.headers), str(me.raw_data), str(me.status))
#facebook.tokengetter
def get_facebook_oauth_token():
return session.get('oauth_token')
if __name__ == '__main__':
app.run()
EDIT:
request_token_params={'scope': 'public_profile,user_friends,email'}
me = facebook.get('/me?fields=id,name,first_name,last_name,age_range,link,gender,locale,timezone,updated_time,verified,friends,email')
If you're using v2.4 of the Graph API, you'll need to specify all the fields you want returned at
me = facebook.get('/me?fields=id,name,gender,email,friends')
etc. It's all in the docs.
https://developers.facebook.com/docs/apps/changelog#v2_4
https://developers.facebook.com/docs/graph-api/using-graph-api/v2.4#fields
I am using flask-oauth to authenticate my users.
I did a bit of digging and I found out that this is the URL that flask-oauth is sending:
https://www.facebook.com/dialog/oauth?response_type=code&client_id=1000000000000&redirect_uri=http%3A%2F%2Fwww.resoorce.com%2Flogin%2Fauthorized&scope=email
It clearly has a redirect_uri parameter!
This is the response that facebook is sending back:
{"error":{"message":"Missing redirect_uri parameter.","type":"OAuthException","code":191}}
If it helps this is my whole code:
from flask import Flask, redirect, url_for, session, request
from flask_oauthlib.client import OAuth, OAuthException
'''fake ones'''
FACEBOOK_APP_ID = '123121321212312313'
FACEBOOK_APP_SECRET = 'dfg12sdf1g2s1dfg31sd3fg'
app = Flask(__name__)
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)
facebook = oauth.remote_app(
'facebook',
consumer_key=FACEBOOK_APP_ID,
consumer_secret=FACEBOOK_APP_SECRET,
request_token_params={'scope': 'email'},
base_url='https://graph.facebook.com',
request_token_url=None,
access_token_url='/oauth/access_token',
authorize_url='https://www.facebook.com/dialog/oauth'
)
#app.route('/')
def index():
return redirect(url_for('login'))
#app.route('/login')
def login():
callback = url_for(
'facebook_authorized',
next=request.args.get('next') or request.referrer or None,
_external=True
)
return facebook.authorize(callback=callback)
#app.route('/login/authorized')
#facebook.authorized_handler
def facebook_authorized(resp):
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error_reason'],
request.args['error_description']
)
if isinstance(resp, OAuthException):
return 'Access denied: %s' % resp.message
session['oauth_token'] = (resp['access_token'], '')
me = facebook.get('/me')
return 'Logged in as id=%s name=%s redirect=%s' % \
(me.data['id'], me.data['name'], request.args.get('next'))
#facebook.tokengetter
def get_facebook_oauth_token():
return session.get('oauth_token')
ain__':
app.run()
if __name__ == '__main__':
app.run()
Here is my route:
#blueprint.before_request
def load_session_from_cookie():
if request.endpoint != 'client.me':
try:
cookie = request.cookies.get(settings.COOKIE_NAME, None)
# if cookie does not exist, redirect to login url
if not cookie:
session.pop('accountId', None)
return redirect(settings.LOGIN_URL)
account = check_sso_cookie(cookie)
if 'accountId' in session:
return
elif 'accountId' in account:
session['accountId'] = account.get('accountId')
return
else:
session.pop('accountId', None)
return redirect(settings.LOGIN_URL)
except BadSignature:
session.pop('accountId', None)
return redirect(settings.LOGIN_URL)
#blueprint.route('/')
def home():
session.permanent = True
return render_template('index.html')
Here is my test:
from flask import Flask
from flask.ext.testing import TestCase
from api.client import blueprint
class TestInitViews(TestCase):
render_templates = False
def create_app(self):
app = Flask(__name__)
app.config['TESTING'] = True
app.config['SECRET_KEY'] = 'sekrit!'
app.register_blueprint(blueprint)
return app
def setUp(self):
self.app = self.create_app()
self.cookie = '{ "account_id": 100 }'
def test_root_route(self):
resp = self.client.get("/")
self.assert_template_used('index.html')
def test_root_route_404(self):
res = self.client.get('/foo')
self.assertEqual(res.status_code, 404)
The problem is that the test test_root_route fails because a redirect happens because the session doesn't exist. I can't find any good resource online that shows how to incorporate session management with Flask-Tests... anyone have a good way of doing this?
You can make login request before:
def create_app(self):
...
app.config['TESTING'] = True # should off csrf
...
def test_root_route(self):
self.client.post(settings.LOGIN_URL, data={'login': 'l', 'password': 'p'})
resp = self.client.get('/')
self.assert_template_used('index.html')
For some difficult cases login route can be mocked.
Or manually set cookie:
def test_root_route(self):
resp = self.client.get('/', headers={'Cookie': 'accountId=test'})
self.assert_template_used('index.html')
Or set session with session transaction (http://flask.pocoo.org/docs/testing/#accessing-and-modifying-sessions):
def test_root_route(self):
with self.client.session_transaction() as session:
session['accountId'] = 'test'
resp = self.client.get('/')
self.assert_template_used('index.html')