flask - preserve response header - flask

This is how my login method looks like:
def login():
if request.method == "GET":
return render_template('user/login.html')
else:
jwt_token = "xxxx"
resp = redirect(url_for('home.index'))
resp.headers.set('Authorization', "JWT {}".format(jwt_token))
return resp
This works fine but the Authorization header does not make it to home.index page.
How do I tell flask to preserve this header on every request?
------Edit---------
This works if I add Token to Cookie as resp.set_cookie('Authorization', "JWT {}".format(json_data["access_token"])) but I would like to keep it in the Authorization Header.

If you want to set Authorization header on all requests, you could do something like this:
#app.after_request
def after_request(response):
my_jwt_token = 'xxxx'
response.headers['Authorization'] = my_jwt_token
return response
More information on documentation: http://flask.pocoo.org/docs/1.0/api/#flask.Flask.after_request

Related

PyTest: How to add X-BULK-OPERATION with HTTP_AUTHORIZATION for session credentials (headers)

I'm writing test for Django Rest Framework with PyTest. There was a problem with headers X-BULK-OPERATION because I can't add X-BULK-OPERATION with HTTP_AUTHORIZATION on header.
my code
#pytest.fixture(scope="module")
def session(django_db_setup, django_db_blocker):
with django_db_blocker.unblock():
user = User.objects.get(email=TEST_EMAIL)
user.set_password(TEST_EMAIL)
user.save(update_fields=["password"])
client = APIClient()
url = reverse("login_sys:login")
response = client.post(url, {
"email": TEST_EMAIL,
"password": TEST_EMAIL
})
client.credentials(**{"X-CSRFToken": response.cookies['csrftoken'].value})
yield client
client.logout()
#pytest.fixture(scope='module')
def choose_crm(session):
crm = Crm.objects.get()
url = reverse("crm:set-jwt", kwargs={"pk": crm.id})
response = session.get(url)
session.credentials(HTTP_AUTHORIZATION=response.data.get("token", None),
**{"X-BULK-OPERATION": True})
yield session
session.logout()
error
{'detail': "Header 'X-BULK-OPERATION' should be provided for bulk operation."}
Debug:print request, as you can see I have Bulk-Operation in header but still gives the error no BULK-OPERATION in header
'HTTP_AUTHORIZATION': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcm1fdXNlciI6MSwiY3JtX2lkIjoxLCJncm91cF9pZCI6MSwiZGVhbF9wZXJtIjp7ImNyZWF0ZSI6MSwiZGlzcGxheSI6MSwiZWRpdCI6MSwiZGVsZXRlIjoxLCJleHBvcnQiOjF9LCJjb250YWN0X3Blcm0iOnsiY3JlYXRlIjoxLCJkaXNwbGF5IjoxLCJlZGl0IjoxLCJkZWxldGUiOjEsImV4cG9ydCI6MX0sImxlYWRfcGVybSI6eyJjcmVhdGUiOjEsImRpc3BsYXkiOjEsImVkaXQiOjEsImRlbGV0ZSI6MSwiZXhwb3J0IjoxfSwidGFza19wZXJtIjp7ImNyZWF0ZSI6MSwiZGlzcGxheSI6MSwiZWRpdCI6MSwiZGVsZXRlIjoxLCJleHBvcnQiOjF9fQ.Wrd89a8jpRnNu1XhNsOKFAkNQR6v_Y_X0nKySocMtLM', 'HTTP_X-BULK-OPERATION': True}

Why HttpResponseRedirect.set_cookie is not working when i use in django project?

When I use Google OAuth to verify my user, After verify is passed, I want to redirect to the page which user visit before authority, So I want to save the page path to user's cookie, so I implementation like this:
def get_login_resp(request, redirect):
print(redirect)
auth_url = "https://accounts.google.com/o/oauth2/auth?" + urlencode({
"client_id": GOOGLE_CLIENT_ID,
"response_type": "code",
"redirect_uri": make_redirect_url(request, redirect),
"scope": "profile email",
"max_auth_age": 0
})
resp = HttpResponseRedirect(auth_url)
max_age = 3600 * 24
expires = datetime.strftime(datetime.utcnow() + timedelta(seconds=max_age), "%a, %d-%b-%Y %H:%M:%S GMT")
print(expires)
resp.set_cookie('google_auth_redirect', redirect, max_age=max_age, expires=expires,
domain=LOGIN_COOKIE_DOMAIN, secure=True, httponly=True)
print(resp._headers)
print(resp.cookies)
return resp
ps: redirect is the page path which I want to save
But when request the login url with Postman, I can only see this headers:
response headers
And these cookies:
Cookies
So how can i do with this problem? There is not any error info for me.
Try every methods to find out what's wrong, But still failed.
So I try to run server on an other machine(a Linux server), it works!!!
BTW: My develop PC is Macbook Pro 15-inch, 2017 with macOS High Sierra 10.13.1
Update at 14/Jan/2020:
Didn't find the root cause, but I solved this issue by saving redirect_url to session data, in this solution you should check auth valid by using another request, then call google auth to reauth again, code like below:
class GoogleAuthView(RedirectView):
# google auth view
def get(self, request, *args, **kwargs):
# get redirect url from url params, frontend code should pass the param in request url
redirect_url = request.GET.get('redirect_url', None)
if redirect_url:
redirect_url = parse.unquote(redirect_url)
credentials = request.session.get("credentials", None)
if (not credentials) or ('expire_time' not in credentials) or (credentials['expire_time'] < time.time()):
request.session['redirect_url'] = redirect_url # if need google auth, save redirect url to session first
else:
if redirect_url:
return HttpResponseRedirect(redirect_url)
flow = google_auth_oauthlib.flow.Flow.from_client_config(
client_config=settings.GOOGLE_AUTH_CONFIG,
scopes=settings.GOOGLE_AUTH_SCOPES
)
flow.redirect_uri = settings.GOOGLE_AUTH_CONFIG['web']['redirect_uris'][0]
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true'
)
request.session['state'] = state
return HttpResponseRedirect(authorization_url)
class GoogleAuthCallBackView(BasicView):
# google callback view
def get(self, request, *args, **kwargs):
state = request.session.get('state')
flow = google_auth_oauthlib.flow.Flow.from_client_config(
client_config=settings.GOOGLE_AUTH_CONFIG,
scopes=settings.GOOGLE_AUTH_SCOPES,
state=state
)
flow.redirect_uri = settings.GOOGLE_AUTH_CONFIG['web']['redirect_uris'][0]
# get redirect url from session data if exists
redirect_url = request.session.get('redirect_url') or settings.ADMIN_LOGIN_REDIRECT_URL
response = HttpResponseRedirect(redirect_url)
try:
del request.session['redirect_url']
except KeyError:
logger.info('Delete `redirect_url` in session get KeyError.')
pass
try:
flow.fetch_token(authorization_response=request.build_absolute_uri())
except Exception as e:
logger.error(e.message)
return response
# save credentials to session
credentials = flow.credentials
request.session["credentials"] = {
'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'scopes': credentials.scopes,
'expire_time': time.time() + TOKEN_EXPIRE_TIME,
}
profile_client = googleapiclient.discovery.build(
serviceName='oauth2',
version='v2',
credentials=credentials
)
profile = profile_client.userinfo().v2().me().get().execute()
email = profile['email']
user = user_manager.get_user_by_email(email)
if user:
user.username = profile['name'] # sync username from google
user.picture = profile['picture'] # sync avatar from google
user.save()
request.session["user"] = user.to_dict()
else:
return HttpResponseRedirect("/api/non_existent_user/") # show non-existent user
return response

Why i am getting 400 Bad Request error when sending json data in Flask?

I am trying to write a small restful api application, i am using Chrome Postman extension for sending requests to the app .
I believe that my code does not have mistakes but every time i am sending post request a 400 Bad Request error raising , here is my code:
#api_route.route('/api', methods=['GET'])
def api():
return jsonify({'message':'Api v1.0'})
#api_route.route('/api', methods=['POST'])
def create_user():
data = request.get_json()
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message', 'New User Created!'})
The json data that i am sending looks like this:
{"email" : "Test", "password" : "123123123"}
Why i am getting the 400 error ??
Update:
Screenshots for the requests using Postman:
GET Request
POST Request
Here i am initiating api route inside api controller :
from flask import Blueprint
api_route = Blueprint(
'api',
__name__
)
from . import views
then i am registering it inside def create_app() function :
from .api import api_route
app.register_blueprint(api_route)
Here are the extensions that i am using in my application:
toolbar = DebugToolbarExtension()
assets_env = Environment()
cache = Cache()
moment = Moment()
htmlminify = HTMLMIN()
csrf = CSRFProtect()
jac = JAC()
googlemap = GoogleMaps()
session = Session()
principal = Principal()
I solved the problem, i've initiated CSRFProtect with app so i need to include X-CSRFToken in all my requests, so i have two choices:
1 - To include the csrf_token in request.headers for all the requests
2 - Using #csrf.exempt decorator that coming with flask_wtf.csrf
For now i am using #csrf.exempt, so it become like this:
#api_route.route('/api', methods=['GET','POST'])
#csrf.exempt
def create_user():
if request.method == 'GET':
return jsonify({'message' : 'API v1.0'})
elif request.method == 'POST':
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user_api = Api(email=data['email'], password=hashed_password)
db.session.add(new_user_api)
db.session.commit()
return jsonify({'message' : 'New user created!'})
return return jsonify({'message' : 'No user has been added!'})
Thanks for #MrPyCharm for his interests , salute :) .
A good approach would be to structure your views as follows:
Instead of creating view with same route for different request methods, you can handle the request methods in the same view:
#api_route.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'GET':
return jsonify({'message':'Api v1.0'})
else:
data = request.get_json(force=True)
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message': 'New User Created!'})
# Just in case the if condition didn't satisfy
return None
A note for anyone else experiencing this with PostMan and Flask - you will also hit a HTTP 404 if your URL in PostMan is HTTPS but your Flask app only handles HTTP.

django and python requests - getting a 403 on a post request

I am using requests to log into my Django site for testing (and yes, I know about the Django TestClient, but I need plain http here). I can log in and, as long as I do get requests, everything is OK.
When I try to use post instead, I get a 403 from the csrf middleware. I've worked around that for now by using a #crsf_exempt on my view, but would prefer a longer term solution.
This is my code:
with requests.Session() as ses:
try:
data = {
'username': self.username,
'password': self.password,
}
ses.get(login_url)
try:
csrftoken = ses.cookies["csrftoken"]
except Exception, e:
raise
data.update(csrfmiddlewaretoken=csrftoken)
_login_response = ses.post(login_url, data=data)
logger.info("ses.cookies:%s" % (ses.cookies))
assert 200 <= _login_response.status_code < 300, "_login_response.status_code:%s" % (_login_response.status_code)
response = ses.post(
full_url,
data=data,
)
return self._process_response(response)
The login works fine, and I can see the csrf token here.
INFO:tests.helper_fetch:ses.cookies:<RequestsCookieJar[<Cookie csrftoken=TmM97gnNHs4YCgQPzfNztrAWY3KcysAg for localhost.local/>, <Cookie sessionid=kj6wfmta
However, the middleware sees cookies as empty.
INFO:django.middleware.csrf:request.COOKIES:{}
I've added the logging code to it:
def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None
try:
csrf_token = _sanitize_token(
request.COOKIES[settings.CSRF_COOKIE_NAME])
# Use same token next time
request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
# import pdb
# pdb.set_trace()
import logging
logger = logging.getLogger(__name__)
logger.info("request.COOKIES:%s" % (request.COOKIES))
Am I missing something with way I call request's session.post? I tried adding cookie to it, made no difference. But I can totally see why crsf middleware is bugging out. I thought the cookies were part of the session, so why are they missing in my second post?
response = ses.post(
self.res.full_url,
data=data,
cookies=ses.cookies,
)
This variation, inspired by How to send cookies in a post request with the Python Requests library?, also did not result in anything being passed to csrf middleware:
response = ses.post(
self.res.full_url,
data=data,
cookies=dict(csrftoken=csrftoken),
)
For subsequent requests after the login, try supplying it as header X-CSRFToken instead.
The following worked for me:
with requests.Session() as sesssion:
response = session.get(login_url)
response.raise_for_status() # raises HTTPError if: 400 <= status_code < 600
csrf = session.cookies['csrftoken']
data = {
'username': self.username,
'password': self.password,
'csrfmiddlewaretoken': csrf
}
response = session.post(login_url, data=data)
response.raise_for_status()
headers = {'X-CSRFToken': csrf, 'Referer': url}
response = session.post('another_url', data={}, headers=headers)
response.raise_for_status()
return response # At this point we probably made it
Docs reference: https://docs.djangoproject.com/en/dev/ref/csrf/#csrf-ajax
You could also try to use this decorator on your view, instead of the csrf_exempt. I tried to reproduce your issue, and this worked as well for me.
from django.views.decorators.csrf import ensure_csrf_cookie`
#ensure_csrf_cookie
def your_login_view(request):
# your view code

Django : Calling a Url with get method

I have the url and corresponding view as follows.
url(r'^(?P<token>.*?)/ack$', views.api_ACK, name='device-api_ack')
def api_ACK(request, token):
"""
Process the ACK request comming from the device
"""
logger.info('-> api_ACK', extra={'request': request, 'token' : token, 'url': request.get_full_path()})
logger.debug(request)
if request.method == 'GET':
# verify the request
action, err_msg = api_verify_request(token=token, action_code=Action.AC_ACKNOWLEDGE)
return api_send_answer(action, err_msg)
I want to call api_ACK function with request method as GET from another view api_send_answer
I am creating one url in /device/LEAB86JFOZ6R7W4F69CBIMVBYB9SFZVC/ack in api_send_answer view as follows..
def api_send_answer(action, err_msg, provisional_answer=None):
last_action = create_action(session,action=Action.AC_ACKNOWLEDGE,token=last_action.next_token,timer=500)
url = ''.join (['/device/',last_action.next_token ,'/',Action.AC_ACKNOWLEDGE])
logger.debug('Request Url')
logger.debug(url)
response = api_ACK(request=url,token=last_action.next_token) # This is wrong
Now from api_send_answer it is redirecting to api_ACK view, but how to call api_ACK with request method as GET?
Please help..Any suggestions would be helpful to me
This line
response = api_ACK(request=url,token=last_action.next_token) is wrong because view expects HttpRequest object and you give him url instead.
if you need to return view response to user, you can use redirect:
def api_send_answer(action, err_msg, provisional_answer=None):
last_action = create_action(session,action=Action.AC_ACKNOWLEDGE,token=last_action.next_token,timer=500)
url = ''.join (['/device/',last_action.next_token ,'/',Action.AC_ACKNOWLEDGE])
logger.debug('Request Url')
logger.debug(url)
return HttpResponseRedirect(url)
if you need to do something else with view response you have to use HttpRequest object not url as parameter.