SignatureDoesNotMatch while uploading file from React.js using boto3 generate_presigned_url - python

Currently the presigned url is generated from Python Lambda function and testing it on postman to upload the file works perfectly.
When uploading file from React.js using axios it fails with 403 status code and below error.
Code: SignatureDoesNotMatch
Message: The request signature we calculated does not match the signature you provided. Check your key and signing method
Python Code
import boto3
s3_client = boto3.client('s3')
params = {
'Bucket': 'bucket_name',
'Key': 'unique_id.pdf',
'ContentType': "application/pdf"
}
s3_response = s3_client.generate_presigned_url(ClientMethod='put_object', Params=params, ExpiresIn=300)
React Code
const readFileDataAsBuffer = (file) =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
resolve(event.target.result);
};
reader.onerror = (err) => {
reject(err);
};
reader.readAsArrayBuffer(file);
});
const onFileUploadChange = async (e) => {
const file = e.target.files[0];
const tempdata = await readFileDataAsBuffer(file);
return axios({
method: 'put',
url: presigned_url_link,
data: tempdata
})
.then(() => {})
.catch(() => {});
};

passing Content-Type worked for s3 link while uploading

Related

400 error when posting PushSubscription object to server

I am setting up push notifications using service workers + websockets using Django Channels and Django WebPush to receive the subscription and send a message back to the service worker.
While I am able to register a service worker and receive the PushSubscription object containing details of a push subscription. I then try to send this subscription to an end point in webpush 'save_information' that should have the subscription. However, when I attempt to do this I get the error:
Response {type: 'cors', url: 'http://127.0.0.1:8000/webpush/save_information', redirected: false, status: 400, ok: false, …}body: (...)bodyUsed: falseheaders: Headers {}ok: falseredirected: falsestatus: 400statusText: "Bad Request"type: "cors"url: "http://127.0.0.1:8000/webpush/save_information"[[Prototype]]: Response
This is the code I have in my service worker/ the code related to getting a subscription and sending through the subscription:
const saveSubscription = async (subscription) => {
const browser = navigator.userAgent.match(/(firefox|msie|chrome|safari|trident)/ig)[0].toLowerCase();
const data = {
status_type: 'subscribe',
subscription: subscription.toJSON(),
browser: browser,
};
const res = await fetch('http://127.0.0.1:8000/webpush/save_information', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'content-type': 'application/json'
},
credentials: 'include'
});
handleResponse(res);
}
const handleResponse = (res) => {
console.log(res);
}
...
self.addEventListener('activate', async () => {
try {
const applicationServerKey = urlB64ToUint8Array('***')
const options = { applicationServerKey,
userVisibleOnly: true }
const subscription = await self.registration.pushManager.subscribe(options)
saveSubscription(subscription)
} catch (err) {
console.log('Error', err)
}
})
and in my server:
path('webpush/', include('webpush.urls')),
Would appreciate any help

FCM onMessage not trigerring in React but app server sends multicast_messages properly

I am sending the message from a Python script as follows.
import firebase_admin
from firebase_admin import credentials, messaging
# initializations
cred = credentials.Certificate('Full path to firebase-admin-sdk.json')
print('Connecting...')
firebase_admin.initialize_app(cred)
registration_tokens = [
'valid_registration_token_from_client',
]
message = messaging.MulticastMessage(
data={'score': '850', 'time': '2:45'},
tokens = registration_tokens
)
response = messaging.send_multicast(message)
print('{0} messages were sent successfully.'.format(response.success_count))
After executing the above code, it prints 1 message sent successfully. I looked at my Firebase console and found that the number of sent notifications increased by 1. However, my React Js client does not seem to receive the message.
In react app,
Root public folder has firebase-messaging-sw.js file,
import { initializeApp } from "firebase/app";
import { getMessaging } from "firebase/messaging/sw";
import { onBackgroundMessage } from "firebase/messaging/sw";
const firebaseConfig = {
apiKey: "MY_API_KEY",
authDomain: "FIREBASE_APP_DOMAIN",
databaseURL: "DB_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "MEASUREMENT_ID"
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
onBackgroundMessage(messaging, (payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
In App.js,
import React, {Component} from "react";
import { getMessaging, getToken, onMessage } from "firebase/messaging";
import { initializeApp } from "firebase/app";
export default class App extends Component {
constructor(props) {
super(props);
this.connectButtonPressed = this.connectButtonPressed.bind(this);
}
render() {
return (
<div><button onClick={this.connectButtonPressed}>Click</button></div>
)
}
connectButtonPressed(e) {
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.');
// TODO(developer): Retrieve a registration token for use with FCM.
// Get registration token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
databaseURL: "DB_URL",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "SENDER_ID",
appId: "APP_ID",
measurementId: "MEASUREMENT_ID"
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
onMessage(messaging, (payload) => {
console.log('Message received. ', payload);
});
getToken(messaging, { vapidKey: 'VAPID_KEY_FROM_CONSOLE' }).then((currentToken) => {
if (currentToken) {
// Send the token to your server and update the UI if necessary
console.log('currentToken: ', currentToken);
} else {
// Show permission request UI
console.log('No registration token available. Request permission to generate one.');
// ...
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
// ...
});
} else {
console.log('Unable to get permission to notify.');
}
});
}
}
The client can successfully request for and receive the registration token to which I send the message from the Python script. However, the onMessage event is not being triggered which makes me think that the client might not be receiving the message even though I sent the message to the token associated with this client.
What might be the issue here? What am I missing?

Download File from Azure Blob Storage Using React

I am trying to download a file stored in Azure Blob Storage with my react app using SAS and am running into issues. I have a working version that relies on having the flask app download the file, then sending the blob to the react app to be downloaded again (obviously not ideal). Here's the current implementation:
flask endpoint:
from azure.storage.blob import BlobServiceClient
blobService = BlobServiceClient(account_url="https://<account_name>.blob.core.windows.net/", credential=<blob_key>)
#app.route('/download')
def downloadFile():
filename = request.args.get('filename')
blob_client = blobService.get_blob_client(container='<container_name>', blob=filename)
blobObject = blob_client.download_blob()
fileObject = io.BytesIO(blobObject.readall())
return send_file(fileObject, attachment_filename=filename, as_attachment=True)
get request in react:
const getFile = (e) => {
e.preventDefault();
const filename = e.currentTarget.getAttribute('name');
axios({
url: `${serverUrl}/download?filename=${filename}`,
method: 'GET',
responseType: 'blob',
})
.then(({ data }) => {
const link = document.createElement('a');
const url = URL.createObjectURL(new Blob([data]));
link.href = url;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
})
.catch(() => {
_isMounted.current && setDisplayError(true);
});
};
I would like to be able to just have my react app download the file direct from blob storage, but am running into authentication issues, along with another issue where clicking the download button navigates the browser to the url rather than just downloading the file at the location while staying on the current page. Here is the new code with the issues.
new flask endpoint:
from azure.storage.blob._shared_access_signature import BlobSharedAccessSignature
signatureService = BlobSharedAccessSignature('<account_name>', account_key='<azure_key>')
#app.route('/download')
def downloadFile():
filename = request.args.get('filename')
expiration = datetime.datetime.today() + datetime.timedelta(minutes=5)
container = '<container_name>'
key = signatureService.generate_blob(container, filename, permission='read', expiry=expiration)
data = {
'container': container,
'key': key
}
return app.response_class(
response=jsonifyWithNumPy(data),
status=200,
mimetype='application/json'
)
get request in react:
const getFile = (e) => {
e.preventDefault();
const filename = e.currentTarget.getAttribute('name');
axios
.get(`${serverUrl}/download?filename=${filename}`)
.then(({data}) => {
const url = `https://<account_name>.blob.core.windows.net/${data.container}/${filename}?${data.key}`
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
})
.catch(() => {
_isMounted.current && setDisplayError(true);
});
};
This is the error I get when I follow the URL generated by the above react code:
<Error>
<Code>AuthenticationFailed</Code>
<Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:48c49456-001e-008b-263c-3625fd000000 Time:2021-04-20T23:23:26.1909093Z</Message>
<AuthenticationErrorDetail>Signature fields not well formed.</AuthenticationErrorDetail>
</Error>
I tried your code to create a blob SAS key and get the same error, just try the code below that works for me to create a blob SAS key and access a blob successfully:
from azure.storage.blob import generate_blob_sas,BlobSasPermissions
from datetime import datetime, timedelta
account = ''
container = ''
blob = ''
account_key = ''
permission=BlobSasPermissions(read=True)
exp = datetime.utcnow() + timedelta(hours=1)
key = generate_blob_sas(account_name=account,container_name=container,blob_name=blob,account_key=account_key,permission=permission,expiry=exp)

http post a base64 json request between Node.js and Python Flask server but Node received an incomplete json

I try to send a base64 json form node.js server to python flask server and than return a same base64 code back to node.js server. Flask can successfully receive my json but when it response to node and node try to print out the response. I got a error message say: "Unexpected end of JSON input". I found the reason is node server can not receive the base64 completely. It just only receive a small portion. What is the problem? Is post request has a string limit?
I tested when I change the base64 code to a short string. Node server can receive response normally.
Anyone can help me? Thank you.
This is my code:
<<< Node.js Server >>>
var express = require('express');
var http = require('http');
var app = express();
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(10000, () => console.log('Running on http://localhost:10000'));
postData = JSON.stringify({
'code': <base64 code or short string here>
});
var options = {
hostname: 'localhost',
port: 10001,
path: '/test',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
var req = http.request(options, (res) => {
res.on('data', (chunk) => {
var data = JSON.parse(chunk);
console.log(data.message);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.write(postData);
req.end();
<<< Python Flask Server >>>
from flask import Flask
from flask import request
from flask import jsonify
app = Flask(__name__)
#app.route('/test', methods=['POST'])
def test():
request_data = request.get_json()
print(request_data['code'])
return jsonify(
message = request_data['code']
)
app.run(host='localhost', port=10001)
In NodeJS code, in data event, you will get chunk data(partial data), you need to wait until end event and then parse, following example may help you
var req = http.request(options, (res) => {
var data = '';
res.on('data', (chunk) => {
data += chunk.toString(); // buffer to string
});
res.on('end', () => {
data = JSON.parse(data);
console.log(data.message);
console.log('No more data in response.');
});
});

Is it OK to pass both token and client_id to the client when Channel API is used?

I need to create an application, where GAE server will always talk with just one client (i.e. one message should be always sent just to one client).
I do the following -
Python:
def get(self):
# generate token, when page is loaded
client_id = uuid.uuid4().hex
token = channel.create_channel(client_id)
template_values = {'token': token,
'client_id': client_id
}
self.response.out.write(template.render('page.html', template_values))
def post(self):
# reply to the client
...
client_id = self.request.get('id')
channel.send_message(client_id, message)
Javascript:
sendMessage = function(field) {
$.ajax({
type: "POST",
url: "/",
data: "f=" + field + "&id=" + "{{ client_id }}", // WARNING!
contentType: "application/x-www-form-urlencoded",
success: function(data) {
}
});
};
onOpened = function() {
connected = true;
sendMessage('opened');
};
onMessage = function(msg) {
alert(msg.data);
};
onError = function(err) {
alert(err);
};
onClose = function() {
alert("close");
};
// open new session
channel = new goog.appengine.Channel('{{ token }}'); // WARNING!
socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
It works well, but with such scenario both token and client_id are passed to the client. Is it OK?
There's no technical reason not to do this. If you're worried about security, the token is far more valuable: an attacker who could listen to your traffic could take the token and listen to channel messages in a different context. The clientid wouldn't let them do that.
But I do have a question: why not return the message in the POST response, rather than sending a message over the channel? Or is the sample code just simplified for the example?

Categories