How to send token from server to client? - javascript

I have express app, running on port 8000, I also have react on port 3000.
I am trying to implement google oauth.
This is what I did.
I try to send get request to my api endpoint,
then my express server redirect user to google Sign in.
And then, how can I send token from server to client from get request?
Here's my express code.
I try to send cookies directly from the server,
but I don't know why the cookies is not available on port 3000 which is my react app.
Is there any neat way to send jwt to client?
router.get(
"/google/callback",
passport.authenticate("google", {
scope: ["profile", "email"],
failureRedirect: "/login",
session: false,
}),
(req, res) => {
const payload = {
id: req.user.id,
};
jwt.sign(payload, secret, { expiresIn: tokenLife }, (err, token) => {
if(err) {
console.log('error', err)
}
const jwt = `Bearer ${token}`;
console.log('ini token', token)
const htmlWithEmbeddedJWT = `
<html>
<script>
// Save JWT to cookie
// document.cookie = 'token=${jwt};'
document.cookie = 'token=${jwt}; SameSite=None; Secure'
// Redirect browser to root of application
window.open('http://localhost:3000/login', '_self')
</script>
</html>
`;
res.send(htmlWithEmbeddedJWT);
});
}
);

It is not available because you have responded to the google call and then redirected the page on a client to the localhost apparently cookies will not be available.
The common way to handle auth in such cases is to define a success redirect that will expect to receive somekind of a token in query params.

Related

Update Vue.js website content after receiving a webhook?

I have a Vue 3 website showing a list fetched from a REST-API (using the fetch method).
Then sometimes this list is modified in the original DB and I would like to update my vue component accordingly.
The good news is that a webhook is posted to me by the DB service when an entry is modified.
However, I do not know how to consume this webhook in my Vue project.
I know how to consume it with an express server with something like
app.post("/hook", (req, res) => {
console.log(req.body)
}
but I don't see how to connect this with my Vue app ?
Maybe it's not even a good approach.
--- Update:
I have implemented #kissu solution (SSE version) but I still have an issue.
I have an Express server running :
```js
const app = express();
```
and i wait for a webhook:
app.post("/hook", (req, res) => {
x=req.body.my_item;
newUpdate = true;
res.status(200).end()
})
and I have a get route for the SSE
app.get('/events', async function(req, res) {
const headers = {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
'Access-Control-Allow-Credentials' : false,
'Access-Control-Allow-Origin': 'https://grange.vercel.app',
};
res.writeHead(200, headers);
while (true) {
await new Promise(resolve => setTimeout(resolve, 500));
if (newUpdate)
{
res.write(`data: ${x}\n\n`);
res.send(`data: ${x}\n\n`);
newUpdate = false;
}
}
});
and await app.listen(443);
On the Vue side I have
sseClient = this.$sse.create({
format: 'plain',
polyfill: true,
url: 'https://sse.mydomain.fr/events',
withCredentials: false,
});
sseClient.connect()
.catch((err) => console.error('Failed make initial connection:', err));
sseClient.on('', this.handleMessage)
and handleMessage do the job of updating my page.
It works for a while then after a minute of inactivity (or sometimes randomly) I receive an 404 error from the sseserver (on the Vue page) because "Access-Control-Allow-Origin is missing".
It looks like somehow, my Vue app cannot grab the header of the SSE sometimes and then fail.
Any suggestion #kissu ?
A client-side app cannot react to a webhook per se.
You will need some kind of polling or socket communication like:
long polling
server sent events
websockets

Where to store user session in Sapper app

I have started moving an app from React to Sapper. I am new to SSR architecture and want to know what the best way is to store the user session and data.
I am using Firebase for my auth and database. After using the client side firebase API to get the session keys and other user data how would I store the data? I have seen some tutorials making a user.js store, but in the Sapper docs I see it recommends using the session store. So which is better? And what would be the flow from client side to the server side session store?
E.g. If I were to make a login folder under which I have the svelte component and the server side route. Would there be a post "endpoint" that would set the session.user?
It's a bit tricky. I managed to get this working with both client and server using a authentication middleware
https://github.com/itswadesh/sapper-ecommerce/blob/master/src/server.js
The best way I have found so far is using JWT's:
Either get a JWT from a third party (Google, facebook, github) or sign your own.
server.js:
express()
.use(
compression({
threshold: 0
}),
sirv('static', {
dev
}),
cookieParser(),
bodyParser.json({strict: false}),
bodyParser.urlencoded({ extended: false }),
async (req, res, next) => {
const token = req.cookies['AUTH']
const profile = token && !dev ? await getBasicUserInfo(token) : false
return sapper.middleware({
session: () => {
return {
authenticated: !!profile,
profile
}
}
})(req, res, next)
}
)
then with every request just add 'credentials':'include to your requests to the server.
you will have to verify the token on every request but this method makes you app super scalable

Express js authorization value undefined

I have two ports opened: 3000 and 4000. I want to pass the authorization header from 3000 and receive it on port 4000.
I'm passing authorization value from 3000 port
app.get('/',function(req,res){
console.log('redirect');
res.header('Authorization', 'my sample');
res.redirect('http://localhost:4000/api/oauth2');
});
Receive on 4000 port
app.get('/api/oauth2', (req, res)=> {
console.log(req.headers.authorization); // undefined
res.end('i reached');
})
How would I receive this value from 3000 to 4000 port?
You can either return the '/' request with a .html file that makes a request on the client side, or you can set the auth token in the url query.
You will have to use request package from npm which is a http client.
npm install request
Import request module.
const request = require('request');
let options = {
url: "http://localhost:4000/api/oauth",
headers:{
authorization: "your_authorization_token"
}
request.get(options, (err, response, body)=>{
// Handle response
})

Passport.js does not authenticate SSR async request

I have setup Passport.js using the local-stratagey, with my express server.
When I am logged in and make an async request within NextJS's getInitialProps, it correctly permits the GET request via the client render but not the server side render. That is to say, If I access the private route via the client side routing, it shows the route and permits the aync request, but when I hit the page directly from the browser it permits the route but not the async request via the server side call of getInitialProps.
To add to the complexity, I am using Next.js and it's new api routes. I catch the route in server.js first to authenticate, and then if it's authenticated I pass it on the Next.
// server.js routes
app.get('/apples', function(req, res) {
if (req.isAuthenticated()) {
console.log(`[${req.method}]`, 'SERVER - ROUTE AUTHENTICATED');
return handle(req, res);
} else {
console.log(`[${req.method}]`, 'SERVER - ROUTE NOT AUTHENTICATED');
res.redirect("/login");
}
});
app.get('/api/apples', function(req, res) {
if (req.isAuthenticated()) {
console.log(`[${req.method}]`, 'SERVER - API AUTHENTICATED');
return handle(req, res);
} else {
console.log(`[${req.method}]`, 'SERVER - API NOT AUTHENTICATED');
res.status(401).end();
}
});
// /pages/apples.js - the consuming page
Apples.getInitialProps = async () => {
const response = await fetch('http://localhost:3000/api/apples');
const apples = await response.json();
return { apples }
}
Any help would be much appreciated!
This thread solved my problem: https://spectrum.chat/next-js/general/halp-nextjs-express-passport-next-api-routes~f5f60d4a-cfea-422b-8dfe-ed243b598ce6
The TL;DR is that if you hit the server directly, return the data you want directly from the server, rather than via a subsequent call to the api. The api request should only be used when you hit a page via client side routing.

Is this a good / secure way to set server side cookies from client

I am working with a single app application framework called reactjs, the issue I encountered is setting httpOnly cookies, as they can not be set / read from a client side I needed to figure out a way how to use express for this.
One idea I came up with is to make a post request to a route like /cookie:data where data is value of token that needs to be stored in a cookie, so:
app.post('/cookie:data', function(req, res) {
// Set cookie here
res.send(200)
})
Issue I am hesitant about is that token contains unique user identifier that is used to secure api, and I am not sure if I am or am not exposing this by setting up a cookie this way.
Alternatively instead of using :data it would be beneficial to figure out how I can grab data (json object) from the post request
EDIT:
One issue I can think of is that anyone can post to this route and set different cookies? what would be a way of securing it?
EDIT 2:
This is the express setup I use to proxy api calls (only relevant for clarifying comments)
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})
Say that you want to proxy all requests starting with /api to a third-party, except /api/users, which you want to perform 'manually' because it returns a token you need:
app.post('/api/users', function(req, res) {
let url = config.API_HOST + req.url;
let apiRequest = request.post(url, function(err, response, body) {
// Responses are just examples, you should tailor them to your situation
if (err) {
return res.sendStatus(500);
} else if (response.statusCode !== 200) {
return res.sendStatus(response.statusCode);
} else {
res.cookie('token', body).send('OK');
}
});
req.pipe(apiRequest);
})
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})

Categories

Resources