I know how to create tokens with this library, and also how to put tokens in reponse body:
access_token = create_access_token(identity = token_identity)
refresh_token = create_refresh_token(identity = token_identity)
set_access_cookies({"login": True}, access_token)
set_refresh_cookies({"login": True}, refresh_token)
However, when using it with my flask application, nothing is stored in my browser cookies.
Do I need to do something more than use the set_access_cookies or set_refresh_cookies in order to store tokens in cookies?
One example of code:
import logging
from flask import Blueprint, render_template, redirect, url_for, request, current_app as app, jsonify
from flask_login import login_user, logout_user, login_required
from werkzeug.security import generate_password_hash, check_password_hash
from .models import User
from . import db, login_manager, login_serializer, jwt
from flask_jwt_extended import (create_access_token,
create_refresh_token, jwt_required, jwt_refresh_token_required, get_jwt_identity, get_raw_jwt, set_access_cookies,
set_refresh_cookies, unset_jwt_cookies)
def set_response_cookies(token_identity, resp=None, token_types=["access", "refresh"]):
"""
Helper function to set cookies
"""
logging.warning("Setting cookies")
token_types.sort()
if token_types == ["access", "refresh"]:
access_token = create_access_token(identity = token_identity)
refresh_token = create_refresh_token(identity = token_identity)
if not resp:
resp = jsonify({"access_token": access_token, "refresh_token": refresh_token})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
elif token_types == ["access"]:
access_token = create_access_token(identity = token_identity)
if not resp:
resp = jsonify({"access_token": access_token})
set_access_cookies(resp, access_token)
return resp
elif token_types == ["refresh"]:
refresh_token = create_refresh_token(identity = token_identity)
if not resp:
resp = jsonify({"refresh_token": refresh_token})
set_refresh_cookies(resp, refresh_token)
return resp
else:
raise ValueError("Wrong Call to this function")
#auth.route('/signup', methods=['POST'])
def signup_post():
email = request.form.get('email')
name = request.form.get('name')
password = request.form.get('password')
user = User.objects(email=email)
if user: # Email already exist.
return redirect(url_for('auth.signup')), 409
logging.warning("User not existing")
new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256'))
new_user.save()
set_response_cookies(email, ["access", "refresh"])
return redirect(url_for('auth.login')), 200
You can make use of set_cookie()
Store token in cookie:
from flask import make_response
#app.route('/')
def index():
response = make_response(render_template(...))
response.set_cookie('access_token', 'YOUR_ACCESS_TOKEN')
response.set_cookie('refresh_token', 'YOUR_REFRESH_TOKEN')
return response
To Retrieve the token from cookie:
from flask import request
#app.route('/')
def index():
access_token = request.cookies.get('access_token')
In this way you can store the token in a cookie and retrive the token from the cookie.
Check that you have several Set-Cookie in your response header:
The HTTP header Set-Cookie is a response header and used to send cookies from the server to the user agent.
I can also suggest to check your app configurations. Here are my configurations related to jwt-extended:
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", 'local-secret')
JWT_TOKEN_LOCATION = ['cookies']
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(seconds=1800)
JWT_COOKIE_SECURE = False
JWT_REFRESH_TOKEN_EXPIRES = datetime.timedelta(days=15)
JWT_COOKIE_CSRF_PROTECT = True
JWT_ACCESS_CSRF_HEADER_NAME = "X-CSRF-TOKEN-ACCESS"
JWT_REFRESH_CSRF_HEADER_NAME = "X-CSRF-TOKEN-REFRESH"
The problem may be client-side instead of server-side. My app uses axios and in order to get it to work for me I had to add axios.defaults.withCredentials = true after importing it in the javascript file:
import axios from 'axios'
axios.defaults.withCredentials = true
Related
I have a login function to authenticate the users of my APIs using JWT and have set access and refresh cookies once the user has logged in as per the below code:
login code
#flask_app.route('/login', methods=['POST'])
def login():
email = request.json.get('email')
password = request.json.get('password', None).encode('UTF-8')
#print(email)
#print(password)
if not email:
return('Missing email', 400)
if not password:
return('Missing password', 400)
user = db.session.query(User).filter(User.email == email).first() or None
if user is None:
return('Wrong email or password!'), 400
hashed_password = bcrypt.checkpw(base64.b64encode(sha256(password).digest()), user.hashed_password)
if hashed_password:
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(identity=user.id)
permissions = get_user_permissions(user.id)
resp = jsonify({'email': user.email, 'permissions': permissions})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
When I login I can see the access and refresh cookies in the postman headers section so it is logging in the user correctly.
When I navigate to a route that requires a jwt token I get the error below and cannot see the access token cookie in Postman.
Missing cookie \"access_token_cookie\
jwt_required code snippet
#flask_app.route('/device-data/<unit_id>', methods=['GET'])
#jwt_required
def get_data(unit_id):
find_device = db.session.query(Unit).filter(Unit.id == unit_id).first()
jwt config
jwt = JWTManager(flask_app)
flask_app.config['JWT_CSRF_CHECK_FORM'] = True
flask_app.config['JWT_TOKEN_LOCATION'] = ['cookies']
flask_app.config['JWT_COOKIE_CSRF_PROTECT'] = True
flask_app.config['JWT_COOKIE_SECURE'] = True
flask_app.config['JWT_SECRET_KEY'] = 'Super Secret'
flask_app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=120)
flask_app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=1)
I am looking to get a simple login sequence on fastapi:
following This tutorial
from fastapi import FastAPI, Depends, HTTPException
from starlette.config import Config
from starlette.requests import Request
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, RedirectResponse
from authlib.integrations.starlette_client import OAuth, OAuthError
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="!secret")
config = Config(".env")
oauth = OAuth(config)
CONF_URL = "https://accounts.google.com/.well-known/openid-configuration"
oauth.register(
name="google",
server_metadata_url=CONF_URL,
client_kwargs={"scope": "openid email profile"},
)
#app.get("/")
async def home(request: Request):
user = request.session.get("user")
if user is not None:
email = user["email"]
html = (
f"<pre>Email: {email}</pre><br>"
'documentation<br>'
'logout'
)
return HTMLResponse(html)
return HTMLResponse('login')
#app.get("/login", tags=["authentication"])
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 = token.get("userinfo")
if user:
request.session["user"] = dict(user)
return RedirectResponse(url="/")
#app.get("/logout", tags=["authentication"])
async def logout(request: Request):
request.session.pop("user", None)
return RedirectResponse(url="/")
# Try to get the logged in user
async def get_user(request: Request) -> Optional[dict]:
user = request.session.get("user")
if user is not None:
return user
else:
raise HTTPException(status_code=403, detail="Could not validate credentials.")
return None
# Endpoint to protect
#app.get("/other_endpoint/")
async def other_endpoint_function(
user: Optional[dict] = Depends(get_user), id: str
):
# Chek if other is authenticated
if user.is_authenticated:
function()
else:
# First redirect to the login page
RedirectResponse("login")
# Once logged in re-run the initial request
RedirectResponse(f"/other_endpoint/{id}")
I am looking to protect only the other_endpoint the homepage has a link to the login page.
check that the user is authenticated before running the function
If the user is authenticated, run function
If the user is not authenticated
redirect to the login page
re-run the function after authentication
So far, I have tried multiple implementations with RedirectResponse, but i ends up bypassing the authentication
I have a flask app that works great on laptops/desktops but on mobile phone and small screens the login route doesn't work properly.
When I want to login to my app on a mobile phone the login page redirects to itself and nothing happen. I think this problem is somehow related to session or login decorated function. Sometimes it works suddenly but it doesn't response often.
What can I do now?
Here is my app heads and login route:
from cs50 import SQL
from math import ceil
from flask import Flask, flash, redirect, render_template, request, session
from flask_session import Session
from tempfile import mkdtemp
from werkzeug.security import check_password_hash, generate_password_hash
from helpers import intro_alert, main_alert, login_required, usd, ex_separator, separator, \
cmc_logo, exchange_rates, cmc_quote, cmc_listing, cmc_info, exchanges, exchange_pairs, cmc_projects
coinjito = Flask(__name__)
coinjito.config["TEMPLATES_AUTO_RELOAD"] = True
#coinjito.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
coinjito.config["SESSION_FILE_DIR"] = mkdtemp()
coinjito.config["SESSION_PERMANENT"] = False
coinjito.config["SESSION_TYPE"] = "filesystem"
Session(coinjito)
#coinjito.route("/login", methods=["GET", "POST"])
def login():
session.clear()
if request.method == "GET":
return render_template("login.html")
else:
rows = db.execute("SELECT * FROM users WHERE username=:username",
username=request.form.get("username"))
if len(rows) != 1 or not check_password_hash(rows[0]["password"],
request.form.get("password")):
return intro_alert("Apology", "alert-danger", "Login Failed",
"Invalid username and/or password.", "/login", "Go Back")
session["user_id"] = rows[0]["id"]
session["username"] = rows[0]["username"]
return redirect("/tracking")
#coinjito.route("/tracking")
#login_required
And here is my login helper function:
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if session.get("user_id") is None:
return redirect("/login")
return f(*args, **kwargs)
return decorated_function
With flask_jwt_extended, whenever I'm trying to send a POST request with the following decorators:
#jwt_refresh_token_required
#jwt_required
I am having this 401 error:
{
"msg": "Missing CSRF token"
}
When I use a GET instead, it's working fine.
I have read the documentation that talk about double submit protection, but that does not solve my problem. Any ideas how I could fix my issue?
The code to reproduce the problem is below.
Below is the structure of my code:
- src/__init.py__ # where I put all configs
- src/auth.py # where are the endpoints
init.py
login_serializer = URLSafeTimedSerializer(SERIALIZER_SECRET_KEY)
jwt = JWTManager()
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = SERIALIZER_SECRET_KEY
app.config['JWT_SECRET_KEY'] = JWT_SECRET_KEY
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
app.config['JWT_COOKIE_CSRF_PROTECT'] = True
db.init_app(app)
jwt.init_app(app)
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .routes import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
auth.py
import logging
from flask import Blueprint, request, current_app as app, jsonify
from werkzeug.security import generate_password_hash, check_password_hash
from . import login_serializer, jwt
from flask_jwt_extended import (jwt_required, jwt_refresh_token_required,
get_jwt_identity, get_raw_jwt, unset_jwt_cookies,
current_user, create_access_token, create_refresh_token, set_access_cookies, set_refresh_cookies)
auth = Blueprint('auth', __name__)
def set_response_cookies(token_identity, resp=None, token_types=["access", "refresh"]):
"""
Helper function to set cookies in response
"""
logging.warning("Setting cookies")
resp = jsonify(resp)
token_types.sort()
if token_types == ["access", "refresh"]:
access_token = create_access_token(identity = token_identity)
refresh_token = create_refresh_token(identity = token_identity)
if not resp:
resp = jsonify({"access_token": access_token, "refresh_token": refresh_token})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
elif token_types == ["access"]:
access_token = create_access_token(identity = token_identity)
if not resp:
resp = jsonify({"access_token": access_token})
set_access_cookies(resp, access_token)
return resp
elif token_types == ["refresh"]:
refresh_token = create_refresh_token(identity = token_identity)
if not resp:
resp = jsonify({"refresh_token": refresh_token})
set_refresh_cookies(resp, refresh_token)
return resp
else:
raise ValueError("Wrong Call to this function")
#jwt.user_claims_loader
def add_claims_to_access_token(identity):
"""
"""
return {
'email': identity
}
#jwt.user_loader_callback_loader
def user_loader_callback(identity):
"""
Ignore Here, but I use it to get a User object (not mentionned here) from a Token.
"""
return User.objects(
email=identity,
).first()
#auth.route('/token', methods=['POST'])
def token_post():
""" obj =
{"email": "email", "password": "password"} => Tokens
"""
obj = request.get_json()
resp = set_response_cookies(obj["email"], {"token": True}, ["access", "refresh"])
return resp, 200
#auth.route('/token/access', methods=['POST'])
#jwt_refresh_token_required
def refresh_access_cookies():
if current_user:
resp = set_response_cookies(current_user.email, {"token_refreshed": True}, ["access"])
return resp, 200
So, here, all I have to do to reproduce the error is:
Make a POST request to /token => In postman, my response will get all cookies and headers.
Make a POST request to /token/access => Give the error mentioned above.
On your configuration, you enabled JWT_COOKIE_CSRF_PROTECT to true.
For devep purpose, the error will be gone if you can set it to False which may not safe.
On production, You need to pass csrf_token on your request header.
I think this links can help you.
https://flask-jwt-extended.readthedocs.io/en/stable/tokens_in_cookies/ (see the last section)
https://flask-wtf.readthedocs.io/en/stable/csrf.html
I would like to return the token as a string to another python script. However I'm stuck at the callback page. I made the redirect_uri to "/callback" and it returns the authorization code in the URL as
www.my.website.com/callback?code={code}.
The Facebook OAuth 2 example at http://requests-oauthlib.readthedocs.org/en/latest/examples/facebook.html. I keep getting a 404 error by just pasting the redirect URI.
I would also like to request the following parameters: {id, message, created_time} but I'm not sure how.
import os
from requests_oauthlib import OAuth2Session
from flask import Flask, request, redirect, session, render_template, url_for
from requests_oauthlib.compliance_fixes import facebook_compliance_fix
app = Flask(__name__)
app.secret_key = os.urandom(24)
#app.route("/login")
def demo():
client_id = '1624399237811291'
client_secret = 'SECRET'
# OAuth endpoints given in the Facebook API documentation
authorization_base_url = 'https://www.facebook.com/dialog/oauth'
token_url = 'https://graph.facebook.com/oauth/access_token'
redirect_uri = 'http://www.bbg.mybluemix.net/callback' # Should match Site URL
facebook = OAuth2Session(client_id, redirect_uri=redirect_uri)
facebook = facebook_compliance_fix(facebook)
# Redirect user to Facebook for authorization
authorization_url, state = facebook.authorization_url(authorization_base_url)
authurl = '%s' % authorization_url
return render_template('welcome.html', authurl=authurl)
# Get the authorization verifier code from the callback url
#app.route("/callback", methods=["GET"])
def callback():
# Fetch the access token
redirect_response = ' '
token = facebook.fetch_token(token_url, client_secret=client_secret,
authorization_response=redirect_response)
return '%s' % token
# Fetch a protected resource, i.e. user profile
#r = facebook.get('https://graph.facebook.com/me?')
#return '%s' % r.content