Flask JWT Extended cookie name overide Flask Session Cookie Name - python-3.x

I am using the Flask JWT Extended extension for flask and have built a login application successfully using JWT. I have gone through the tutorial on JWT in Cookies on the JWT extended documentation site using CSRF protection and everything.
What I can't seem to figure out is when using the set_access_cookies() and set_refresh_cookies() methods the JWTs are not saved in a httponly cookie named using the JWT Extended default configuration setting.
app.config.setdefault('JWT_ACCESS_COOKIE_NAME', 'access_token_cookie')
app.config.setdefault('JWT_REFRESH_COOKIE_NAME', 'refresh_token_cookie')
Instead when I debug the return back from the auth call the cookies are saved in the base Flask default configuration instead.
'SESSION_COOKIE_NAME': 'session',
Shouldn't the set_access_cookies() and set_refresh_cookies() methods override the base Flask default configurations as long as make sure to register my app in the JWTManager()?
uscc_login_app = Flask(__name__)
jwt = JWTManager(uscc_login_app)
Or is there something else I missed in the base Flask JWT Extended documentation to ensure that its configuration defaults are used when appropriate?
Updated code via request.
The code is pretty spread out but here is my best shot to include what I think will help.
In init.py:
from flask import Flask, url_for
from flask_restful import Api
from flask_jwt_extended import JWTManager
from resources.auth import Authenticate
from resources.refresh import Refresh
from temp_app.temp import TempApp
from uscc_login.uscc_app_login import *
uscc_login_app = Flask(__name__)
uscc_login_app.config.from_object(os.environ.get('FLASK_ENV'))
jwt = JWTManager(uscc_login_app)
api = Api(uscc_login_app, prefix='/v1')
# Add resources via the add_resource method
api.add_resource(Authenticate, '/login')
api.add_resource(Refresh, '/refresh_token')
login_view = Login.as_view(name='uscc_login')
uscc_login_app.add_url_rule('/login', view_func=login_view, methods=['POST', 'GET'])
In my app.py:
from uscc_login import uscc_login_app
if __name__ == '__main__':
uscc_login_app.run(debug=uscc_login_app.config.get('DEBUG'), threaded=uscc_login_app.config.get('THREADED'),
port=uscc_login_app.config.get('PORT'), host=uscc_login_app.config.get('HOST'))
In my config.py since I am using the Flask config.from_objects
import os
import datetime
uscc_login_app_dir = os.path.abspath(os.path.dirname(__file__))
class BaseConfig:
SECRET_KEY = os.environ.get('USCC_SECRET_KEY') or 'you-will-never-guess'
JWT_SECRET_KEY = os.environ.get('USCC_JWT_KEY') or 'super-secret'
JWT_TOKEN_LOCATION = ['cookies']
JWT_COOKIE_CSRF_PROTECT = True
JWT_HEADER_TYPE = 'JWT'
PROPAGATE_EXCEPTIONS = True
THREADED = True
class DevelopmentConfig(BaseConfig):
DEBUG = True
PORT = 5000 if os.environ.get("PORT") is None else int(os.environ.get("PORT"))
HOST = os.environ.get('HOST') or 'localhost'
if os.environ.get('access_token_expiration') is not None:
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(seconds=int(os.environ.get('access_token_expiration')))
if os.environ.get('refresh_token_expiration') is not None:
JWT_REFRESH_TOKEN_EXPIRES = datetime.timedelta(seconds=int(os.environ.get('refresh_token_expiration')))
So then in my Flask MethodView that contains my login authorization POST I have the following:
auth.py
import sys
import os
from flask import jsonify, request
from flask_restful import Resource
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_refresh_token_required, get_jwt_identity, \
set_access_cookies, set_refresh_cookies
from utilities import Common
class Authenticate(Resource):
#staticmethod
def post():
"""
:return:
"""
api_cred_path = os.environ.get('api_cred_path')
if api_cred_path is None:
response = jsonify({"msg": "Environment Variable 'api_cred_path' is not set."})
response.status_code = 500
return response
if not request.is_json:
response = jsonify({'msg': 'Missing JSON in request'})
response.status_code = 400
return response
params = request.get_json()
user_name = params.get('username')
user_password = params.get('password')
if not user_name:
response = jsonify({'msg': 'Missing username parameter'})
response.status_code = 400
return response
if not user_password:
response = jsonify({'msg': 'Missing password parameter'})
response.status_code = 400
return response
if Common.check_path_exists(api_cred_path):
with open(api_cred_path) as afh:
for line in afh:
file_userid, file_password = line.split('=')
if file_userid == user_name and file_password.strip('\n') == user_password:
access_token = create_access_token(identity=user_name)
refresh_token = create_refresh_token(identity=user_name)
response = jsonify({'login': True})
set_access_cookies(response, access_token)
set_refresh_cookies(response, refresh_token)
# # Identity can be any data that is json serializable
# art = {
# 'access_token': create_access_token(identity=user_name),
# 'refresh_token': create_refresh_token(identity=user_name)}
# response = jsonify(art)
response.status_code = 200
return response
else:
response = jsonify({"msg": "api_cred_path invalid."})
response.status_code = 500
return response
response = jsonify({'msg': 'Bad username or password'})
response.status_code = 401
return response

Could you provide some code to duplicate what you are seeing? When I try running the example token in jwt code (https://github.com/vimalloc/flask-jwt-extended/blob/master/examples/jwt_in_cookie.py) I see the expected cookie values when I login:
$ http :5000/token/auth username=test password=test
...
Set-Cookie: access_token_cookie=<jwt>; HttpOnly; Path=/api/
Set-Cookie: refresh_token_cookie=<jwt>; HttpOnly; Path=/token/refresh
...

So I realized my mistake in this. I was trying to get the access_token_cookie variable to be set from my auth.py which serves as my RESTFUL based microservice of which my login app calls to do the authorization. Realized it won't be available after redirecting back to the caller from the login apps POST method since the cookie was related to the login app UI frontend. So I just base the access and refresh tokens back from the auth.py POST method to the login POST method and let it set the cookies so they are available to the end client.
This was more of design problem than a code problem.

Related

How do I pass an API Token into Cookie via Python Request?

I'm having issues w/ authenticating to pkgs.org api, a token was produced, by support mentioned it needs to be passed as a cookie. I've never worked with cookies before.
import requests
import json
import base64
import urllib3
import sys
import re
import os
token=('super-secret')
#s = requests.Session()
head = {'Accept':'application/json'}
r = requests.get('https://api.pkgs.org/v1/distributions', auth=(token), headers=head)
print(r)
print(r.connection)
print(r.cookies)
I tried to use the request.session method, to handle the cookie, but i honestly don't know syntax on how to ever 1 create a cookie, let alone pass the cookie.
If I read the API documentation correctly you should set acces_token cookie:
import requests
token = "super-secret"
cookies = {"access_token": token}
headers = {"Accept": "application/json"}
r = requests.get(
"https://api.pkgs.org/v1/distributions", cookies=cookies, headers=headers
)

Get requests to Google Chronicle using API KEY in python

Im trying to make some GET requests in Google Chronicle using only API KEY.
I found this code but it only work with json credentials:
def call_list_alerts():
import os
from google.oauth2 import service_account
from googleapiclient import _auth
SCOPES = ['https://www.googleapis.com/auth/chronicle-backstory']
SERVICE_ACCOUNT_FILE = os.path.join(os.environ['HOME'], 'bk_credentials.json')
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
http_client = _auth.authorized_http(credentials)
BACKSTORY_API_V1_URL = 'https://backstory.googleapis.com/v1'
LIST_ALERTS_URL = '{}/alert/listalerts?start_time=2019-10-15T00:00:00Z&end_time=2019-10-17T00:00:00Z&page_size=1'.format
(BACKSTORY_API_V1_URL)
response = http_client.request(LIST_ALERTS_URL, 'GET')
if response[0].status == 200:
alerts = response[1]
print(alerts)
else:
err = response[1]
print(err)
How can i achieve this using API key and not service account?

How to return 401 in flask despite authorization

I am new to flask and I want to return 401 error, I am not using authentication, I am simply using a parameter api_key to check whether the user is authenticated.
I've tried abort(401) and return false, but it returns error 400 'Bad Request', is this the default way it returns a 401. Or is there a different way to do this?
My Code:
import flask
from flask import jsonify, request, Response
from flask_cors import CORS, cross_origin
from GoogleNews import GoogleNews
googlenews = GoogleNews()
googlenews.set_lang('en')
googlenews.set_period('1d')
googlenews.set_encode('utf-8')
app=flask.Flask(__name__)
cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'
#cross_origin()
#app.route('/', methods=["GET"])
def return_news():
API_KEY = request.args['api_key']
if API_KEY == "xyz":
googlenews.get_news('TECHNOLOGY')
return jsonify(googlenews.result(True)[0:4])
else:
return Response(response="Unauthorized", status=401)
Thankyou
instead, try flask.Response object.
from flask import Flask, Response
app = Flask(__name__)
#app.route("/abort")
def abort():
return Response(response="Unauthorized", status=401)
app.run()
without the actual code it is hard to tell, but I think it is not using the flask abort() but the python internal abort() function.
Check the code below:
import flask
app = flask.Flask("aborter")
#app.route("/abort")
def abort():
return flask.abort(401)
if __name__ == '__main__':
app.run()
Happy Coding

oauth2session dynamics365 : keep receiving Error 401 Unatuthorized

The use case
I am trying to connect to Microsoft Dynamics 365 - Field Service.
I am using Python, Falsk and OAuth2Session to perform a Oauth2 authentication
I have setup the Azure App on Azure.
the error message
I keep receiving the HTTP Error 401
Who could help me?
the code : config.py
"""Configuration settings for running the Python auth samples locally.
In a production deployment, this information should be saved in a database or
other secure storage mechanism.
"""
import os
from dotenv import load_dotenv
load_dotenv()
CLIENT_ID = os.environ["dynamics365_field_service_application_client_id"]
CLIENT_SECRET = os.environ["dynamics365_field_service_client_secrets"]
REDIRECT_URI = os.environ["dynamics365_field_service_redirect_path"]
# AUTHORITY_URL ending determines type of account that can be authenticated:
# /organizations = organizational accounts only
# /consumers = MSAs only (Microsoft Accounts - Live.com, Hotmail.com, etc.)
# /common = allow both types of accounts
AUTHORITY_URL = os.environ["dynamics365_field_service_authority"]
AUTHORIZATION_BASE_URL = os.environ["dynamics365_field_service_authorization_base_url"]
TOKEN_URL = os.environ["dynamics365_field_service_token_url"]
AUTH_ENDPOINT = "/oauth2/v2.0/authorize"
RESOURCE = "https://graph.microsoft.com/"
API_VERSION = os.environ["dynamics365_field_service_version"]
SCOPES = [
"https://admin.services.crm.dynamics.com/user_impersonation"
]
# "https://dynamics.microsoft.com/business-central/overview/user_impersonation",
# "https://graph.microsoft.com/email",
# "https://graph.microsoft.com/offline_access",
# "https://graph.microsoft.com/openid",
# "https://graph.microsoft.com/profile",
# "https://graph.microsoft.com/User.Read",
# "https://graph.microsoft.com/User.ReadBasic.All"
# ] # Add other scopes/permissions as needed.
the code : dynamics365_flask_oauth2.py
# *-* coding:utf-8 *-*
# See https://requests-oauthlib.readthedocs.io/en/latest/index.html
from requests_oauthlib import OAuth2Session
from flask import Flask, request, redirect, session, url_for
from flask.json import jsonify
import os
import flaskr.library.dynamics365.field_service.config as config
app = Flask(__name__)
app.secret_key = os.urandom(24)
# This information is obtained upon registration of a new dynamics
# client_id = "<your client key>"
# client_secret = "<your client secret>"
# authorization_base_url = 'https://dynamics.com/login/oauth/authorize'
# token_url = 'https://dynamics.com/login/oauth/access_token'
#app.route("/")
def index():
"""Step 1: User Authorization.
Redirect the user/resource owner to the OAuth provider (i.e. dynamics)
using an URL with a few key OAuth parameters.
"""
dynamics = OAuth2Session(
config.CLIENT_ID, scope=config.SCOPES, redirect_uri=config.REDIRECT_URI
)
authorization_url, state = dynamics.authorization_url(config.AUTHORIZATION_BASE_URL)
# State is used to prevent CSRF, keep this for later.
session["oauth_state"] = state
print(f"Please go here and authorize : {authorization_url}")
return redirect(authorization_url)
# Step 2: User authorization, this happens on the provider.
#app.route("/login/authorized", methods=["GET"]) # callback
def callback():
""" Step 3: Retrieving an access token.
The user has been redirected back from the provider to your registered
callback URL. With this redirection comes an authorization code included
in the redirect URL. We will use that to obtain an access token.
"""
if session.get("oauth_state") is None:
return redirect(url_for(".index"))
dynamics = OAuth2Session(
config.CLIENT_ID, state=session["oauth_state"], redirect_uri=config.REDIRECT_URI
)
token = dynamics.fetch_token(
token_url=config.TOKEN_URL,
client_secret=config.CLIENT_SECRET,
authorization_response=request.url,
)
print(f"token: {token}")
# At this point you can fetch protected resources but lets save
# the token and show how this is done from a persisted token
# in /profile.
session["oauth_token"] = token
# return redirect(url_for(".dynamics_get_accounts_postman"))
return redirect(url_for(".dynamics_get_accounts"))
#app.route("/profile", methods=["GET"])
def profile():
"""Fetching a protected resource using an OAuth 2 token.
"""
dynamics = OAuth2Session(config.CLIENT_ID, token=session["oauth_token"])
return jsonify(dynamics.get("https://api.dynamics.com/user").json())
#app.route("/get_accounts")
def dynamics_get_accounts():
if session.get("oauth_token") is None:
return redirect(url_for(".index"))
dynamics = OAuth2Session(
client_id=config.CLIENT_ID,
# token="Bearer " + session["oauth_token"]["access_token"]
token=session["oauth_token"],
)
result = dynamics.get("https://{env_name}.{region}.dynamics.com/api/data/v9.0")
if result.status_code != 200:
result = {"status code": result.status_code, "reason": result.reason}
else:
result = result.json()
result = jsonify(result)
return result
import requests
#app.route("/dynamics_get_accounts_postman")
def dynamics_get_accounts_postman():
if session.get("oauth_token") is None:
return redirect(url_for(".index"))
url = "https://{env_name}.{region}.dynamics.com/api/data/v9.0/accounts"
payload = {}
headers = {
"Accept": "application/json",
"OData-MaxVersion": "4.0",
"OData-Version": "4.0",
"If-None-Match": "null",
"Authorization": f'Bearer {session["oauth_token"]["access_token"]}',
}
response = requests.request("GET", url, headers=headers, data=payload)
result = response.text.encode("utf8")
print(f"result : {result}")
return jsonify(result)
if __name__ == "__main__":
# This allows us to use a plain HTTP callback
os.environ["DEBUG"] = "1"
app.secret_key = os.urandom(24)
app.run(debug=True)
The parameter resourcewas missing when generating the authorization_url.
authorization_url, state = dynamics.authorization_url(
config.AUTHORIZATION_BASE_URL + f"?resource={config.DYNAMICS365_CRM_ORG}"
)

How can I validate payloads from GitHub webhooks in Python 3?

I've realized auto-deploying of my Flask app on the server by Github webhooks, but I cannot adapt Ruby script specified in the manual to Python 3 to validate the POST-request. I've tried this:
from flask import Flask, request
from hmac import HMAC, compare_digest
from hashlib import sha1
app = Flask(__name__)
def verify_signature(req):
received_sign = req.headers.get('X-Hub-Signature').split('sha1=')[-1].strip()
secret = 'my_secret_string'.encode()
expected_sign = HMAC(key=secret, msg=req.data, digestmod=sha1).hexdigest()
return compare_digest(received_sign, expected_sign)
#app.route('/webhook', methods=['POST', 'GET'])
def webhook():
if request.method == 'POST':
if verify_signature(request):
do_smth()
return 'Successfully', 200
return 'Forbidden', 403
return 'Not allowed', 405
Also I tried other variants with sha1(...).hexdigest() and compare_digest() from secrets package, but received signature always differs.
What do I do wrong?
Use SHA-256 instead of SHA-1
Instead of SHA-1 GitHub recommends using the more secure SHA-256.
Changed your code respectively for convenience.
from flask import Flask, request
from hmac import HMAC, compare_digest
from hashlib import sha256
app = Flask(__name__)
def verify_signature(req):
received_sign = req.headers.get('X-Hub-Signature-256').split('sha256=')[-1].strip()
secret = 'my_secret_string'.encode()
expected_sign = HMAC(key=secret, msg=req.data, digestmod=sha256).hexdigest()
return compare_digest(received_sign, expected_sign)
#app.route('/webhook', methods=['POST', 'GET'])
def webhook():
if request.method == 'POST':
if verify_signature(request):
do_smth()
return 'Successfully', 200
return 'Forbidden', 403
return 'Not allowed', 405
I've updated the code. Now it works well.

Resources