I'm trying to create an auth system for my react(nextjs) app with flask_JWT_extended + ariadne(graphql). I have succesfully set up the login mutation that retrieves the access and refresh token but I dont know how to properly integrate them into my app. I am aware the access token is used to make subsequent requests and the refresh token is used to maintain token freshness but I dont know how to implement it with this stack.
mutations.py
Here is my login mutation that returns the access_token and refresh_token. It works fine.
#mutation.field("login")
#convert_kwargs_to_snake_case
def resolve_login(_, info, username, password):
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
payload = {
"user": user,
"access_token": access_token,
"refresh_token": refresh_token,
}
return payload
core.py
Here are my JWT configs, from what I have gathered online I am supposed to do a check on the token on each api request to maintain its freshness but I dont know how to do that especially with python + ariadne. Here is a link with someone implementing it with nodejs: https://github.com/benawad/graphql-express-template/blob/22_advanced_jwt_auth/auth.js
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql://localhost/pinkle"
app.config["JWT_SECRET_KEY"] = "this_is_a_secret"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
JWTManager(app)
index.js
Here is my front end making the call to login the user, it returns the tokens but I dont know where to utilize the tokens or if I should save it in client side state and just make calls with the token.
function login({ username, password }) {
axios
.post('http://localhost:5000/graphql', {
query: `mutation {
login(username: "${username}", password: "${password}") {
user {
id
username
password
}
}
}`,
})
.then(result => {
console.log(result.data)
})
}
The Flask-JWT-Extended documentation includes examples of utilizing JWTs from JavaScript which might be helpful for you: https://flask-jwt-extended.readthedocs.io/en/stable/token_locations/
Related
I have a Flask backend server that integrates Reddit's OAuth integration via PRAW, and a VueJS Frontend.
The backend APIs for handling the OAuth cycle look like this:
#api_user.route('/login', methods=['GET'])
def login() -> Response:
reddit: Reddit = Reddit()
auth_url: str = reddit.auth.url(scopes=SCOPES, state=STATE)
auth_url = urllib.parse.unquote(auth_url) # remove url encodings
return redirect(auth_url)
#api_user.route('/auth_callback', methods=['GET'])
def callback() -> Tuple[Response, int]:
code: str = request.args.get('code', '')
reddit = Reddit()
refresh_token: str = reddit.auth.authorize(code)
me: Redditor = reddit.user.me()
token = jwt.encode({
'refresh_token': refresh_token,
'user': {
'name': me.name,
'id': me.id
}
}, str(SECRET_KEY), algorithm=ALGORITHM)
response: Response = make_response(jsonify({
'token': token
}))
response.set_cookie('access_token', token, max_age=COOKIE_MAX_AGE)
return response, 200
#api_user.route('/me', methods=['GET'])
#jwt_required
def about(reddit: Reddit) -> Tuple[Response, int]:
user: Redditor = reddit.user.me()
return jsonify({
'name': user.name,
'id': user.id,
'karma': user.link_karma + user.comment_karma
}), 201
/api/login redirects to Reddit's authorization URL where user can provide their username/password, which then redirects the callback uri /api/auth_callback (set in the Reddit app configuration), which then generates a refresh token and sends it as a JWT Token to be stored as a cookie.
What I would like to know is how would I connect this flow with a separate VueJS frontend. Even if the VueJS login page /login calls the API /api/login, after the OAuth is finished it will end up at the server-side route /api/auth_callback?code=xyz.
Integrating VueJS in the Jinja2 Template and making the whole application contained within Flask can work, but it becomes messy quickly.
Another way is to make the redirect_uri be a VueJS route, not a backend route. But in that case, I would like to know how can I parse the URL and extract the code parameter (like I did in the /auth_callback Flask route handler). In that case, I can get rid of /api/auth_callback and add /api/jwt which takes the code parameter extracted by the Vue frontend and returns the JWT token after validating using PRAW.
Any kind of help would be appreciated. Thanks.
Im trying to add basic Authorization to my flask rest-x application.
By referring to https://github.com/python-restx/flask-restx/issues/271 I have made code changes.
But here there is no clarity on how to set and a username and password. As well in the swagger UI i see the login for basic auth happens with any username and password.
I have tried searching multiple resources but could not get a way how to set username & password and authenticate it. Please help regarding the same.
Flask restx version: 0.5.1
try the following code
from flask_restx import Api
blueprint = Blueprint("api", __name__)
authorizations = {
"Authorization": {
"description": "Inputs: Basic \\email\\>",
"type": "apiKey",
"in": "header",
"name": "Authorization",
}
}
api = Api(
blueprint,
title="Dummy API",
version="1.0",
authorizations=authorizations,
security="Authorization",
)
I'm pretty new to the world of authentication / keycloak, and after reading the keycloak doc I still can't wrap my head around the following situation.
Let's assume there's an existing web-application with keycloak as IAM.
Now, I need to implement a Flask/dash application which provides a service with business client specific data and dedicated API endpoints for all the business clients. Let's say the base URL is website.com/myapp/
Each business client has users in keycloak and some, not all, should access the data at the according endpoint, website.com/myapp/client1/
user_1 can view /client1/, user_2 and user_3 can view /client2/
That means I have to hardcode somewhere (database?) which user belongs to a client endpoint, correct?
In keycloak I've setup a client (myapp) with service accounts enabled
I gave the client a role view
I gave a user the role view
With the client_id and client_secret I can obtain the access_token and make all the known requests via the keycloak API.
The API routes in Flask/dash have a decorator function to check if the user is logged in and has the general view permission for myapp.
This works fine, but the problem is I need to pass the user_id to check if the keycloak session is active or not.
How do I check for the logged in user_id or user_name who wants to access /myapp/ so that I can validate the view permission and match to the dedicated Flask route?
authentication:
def authentication(user_id):
url = 'http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token'
payload = 'grant_type=client_credentials'
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
response = requests.request('POST', url, headers=headers, data=payload, auth=HTTPBasicAuth(client_id, client_secret))
token = json.loads(response.text)
return get_client_roles(token['access_token']), get_sessions(token['access_token'], user_id)
def get_sessions(token, user_id):
url = f'http://127.0.0.1:8080/auth/admin/realms/master/users/{user_id}/sessions'
payload = {}
headers = { 'Authorization': 'Bearer '+token }
response = requests.request('GET', url, headers=headers, data=payload)
sessions = json.loads(response.text)
return sessions
def get_client_roles(token):
url = 'http://127.0.0.1:8080/auth/admin/realms/master/clients/'+id+'/roles/view/users'
payload = {}
headers = { 'Authorization': 'Bearer '+token }
response = requests.request('GET', url, headers=headers, data=payload)
client_roles = json.loads(response.text)
return client_roles
With that I can check if user_x is logged in and has role view for myapp
But unfortunately I need to hardcode the user_id in my Flask/dash file:
excerption from Flask/dash:
dash_app1 = dash.Dash(server = app, name = 'Dashboard client1', url_base_pathname='/client1/')
dash_app2 = dash.Dash(server = app, name = 'Dashboard client2', url_base_pathname='/client2/')
...
user_id = 'dsdgbc-fd68-41c3-b6d-34fds343436aa75' # how to retrieve and not hardcode?
#app.route("/client1/", endpoint='route1')
#login_required
def render_dashboard():
return redirect('/client1/')
The user_id is the parameter in authentication(user_id) in the decorator function
I have the following routes for logging in/out and checking logging status:
from flask_login import current_user, login_user, logout_user
#app.route('/check_login_status')
def check_login_status():
return str(current_user.is_authenticated)
#app.route('/log_in')
def log_in():
user = User.query.first()
login_user(user)
return 'OK'
#app.route('/log_out')
def log_out():
logout_user()
return 'OK'
If I enter those routes manually in the browser, everything works as expected - the user gets logged in/out and check_login_status returns correct boolean value.
However, I need to access those routes with fetch API on the frontend like this:
function login() {
fetch('http://127.0.0.1:5000/log_in')
}
function logout() {
fetch('http://127.0.0.1:5000/log_in')
}
But calling those functions in the frontend (upon button click for example) will not affect the login status: the response of check_login_status route will remain unchanged.
Why is that so and can I fix this?
First of all in your login route
#app.route('/log_in')
def log_in():
user = User.query.first() // this is wrong .. you always login
//with the first user in your database table
login_user(user)
return 'OK'
.....
so first you need to specify the method for this route to be posted
#app.route('/log_in' , methods=['POST'])
and you receive the user credentials in the request body for example email and password
user_data= request.get_json()
then query by email
user = User.query.filter_by(email=user_dat['email']).first()
then you need to hash the password coming in the request and compare it to the hashed password of the user saved in the database .. if they are identical
then pass it to your login_user function
login_user(user)
/// java script
in request you need to do it like that
function login() {
fetch('http://127.0.0.1:5000/log_in',{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({email:'email#email.com',password:'password'}),}).then((res) => {
return res.json();
}).catch((err) => {
return err;
});
}
I would like to know how can I generate a JWT token in django based on some fields which I get from a form submit in the view. This toke is not used authentication in django but I need to pass this token back in response which will then be sent to another service using JavaScript.
I am not using DRF currently.
The flow will be something like
FrontEnd -> Form submit -> Django Backend generate JWT -> Send back to FrontEnd in response
Again this is not for authentication on django.
If someone can point me to a working example that will definitely help me out.
Regards,
M.
Found my answer.
Use the python library called pyjwt
code example (JWTvalid for 1 hour)
import jwt
import datetime
exp_time = datetime.datetime.now() + datetime.timedelta(hours=1)
JWT_PAYLOAD = {
"context": {
"user": {
"name": "My Name",
"email": "My Email",
},
"iss": "My ISS",
"exp": int(exp_time.timestamp()),
"iat": int(datetime.datetime.now().timestamp()),
}
}
jwt_token = jwt.encode(JWT_PAYLOAD, settings.JWT_SECRET, algorithm='HS256')