Set cookie after fetch request response from auth server - express

I just started learnign about the express library, im developing an app that access restricted info about a user using an Eve Online API, to do this the user is redirected to an authentication page to get an access and refresh token.
Everything works fine until I try to pull the 'refresh_token' from the response and set a cookie with it.
The error:
(node:20600) UnhandledPromiseRejectionWarning: TypeError: res.cookie is not a function
My code:
const url = 'https://login.eveonline.com/v2/oauth/token';
const options = {
'method':'POST',
headers:
{
'authorization':'Basic '+ process.env.ENV_PASS,
'Content-Type':'application/x-www-form-urlencoded',
'host':'login.eveonline.com'
},
body:''
}
function send_get_request(req, res){
options.body =`grant_type=authorization_code&code=${req.query.code}`
fetch(url,options,{credentials:'include'})
.then(handle_stream)
.then(handle_json)
.then(set_cookie)
.then(redirect_home)
}
function handle_stream(res){
return res.json() //handless data stream, returns responce
}
function handle_json(res){
return res.refresh_token
}
function set_cookie(res){
return res.cookie('refresh_token','hello')
}
function redirect_home(res){
res.redirect('http://localhost:3000/home')
}
router.get('/',send_get_request)
I tried breaking the .then() block but still res.cookie doesn't exits. Also tried using credentials but it doesn't work.

Your code contains two variables res: One stands for the response that your middleware sends back to the client, the other stands for the response you are receiving from the fetch operation. Only the handle_stream function operates on the latter, whereas set_cookie and redirect_home operate on the former.
The last three function are also synchronous and need not be chained with .then. The are invoked synchronously in a function that takes as parameter the json which handle_stream produces asynchronously.
function send_get_request(req, res){
options.body =`grant_type=authorization_code&code=${req.query.code}`;
fetch(url,options,{credentials:'include'})
.then(handle_stream)
.then(function(json) {
var refresh_token = handle_json(json);
set_cookie(res, refresh_token);
redirect_home(res);
});
}
It is all perhaps easier to understand with the async-await syntax:
async function send_get_request(req, res){
options.body =`grant_type=authorization_code&code=${req.query.code}`;
var response = await fetch(url,options,{credentials:'include'});
var json = await handle_stream(response);
var refresh_token = handle_json(json);
set_cookie(res, refresh_token);
redirect_home(res);
}

Related

How does redirecting from a post method to get method work?

The server doesn't recognize any get request except for the post method after executing some queries in the mongodb.
The express middleware takes the post method and after interacting with the database and using the res.redirect() to get to other get methods, the server doesn't recognize the request at all. I tried using res.all(). This showed that the request was seen but no action was taken.
var express = require('express');
var router = express.Router();
var Product = require('../models/product');
router.get('/', function(req, res, next) {`//homepage
res.render("index");
}
router.post('/add',function(req,res next){
//Product model
var prod = new Product({
//data here
});
prod.save(function(err,res2){
if(err){
console.log(err);
return res.redirect('/error');
}
else{
mongoose.disconnect();
console.log("Complete1");
return res.redirect('/');
console.log ("Complete2);
}
});
}
After I get to the post method it should redirect to the homepage
The problem may not be with the backend, but with the frontend. If you are using AJAX to send your POST request, it is specifically designed to not change your url.
Use window.location.href after AJAX's request has completed (in the .done()) to update the URL with the desired path, or use JQuery: $('body').replaceWith(data) when you receive the HTML back from the reques

How do i call third party API data via fastify?

I had a small node server and I use the framework fastify.
In one of my routes, I want to get the data from a third party API.
I tried the following snippet:
fastify.route({
method: 'GET',
url: 'https://demo.api.com/api/v2/project/',
handler: async function ({ params, body}, reply) {
if (!body) return reply.send({ sucess: false })
console.log('testing')
console.log(body)
return reply.send({ sucess: true })
}
})
Unfortunately, I cannot call the URL by get because GET url's can only start with '/'.
How do i call a third pary api via fastify? do i need a extention?
If you need to define a route (like http://localhost:3000/) that proxies another server you need to use fastify-http-proxy.
Or if you need to call another endpoint and manage the response, there is the fastify.inject() utility but it is designed for testing.
Anyway, I think the best approach is to use some HTTP client like got
const got = require('got') // npm install got
fastify.get('/my-endpoint', async function (request, reply) {
const response = await got('sindresorhus.com')
console.log(response.body)
// DO SOMETHING WITH BODY
return { sucess: true }
})
Proxy your http requests to another server, with fastify hooks.
here is the example in fastify-http-proxy
server.register(require('fastify-http-proxy'), {
upstream: 'http://my-api.example.com',
prefix: '/api', // optional
http2: false // optional
})
https://github.com/fastify/fastify-http-proxy/blob/master/example.js

When to refresh a JWT token?

Currently I am using JWT-Auth on my Laravel back-end to protect my API routes with a token. However, after a certain time the token gets invalid and I get the error 401 Unauthorized. So I guess I have to refresh the token somewhere. When would be the best time to do this? I read about doing it every time you make a request but I want to be sure that’s the right way to do so. I used this guide from their docs: https://jwt-auth.readthedocs.io/en/develop/quick-start/#create-the-authcontroller. In here they make a function to fresh a token. But how would I implement this every time I make a request? Do I just call this function in the controller with an Axios request or call it in another controller or something? Any tips are greatly appreciated.
I have a Vue.js front-end by the way.
With Tymon/JWTAuth you have two options:
You can add the jwt.refresh middleware to your api routes, which will refresh the token everytime a request is made. The downside of this solution is that this could be abused. The upside is that you do not really need to worry about the token in your application, especially if you do not have a frontend or do not develop the frontend yourself.
You parse the token client side. The first two parts of a jwt token are completely public and are base64-encoded. You don't really need to know if this token was signed by the server client-side, so you can safely ignore the last part. This solution is relatively easy if you have a wrapper around api calls that handles common logic for api calls (e.g. adding the authorization header to begin with).
const token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJpYXQiOjE1NTUzNDkxMjYsImV4cCI6MTU1NTM3NzkyNiwibmJmIjoxNTU1MzQ5MTI2LCJqdGkiOiJtZEdTNGE2ZDJnNHM5NzRnNSJ9.TygbG5smlhAapE8fy4rgXlLVYW-qOcWtLYnnbgJCIKg";
function shouldRefreshToken(token) {
const currentTime = 1555350309829; // Date.now()
const universalTimestamp = currentTime / 1000;
const gracePeriod = 60 * 60 * 8; // 8 hours
const tokenParts = token.split('.');
const payload = JSON.parse(atob(tokenParts[1]));
if (payload.iat > universalTimestamp) {
console.log("This monstrosity was issued in the future O_o");
}
if (payload.nbf > universalTimestamp) {
console.log("This token is not valid yet. Refreshing it does not yield anything useful. Maybe we still have some previous token?");
}
if (payload.exp < universalTimestamp) {
console.log("This token has expired. We should try to refresh it before doing anything else.");
} else if (payload.exp - gracePeriod < universalTimestamp) {
console.log("This token is about to expire. We can refresh it asynchronously.");
} else {
console.log("Nah, we are fine!");
}
}
shouldRefreshToken(token);
In the end you would want to send a request to a refresh endpoint that does something like this, which is then parsed by the frontend:
$myNewToken = JWTAuth::refresh(JWTAuth::getToken());
response()->header('Authorization', "Bearer {$myNewToken}");
To get it to work, you can do something like this:
import store from '../store';
import { shouldRefreshToken } from '../helpers/auth';
const someBaseUrl = 'https://example.com';
export function request(options = {}) {
// Hopefully you rewrite that function above to return a boolean ;-)
if (shouldRefreshToken(store.state.auth.token)) {
refreshToken();
}
const config = {
method: options.method,
url: `${someBaseUrl}/${options.resource}`,
credentials: 'include',
headers: {
...(options.headers || {}),
Authorization: `Bearer ${store.state.auth.token}`,
'Content-Type': 'application/json'
},
data: options.data
}
return axios(config).then(parseResponse)
}
function parseResponse(axiosResponse) {
// Probably want to get the token and do something with it
}
function refreshToken() {
axios({
method: 'POST',
url: `${someBaseUrl}/refresh`
}).then(parseResponse)
}

Unable to get access Token linkedin Oauth

I know some will put comment like this post is duplicate of so many questions, but I've tried many ways to achieve Access Token in linkedin Oauth. Explaining what i tried.
1) I'm following it's official doc's Linkedin Oauth2
2) I'm successfully getting Authorization code from step 2 and passing that code to step 3 for exchanging Auth code for getting Access Token. But i'm getting following error
{"error_description":"missing required parameters, includes an invalid parameter value, parameter more than once. : Unable to retrieve access token : appId or redirect uri does not match authorization code or authorization code expired","error":"invalid_request"}
3) According to some links i need to set content-type in the header.Link which tells to set content-type is missing
4)Then i tried calling https://www.linkedin.com/uas/oauth2/accessToken this service instead of POSt to GET. And passing data as queryParams.
5) Some link says oauth code expires in 20 sec, So i've checked, i'm making call for access token in less that 1 sec.
6) And if i pass data in Body params like as below and used url as https://www.linkedin.com/uas/oauth2/accessToken
var postData = {
grant_type: "authorization_code",
code: authCode,
redirect_uri: 'https%3A%2F%2Foauthtest-mydeployed-app-url',
client_id: 'my_client_id',
client_secret: 'secret_key'
};
7) With Get call my url i tried
https://www.linkedin.com/uas/oauth2/accessToken?grant_type=authorization_code&code='+authCode+'&redirect_uri=https%3A%2F%2Foauthtest-mydeployed-app-url&client_id=my_client_id&client_secret=secret_key
Still i'm getting Error even though status code is 200, i'm getting that error(with GET api)
and If POSt by passing postData in body i'm getting bad request 400 status code
Not understanding why m I not getting access code. I've read many solutions.
Sharing code as requested.
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageToast"
], function (Controller, MessageToast) {
"use strict";
return Controller.extend("OauthTest.OauthTest.controller.View1", {
onPress: function (evt) {
var sPath =
'https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=my_client_id&redirect_uri=https%3A%2F%2Foauthtest-mydeployed-app-url&state=DCEeFWf45A53sdfKef424&scope=r_basicprofile';
window.location.href = sPath;
var oRouter = new sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("View2", {
"username": "Test"
});
MessageToast.show(evt.getSource().getId() + " Pressed");
},
//after user allows access, user will be redirected to this app with code and state in URL
//i'm fetching code from URL in below method(call is happening in max.569ms)
onAfterRendering: function () {
var currentUrl = window.location.href;
var url = new URL(currentUrl);
var authCode = url.searchParams.get("code");
if (authCode !== undefined && authCode !== null) {
var postData = {
grant_type: "authorization_code",
code: authCode,
redirect_uri: 'https%3A%2F%2Foauthtest-mydeployed-app-url',
client_id: 'my_client_id',
client_secret: 'secret_key'
};
/* var accessTokenUrl = 'https://www.linkedin.com/uas/oauth2/accessToken?grant_type=authorization_code&code=' + authCode +'&redirect_uri=https%3A%2F%2Foauthtest-mydeployed-app-url&client_id=my_client_id&client_secret=secret_key';*/
var accessTokenUrl = 'https://www.linkedin.com/uas/oauth2/accessToken';
$.ajax({
url: accessTokenUrl,
type: "POST",
beforeSend: function (xhr) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
},
data: postData,
success: function (data, textStatus, jqXHR) {
console.log(data);
alert('success');
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(errorThrown);
alert('error');
}
});
}
}
});
});
Help will be appriciated..!!!
Finally I am happy to post my answer after so much search.
Every step I did is correct only, but one thing I was missing here like, Linkedin API doesn't supports CORS.
I tried implementing Javascript SDK, that works like charm. But API wasn't.
Then I found very helpful Link which says I need to implement Rest API from backend by allowing CORS, not from front end.
Make sure to follow all the points which I mentioned above in my post.
And for Allow CORS follow this link. You will get data but only basic profile of user according to LinkedIn Terms data can be accessible
Hope this post may help someones time to search more

Where do you place NPM's request code?

I want to use the request module in my express app, but I am not sure where the actual requests code goes.
Usage:
When a user loads a page, make a GET request and populate the page with data.
When a users clicks on a item from a table, make a GET request.
When a user fills out a form, POST.
I tried searching for answers but it seems to be implied that the developer knows where to place the code.
Example of a code snippet using request that I am unsure where to place in the express app:
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
}
})
I am guessing that I should not place the code in the server.js file especially if I am going to be making many different calls, but that's what it looks like others are doing on StackOverflow.
Does the request belong in a model?
If you are doing this in response to a user interaction, like clicking on something you can just do it from the route handler. Below, I just return the results to the client, or I pass an error to the next handler in the chain.
var request = require('request');
var express = require('express');
var app = express();
app.get('/click', function(req, res, next){
request('http://www.google.com', function (error, response, body) {
if (error || response.statusCode != 200)
return next(err);
response.send(body) // return the html to the client
})
});
app.listen(3000);
In bigger apps you might move routes into separate modules.