I've setup my project, i.e. I have created a front-end in React, and a back-end in Flask.
In my front-end I call my back-end with a post method with the following code:
function POST(path, data) {
return fetch(`${fetchUrl}${path}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + RequestAccessToken(),
},
body: JSON.stringify(data)
}
)
}
Where RequestTokenAccess():
const { instance, accounts, inProgress } = useMsal();
const [accessToken, setAccessToken] = useState(null);
const name = accounts[0] && accounts[0].name;
function RequestAccessToken() {
const request = {
...loginRequest,
account: accounts[0]
};
instance.acquireTokenSilent(request).then((response) => {
setAccessToken(response.accessToken);
}).catch((e) => {
instance.acquireTokenPopup(request).then((response) => {
setAccessToken(response.accessToken);
});
});
}
And then just the following to actually make the call to the back-end:
const [data, setData] = useState()
function fetchData(e) {
e?.preventDefault();
POST('/my_app', { data: data }).then(
async (response) => {
const json = await response.json()
setData(json.return_data)
}
)
}
So for the front-end everything is working. I can get a MS Login that authorizes me so I can actually se the front-end, and I can also get a token from the RequestAccessToken function, which is given as a header to the back-end call. So everything seems to be set on the front-end part. However, the back-end calls also need to be secure is my guess, but I am not sure how that works.
Basically my app.py file looks something like:
from flask import Flask, request, jsonify
from my_app_func import MyAppFunc
app = Flask(__name__)
#app.post("/api/my_app")
def my_app():
data = request.json.get("data")
return_data = MyAppFunction(data)
return return_data
So basically, what do I need in order secure back-end calls ? I have the token as a Bearer Token in the post call. But what is the next step ? What do I actually do with it ?
I also have the same question, but couldn't find answer. Below is what works for me:
If you want to validate the user from flask, you can send the token along with your request from react.
Then within flask, validate the user by making a request to microsoft graph api.
Here is one example how to do this:
https://github.com/Azure-Samples/ms-identity-python-flask-webapp-call-graph
Another question for you is why you can directly concatenate RequestAccessToken() as a string? isn't it only call the setAccessToken? I ask because in my react app, I don't know how to export the token so that other function can use it. I ended up using the MSAL.js v2, not the one for react.
You have to register another app on the portal azure and and give permissions to the api and configure that in the another app in portal azure . Try to do something in that space.
Related
I am unable to track down this issue I am having attempting to send a FormData object (containing a file) to a Flask-RESTful application. On the ReactJS side i have the following code:
const updateAvatar = ({ target }) => {
if (target.value) {
let form = new FormData()
form.append("file", target.files[0])
fetch(`myurl/avatar`, {
credentials: "include",
method: "POST",
headers: { 'Content-Type':"application/x-www-form-urlencoded" },
body: form
})
.then(response => response.json())
.then(data => Do Something)
}
}
On the Flask side I have the following code:
def post(self,user):
if request.endpoint=="avatar":
print('I can see this message but I error out on the next line')
f=request.files["file"]
The f=request.files["file"] causes the server to return a 400 error. I thought this was straight forward but i'm a bit stuck here.
One other thing to mention: I am running in a local HTTPS environment using credentials.
Any help would be much appreciated.
So I've created a small 2 page VUE with a login page and then routed over to a search page.
On login the Python handles the LDAP check and creates the JWT and it looks like it exists in the cookies in the browser however i'm not sure how to handle it when back in "Vue" land.
In addition I though it wasn't great to keep this info in cookies in the browser....hmmmmm
const routes = [
{
path: '/',
name: 'home',
component: LoginEntry,
props:{test:'Service Center Search Portal'}
},
{
path: '/scsearch',
name: 'scsearch',
component: SearchView
},
The cookies which I see...
Added Code
router.beforeEach(async (to, from, next) => {
console.log('runniing router');
console.log(to.name);
if (to.name === "scsearch") {
const response = await axios.get('/api/jwt/check');
console.log('juust did call');
console.log(response.status);
if ( response.status === 401 ) {
console.log('ressponse status 401');
return next({ name: "home" });
}
console.log('doing noothiing');
next();
}else{
next();
}
});
Added Code -2 (Working)
router.beforeEach(async (to, from, next) => {
console.log('runniing router');
console.log(to.name);
if (to.name === "scsearch") {
console.log('doing call');
const response = await axios.get('/api/jwt/check')
.then(function(value){
console.log(value);
next('/');
})
.catch(function(err){
console.log(err)
});
console.log('juust did call');
console.log(response);
console.log('doing noothiing');
next();
}else{
next();
}
});
I'm still not sure if this is what you want, but from your question, I assume that:
You are using Vue SPA
You want user that's not logged in can't access route /scsearch on your Vue app
You store your JWT on cookie and can be accessed with document.cookie
If there's JWT on cookie, that means user is logged in, if there's no JWT on cookie, that means user is logged out.
What you want is navigation guard, this is how you use it:
// function to get cookie by name, taken from https://stackoverflow.com/a/15724300/12397250
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
const router = new VueRouter({ ... })
// do something everytime user is changing route
router.beforeEach((to, from, next) => {
// if user is accesing route "ssearch" and not logged in, redirect to home
// assuming you store your jwt on your cookie with key "authtoken"
if (to.name === "ssearch" && !getCookie("authtoken")) {
// User is not logged in, redirect to route "home"
// return here is important so you don't call other next() below
return next({ name: "home" });
}
// Do nothing
next();
});
const app = new Vue({
router
}).$mount('#app')
Edit:
Since you are using HTTPOnly cookie, you need to check it by sending a request to your backend API.
for example:
router.beforeEach(async (to, from, next) => {
if (to.name === "ssearch") {
// You need to implement this function, you can use XHR, or library like axios.
// You also need to add new endpoint to check cookies on your flask app
const response = await http.get("http://yourbackend.com/api/auth/check");
// Assuming if the jwt verification failed, it returns 401
if ( response.status === 401 ) {
return next({ name: "home" });
}
}
next();
});
As for JWT, there is no better place to store them than the cookie. LocalStorage/sessionStorage could be unsafe (Read about Cross-Site-Scripting attacks).
I don't have any experience with Vue, but i guess to protect your 'search page', you should have something in your local state, that says the author has been authorized, if not route the client to login page.
I'm trying to get the FB messenger API working using Python's Flask, adapting the following instructions: https://developers.facebook.com/docs/messenger-platform/quickstart
So far, things have been going pretty well. I have verified my callback and am able to receive the messages I send using Messenger on my page, as in the logs in my heroku server indicate the appropriate packets of data are being received by my server. Right now I'm struggling a bit to send responses to the client messenging my app. In particular, I am not sure how to perform the following segment from the tutorial in Flask:
var token = "<page_access_token>";
function sendTextMessage(sender, text) {
messageData = {
text:text
}
request({
url: 'https://graph.facebook.com/v2.6/me/messages',
qs: {access_token:token},
method: 'POST',
json: {
recipient: {id:sender},
message: messageData,
}
}, function(error, response, body) {
if (error) {
console.log('Error sending message: ', error);
} else if (response.body.error) {
console.log('Error: ', response.body.error);
}
});
}
So far, I have this bit in my server-side Flask module:
#app.route('/', methods=["GET", "POST"])
def chatbot_response():
data = json.loads(req_data)
sender_id = data["entry"][0]["messaging"][0]["sender"]["id"]
url = "https://graph.facebook.com/v2.6/me/messages"
qs_value = {"access_token": TOKEN_OMITTED}
json_response = {"recipient": {"id": sender_id}, "message": "this is a test response message"}
response = ("my response text", 200, {"url": url, "qs": qs_value, "method": "POST", "json": json_response})
return response
However, running this, I find that while I can process what someone send my Page, it does not send a response back (i.e. nothing shows up in the messenger chat box). I'm new to Flask so any help would be greatly appreciated in doing the equivalent of the Javascript bit above in Flask.
Thanks!
This is the code that works for me:
data = json.loads(request.data)['entry'][0]['messaging']
for m in data:
resp_id = m['sender']['id']
resp_mess = {
'recipient': {
'id': resp_id,
},
'message': {
'text': m['message']['text'],
}
}
fb_response = requests.post(FB_MESSAGES_ENDPOINT,
params={"access_token": FB_TOKEN},
data=json.dumps(resp_mess),
headers = {'content-type': 'application/json'})
key differences:
message needs a text key for the actual response message, and you need to add the application/json content-type header.
Without the content-type header you get the The parameter recipient is required error response, and without the text key under message you get the param message must be non-empty error response.
This is the Flask example using fbmq library that works for me:
echo example :
from flask import Flask, request
from fbmq import Page
page = fbmq.Page(PAGE_ACCESS_TOKEN)
#app.route('/webhook', methods=['POST'])
def webhook():
page.handle_webhook(request.get_data(as_text=True))
return "ok"
#page.handle_message
def message_handler(event):
page.send(event.sender_id, event.message_text)
In that scenario in your tutorial, the node.js application is sending an HTTP POST request back to Facebook's servers, which then forwards the content on to the client.
So far, sounds like your Flask app is only receiving (AKA serving) HTTP requests. The reason is that that's what the Flask library is all about, and it's the only thing that Flask does.
To send an HTTP request back to Facebook, you can use any Python HTTP client library you like. There is one called urllib in the standard library, but it's a bit clunky to use... try the Requests library.
Since your request handler is delegating to an outgoing HTTP call, you need to look at the response to this sub-request also, to make sure everything went as planned.
Your handler may end up looking something like
import json
import os
from flask import app, request
# confusingly similar name, keep these straight in your head
import requests
FB_MESSAGES_ENDPOINT = "https://graph.facebook.com/v2.6/me/messages"
# good practice: don't keep secrets in files, one day you'll accidentally
# commit it and push it to github and then you'll be sad. in bash:
# $ export FB_ACCESS_TOKEN=my-secret-fb-token
FB_TOKEN = os.environ['FB_ACCESS_TOKEN']
#app.route('/', method="POST")
def chatbot_response():
data = request.json() # flasks's request object
sender_id = data["entry"][0]["messaging"][0]["sender"]["id"]
send_back_to_fb = {
"recipient": {
"id": sender_id,
},
"message": "this is a test response message"
}
# the big change: use another library to send an HTTP request back to FB
fb_response = requests.post(FB_MESSAGES_ENDPOINT,
params={"access_token": FB_TOKEN},
data=json.dumps(send_back_to_fb))
# handle the response to the subrequest you made
if not fb_response.ok:
# log some useful info for yourself, for debugging
print 'jeepers. %s: %s' % (fb_response.status_code, fb_response.text)
# always return 200 to Facebook's original POST request so they know you
# handled their request
return "OK", 200
When doing responses in Flask, you have to be careful. Simply doing a return statement won't return anything to the requester.
In your case, you might want to look at jsonify(). It will take a Python dictionary and return it to your browser as a JSON object.
from flask import jsonify
return jsonify({"url": url, "qs": qs_value, "method": "POST", "json": json_response})
If you want more control over the responses, like setting codes, take a look at make_response()
Im trying to connect to the REST API of FreeNAS (http://api.freenas.org/authentication.html) within my AngularJS app. The API uses basic authentication with username and password.
In python this is a very easy thing as there is only one line of code:
requests.get('http://freenas.mydomain/api/v1.0/account/bsdusers/',auth=('root', 'freenas'))
I tried to find something for AngularJS but stumbled only over excrutiating code, e.g. How do I get basic auth working in angularjs?
Is there anything available like this:
$http({
method: 'GET',
url: 'http://freenas.mydomain/api/v1.0/account/bsdusers/',
auth: ['username':'root', 'password':'pw']
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
You need to create a function for encoding the user and password in Base64("username:password") and add Authorization header.
You can try encoding your username and password over here https://www.base64encode.org/ and see if it works. "root:freenas" being cm9vdDpmcmVlbmFz you can try the code below.
$http.defaults.headers.common['Authorization'] = 'Basic cm9vdDpmcmVlbmFz';
Once you get it working get implement the Base64 factory you posted ( How do I get basic auth working in angularjs? )
Hope it helps :)
You can try like this.
$http.defaults.headers.common = {"Access-Control-Request-Headers": "accept, origin, authorization"};
$http.defaults.headers.common['Authorization'] = 'Basic ' + Base64.encode('root' + ':' + 'freenas');
$http({
method: 'GET',
url: 'http://freenas.mydomain/api/v1.0/account/bsdusers/'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
I've got an app on Google App Engine for which I use the webapp2 authentication as described in this tutorial (thus Google Account API is not being used for user account management).
Therefore I'm using this Google tutorial to implement Google+ Sign-In. The front-end works fine, however I am having troubles with the callback. I would like to do this without Flask, since the only thing it seems to be used for is generating a response. The original code for the first part of the callback is:
if request.args.get('state', '') != session['state']:
response = make_response(json.dumps('Invalid state parameter.'), 401)
response.headers['Content-Type'] = 'application/json'
return response
To get rid of the Flask dependency, I rewrote this to:
if self.request.get('state') != self.session.get('state'):
msg = json.dumps('Invalid state parameter.')
self.response.headers["Content-Type"] = 'application/json'
self.response.set_status(401)
return self.response.out.write(msg)
The problem though, is that self.request.get('state') returns nothing. I'm guessing this is because I am not reading the response properly, however I don't know how to do it right.
The Javascript that launches the callback is:
function signInCallback(authResult) {
if (authResult['code']) {
// Send the code to the server
$.ajax({
type: 'POST',
url: '/signup/gauth',
contentType: 'application/octet-stream; charset=utf-8',
success: function(result) {
console.log(result),
processData: false,
data: authResult['code']
});
} else if (authResult['error']) {
// There was an error.
// Possible error codes:
// "access_denied" - User denied access to your app
// "immediate_failed" - Could not automatially log in the user
console.log('There was an error: ' + authResult['error']);
}
}