What must be done on backend server and what on client - javascript

I have situation,
I need to download some data let's say X gigs(huge data) on client machine using browser's native js. Then I need to run compute extensive job tasks(tensorFlow like computation) on client and finally show results to actual uses.
I need to understand things like how can I architect such requirement, I am UI engineer I have never done this things in life.
If someone can suggest end 2 end things this will save my life.
Thanks in advance.

There's a few ways you can communicate between a client and a server.
1
The first one is the more traditional way of sending xhr requests from the client who waits for a response.
On Server:
app.post('/path', (req, res) => {
const json = req.body;
//do work
const resp = {some: 'data'};
res.json(resp);
}
On Frontend
fetch('/path', {
method: 'post',
body: JSON.stringify(data),
headers: { 'Content-type': 'application/json' }
})
.then(res => res.json()) // get json data out of response object
.then(json = > {
// do something with response json
}
2
This second method uses a package called Socket.IO to communicate via websockets. Both the client and server can send and listen to specific events using the following simple pattern.
socket.emit('event_name', optional_json);
socket.on('event_name', res => useResponse(res));
You may look at these resources to learn about these methods of communicating between client and server:
Fetch API
Express Routing
Socket.IO

Related

Getting an authentication token for a Microsoft Custom App

I have been struggling with the same issue for a while now, i'm trying to upload a file to my MS Teams OneDrive through the Graph-API but i dont have the authorization for it.
Reading the documentation to get my Token from Microsoft has so far done nothing for me as i am new to Javascript and React, so im having extreme difficulty getting it to work right. Can anyone give me an example of what the code looks like to get the authorization token that i need to access the Graph-API?
I have registered my Microsoft app and made a client-secret that i need in order to fetch the token.
Thank you in advance!
My code:
import React from 'react';
import './App.css';
import * as microsoftTeams from "#microsoft/teams-js";
class Tab extends React.Component {
constructor(props){
super(props)
this.state = {
context: {}
}
}
componentDidMount() {
var myHeaders = new Headers();
myHeaders.append("Content", "text/plain");
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Authorization", "Bearer {token}");
var raw = "This works";
var requestOptions = {
method: 'PUT',
headers: myHeaders,
body: raw,
redirect: 'follow'
}
fetch("https://graph.microsoft.com/v1.0/sites/OpenSesameTest/Shared%20Documents/General/FileB.txt:/", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
componentDidMount() {
var myHeaders = new Headers();
myHeaders.append("Content", "text/plain");
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Authorization", "Bearer {token}");
var raw = "Fetch my token";
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
}
fetch("https://login.microsoftonline.com/openimstest/oauth2/v2.0/c7094fc6-9d30-429d-bb66-dd389295b426", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
render() {
return (
<form>
<div>
<label>Select file to upload</label>
<input type="file"></input>
</div>
<button type="submit">Upload</button>
</form>
);
}
}
export default Tab;
P.S.
I know im not actually using teh file input on my page but i want to do it as simple as possible at first, i'll be happy just to succesfully upload a file through the Graph-API at the moment.
Once again thank you!
EDIT:
The the fetch im trying to use in order to get the token:
componentDidMount() {
var myHeaders = new Headers();
myHeaders.append("Content", "text/plain");
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Authorization", "Bearer <token>");
var raw = "This works";
var requestOptions = {
method: 'GET',
grant_type: 'Unsure where to find my client_credentials',
client_id: 'my client-id',
scope: 'https://graph.microsoft.com/.default',
client_secret: 'my client-secret',
headers: myHeaders,
body: raw,
redirect: 'follow'
}
fetch("https://login.microsoftonline.com/openimstest/oauth2/v2.0/token", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
Im unsure where to find my client_credentials or what to put there. Also there is probably something else wrong with the fetch im trying to use.
Microsoft has made a library specially for getting tokens in single pages apps. It’s called #azure/MSAL-browser They even made a package specially for react. msal-react this page shows a getting started on how to use this in react. This package will handle the token request for you.
No client credentials flow
You can do this using client secret by following the steps below. Before you continue, Note that you should not be be using the client credentials flow on the SPA Reactjs application because there is no way to secure the client secret.
The other answer should had left it with this statement. Let me explain that. The client credentials flow is for application that run server side without user interaction. Anyone using this flow in a client app is using it incorrectly! So I’m my opinion we should not educate people in how it might be possible to misuse an authentication flow.
Have a look at this Single sign-on (SSO) support for tabs.
Follow document for creating app registration and setting up expose an API part.
Add route for all these file in your app. You might use the same route as mention in the app I shared below for folder structure.
<Route exact path="/signin" component={SignInPage} />
<Route exact path="/signin-simple-start" component={SignInSimpleStart} />
<Route exact path="/signin-simple-end" component={SignInSimpleEnd} />
Next have a look at this folder structure-> signin. Here you will see three files
sign-in.tsx -> This file route, you need to give in the manifest. It has the button that starts the authentication flow. Also in this file only, you need to give the route of the page you want to show when authentication is successful.
successCallback: () => {
history.push("/yourPage");
}
sign-in-start.tsx -> Here you need to call the authentication endpoint. So in the app that I shared we have backend with C# and from there we get the URL endpoint on the line 22 and push it in history. What you can do is you can directly create that URL(see below) and assign it to variable result(replacing the code from line 22 to 24 with this).
var result = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize?client_id=<client_id>&response_type=id_token token&redirect_uri=https://<app-domain>/signin-simple-end&response_mode=fragment&scope=<required-scope>&state=12345&nonce=abcde&login_hint=<user-principal-name>;
document.location.href = result;
sign-in-end.tsx -> In this file we get all the token that we have asked in the last step. So, in order to get access token you need to add this code
if (hashParams["access_token"]) {
localStorage.setItem("auth.result", JSON.stringify({
accessToken: hashParams["access_token"]
}));
}
in useEffect where we are getting other tokens. The above code checks if we get the access_token and set it in localStorage later on you can get it with localStorage.getItem("auth.result").
You might need to do little manipulation on localStorage.getItem("auth.result") in order to get the token.
Above method get us the delegated Graph API permission, so you need to give the delegated permission in your app according to your graph call.
You can do this using client secret by following the steps below.
Before you continue, Note that you should not be be using the client credentials flow on the SPA Reactjs application because there is no way to secure the client secret.
Note that If you have to use client secret there then you should remove the interaction with Graph to the serve side and then secure your app using some other way see - this Tutorial: Call the Microsoft Graph API in a Node.js console app for Nodejs Secured Daemon Service authentication
For getting access token using Client Secret.
Add the required application permission on Azure AD for uploading the file to onedrive. In this case Files.ReadWrite.Al. This permission will require admin approval.
Acquire an access token from Azure AD using the request below in fetch format.
curl --location --request GET 'https://login.microsoftonline.com/tenant-id/oauth2/v2.0/token'
--header 'Content-Type: application/x-www-form-urlencoded'
--data-urlencode 'grant_type=client_credentials'
--data-urlencode 'client_id=client-id'
--data-urlencode 'scope=https://graph.microsoft.com/.default'
--data-urlencode 'client_secret=client-secret'
As I said, client secret will not be secure when used in the Single Page Reactjs application.
The better option is to use auth code flow which will be more secure in your SPA case. Follow this for reactjs Tutorial: Sign in users and call the Microsoft Graph API from a React single-page app (SPA) using auth code flow
This will just require:
Adding Files.ReadWrite as delegated permission on AAD
Signing in with a user that has access to the teams team and authenticating as them.
Call PUT /groups/{group-id}/drive/items/{item-id}/content where the group id is the team id.

GET/POST methods on Express

I am configuring a server using express.
My question has nothing to do with the project itself because it is running great.
I just have a minor doubt about why I have to use GET when for me it makes more sense to use POST.
So, for short I am configuring an API key on the server side and fetching it on the client side so I can use it.
This is the snippet on the server side:
const apiKey = process.env.API_KEY;
console.log(`Your API key is ${apiKey}`);
const dataObject ={};
app.get('/api', (req,res) => {
res.send({key: apiKey})
})
app.get('/all', sendData = (req,res) => {
res.send(dataObject)
})
app.post('/addText', (req,res) => {
let newEntry = {
agreement = req.body.agreement,
subjectivity = req.body.subjectivity
}
dataObject = newEntry;
res.send(dataObject);
} )
And then on the client side I fetch on the '/api' path:
const getApiKey = async () => {
// Getting API key from server
const request = await fetch('/api');
try {
const data = await request.json();
console.log(data);
return data;
}catch(error) {
console.log('ERROR', error);
}
}
Ok, that's working and everything, but my question is:
On the first GET on the server side, I understand that I am sending the API key to the '/api' path so that I can retrieve this key with fetch on the client side. But if I am sending the api key to this path, why am I using GET and not POST?
Sorry if it seems a stupid question but I am having a hard time understanding the GET method.
Thanks!
You are not sending any API key to the server. The server is sending the API key to the client as a response. The client uses a GET request to get the API key from /api. The names of the methods (GET, POST, PUT, DELETE, ...) are from the perspective of the client.
"And then on the client side I fetch on the '/api' path:" No. First the client sends the request with
const request = await fetch('/api');
try {
const data = await request.json();
console.log(data);
return data;
}catch(error) {
console.log('ERROR', error);
}
This triggers the callback in
app.get('/api', (req,res) => {
res.send({key: apiKey})
})
and the server sends the response.
This code returns the API key from the server. It does not send it from the client to the server.
app.get('/api', (req,res) => {
res.send({key: apiKey})
}
The
res.send()
Function is constructing the response returned by the server to the client.
Usually, you use the GET method when the client has to read some data from the server, in this case, you want to read the API_KEY defined in the server. GET has no body, but every request may be parametric by passing parameters in the query string.
On the other hand, when you have to update or create an entity on the server, that's when POST (or PUT) enters into action. Here you pass your data in the body of the request.

How do I stream JSON objects from ExpressJS?

I'm trying to stream JSON objects from an ExpressJS / Node backend API to a frontend site.
I do not want to use Sockets.IO for various reasons. As I understand it, the native streaming libraries should support streaming objects, it appears that just Express is complicating this.
My frontend code seams straight forward. I use Fetch to get my target URL, get a read stream from the response object, and set that read stream to objectMode: true.
Frontend Example:
async function () {
let url = "myurl";
let response = await fetch( url, {
method: 'GET',
mode: 'cors',
wtihCredentials: 'include'
}
const reader = response.body.getReader({objectMode: true });
// Where things are a bit ambiguous
let x = true;
while (x) {
const {done, value} = reader.read()
if (done) { break; }
// do something with value ( I push it to an array )
}
}
Backend Bode Example ( fails because of I cannot change the stream to objectMode )
router.get('/', (request, response) => {
response.writeHead(200, { 'Content-Type' : 'application/json' });
MongoDB.connection.db.collection('myCollection').find({}).forEach( (i) => {
response.write(i);
}).then( () => {
response.end()
})
})
Now my problem is that there does not appear to be anyway to change the ExpressJS write stream to objectMode: true. To my dismay, the ExpressJS documentation doesn't even acknoledge the existence of the write() function on the response object: https://expressjs.com/en/api.html#res
How do I change this over to objectMode: true ?
conversely, I tried to work with the writeStream as a string. The problem that I run into is that when the send buffer fills up, it does it by characters, not by the object. These means that at some point invalid JSON is passed to requester.
A suggested solution that I run into often is that I could read all of the chunks on the client and assemble valid JSON. This defeats the purpose of streaming, so Im trying to find a better way.
For what I believe is the same problem, I cannot figure out how to talk directly to the write stream object from the express code so I am unable to use the native writeStream operation writable.length in order to manually check to see if there is space for the entire JSON object as a string. This is preventing me from using stringified JSON with new line terminators.
https://nodejs.org/api/stream.html#stream_writable_writablelength
https://nodejs.org/api/stream.html#stream_writable_writableobjectmode
Could someone set me straight? I am working with 100k + records in my Mongo database, I really need partical page loading to work so that the users can start picking through the data.

Headers not set node.js api push notification

Hello im trying to set up push notifications for my webapp.
I'm getting my subscription like I should.
It saves it to my database correctly.
It sends my notification like it should if there only is ONE user in the db
and i want to send to more than only one user :)
Im using:
Vue.js (framework)
Axios (post)
node.js (api)
mongoDB (database)
Here's my post to API.
await axios({
method: 'post',
url: 'API',
data: {
subscription: JSON.stringify(subscription),
storeId: storeId
},
headers: {
'Content-Type': 'application/json'
}
})
It registreres my post, but then i get an throw error.
that "Can't set headers after they are sent."
I'm using CORS in my app like this:
const cors = require('cors')
const app = express();
app.use(bodyParser.json());
app.use(cors())
app.use(morgan('combined'))
The way I'm handling the post from my website is by finding my subscriptions and then map through and say foreach subscription
webpush
//subscribe routes
app.post('/pushNotification', (req, res) => {
var storeId = req.body.storeId
res.setHeader('Content-Type', 'application/json');
console.log(storeId)
if (req.body.storeId != null) {
console.log('Test1')
//get pushSubscription object
//create payload
const payload = JSON.stringify({ title: 'push test' })
Push.find({
"storeId": storeId
},
'subscription', function(error, response) {
console.log('Test2')
console.log(response)
response.map(item => {
res.status(201).json({});
console.log('Test3')
var subscription = item.subscription
console.log(subscription)
webpush.sendNotification(subscription, payload).catch(err => console.error(err));
})
})
} else {
res.send("failed")
}
})
As i can read around somewhere is it im not setting headers or something right. I have used cors like in tutorials and stuff.
So it's like it is crashing because it iterates wrong.
but i can't see how.
ERROR MESSAGE:
Thanks in advance
you are getting this error because res.status(201).json({}) has already set the headers and sent back the response to the client but webpush.sendNotification also trying to set the headers.You should use only webpush.sendNotification(subscription, payload).catch(err => console.error(err));
res.json([body]) sets the corresponding header and sends the result:
Sends a JSON response. This method sends a response (with the correct content-type) that is the parameter converted to a JSON string using JSON.stringify().
So, first of all you don't need to set header manually.
second, If the response has more than one item, since you can't send multiple result for a request, you shouldn't use res.json in a map.
Moreover, be aware of webpush.sendNotification that it may send a result too.

Overcome login middleware in react

i am login from localhost use react to another localhost use express,
Hi Guys im using this code to post the login data via react:
handleSubmit(event) {
event.preventDefault();
var params = Object.entries(this.state).map(([key, val]) => `${key}=${val}`).join('&')
fetch("http://localhost:3006/login", {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'},
body: params
})
.then((response) => response.json())
.then((responseJson) => {
//this.props.history.push("/landing");
console.log("loggedin", responseJson);
})
}
and the other localhost the backend is :
router.post("/login", passport.authenticate("local",{successRedirect: "/landing",failureRedirect: "login"}) ,function(req, res){
console.log(req.body);
});
i need way to save my login data after i sent it so i can login to the page /landing.
normaly the code work when i remove the middleware.isLoggedIn from the landing page
router.get("/landing",middleware.isLoggedIn, function(req, res) {
so is there is any expert in react can help me to do it :)
One way to save user data (user session) is "WebTokens". Yow can use JsonWebToken to let the server recognize the user and know some data about him. This data is hashed and stored with the cookies in the browser. Any query from the browser (the client) to the server will be carrying this data and the server will read it and use any useful data.
For more info on how to use it with your express server take a deeper look at this example.

Categories

Resources