Express js - can't redirect - node.js

I am trying to do the following:
from client
var req = jQuery.post(
"http://www.example.com:3000"+"/dologin",
{"username" : username, "password" : password}).error(function(){
alert("an error occurred");
});
in express root
app.post('/dologin',function(req, res) {
res.redirect('http://bbc.co.uk');
});
result passed back
<p>Moved Temporarily. Redirecting to http://bbc.co.uk</p>
Seems that if I do post from jquery the redirect will not work. Does anyone know a way to force it to redirect?

Browser does not redirect the window on redirect on ajax response. Redirect the browser with javascript.
In server send the new site as content, for example.
res.contentType('application/json');
var data = JSON.stringify('http://site.example.com/')
res.header('Content-Length', data.length);
res.end(data);
In client
var req = jQuery.post(
"http://www.mysite.com:3000"+"/dologin",
{"username" : username, "password" : password}, 'json').error(function(){
alert("an error occurred");
}).success(function(data) {
window.location = data;
});

I've actually encountered the same thing when developing an app. It seems Express doesn't redirect if the method is post.
Try:
app.post('/dologin',function(req, res) {
req.method = 'get';
res.redirect('http://bbc.co.uk');
});

I'm doing something like this when using OAuth2. I have an link to one of my pages and this in turn redirects to google.
To redirect to another location the following code does the actual redirect
app.get('/GoogleRequestAuthorization.html',function(req,res) {
.
.
.
.
res.writeHead(302, {location: url});
res.end();
});
url being the address you want to redirect to.
The full function is...

I have come to a similar problem and solved it by checking the type of the request. In my case I use JSON, but it works for other POST requests as well:
var ajax = req.xhr;
if(ajax) {
res.status(401).json({'msg':'redirect','location':'/login'});
}
else {
req.method = 'get';
res.status(401).redirect('/login');
//Or if you prefer plain text
//res.status(333).send("Redirect");
}
This handles both Ajax POST and AJAX and standard GET requests.
On the client, in the Ajax respose callback:
$.ajax({
type: 'POST',
data: Msg,
url: '/some/post',
dataType: 'JSON'
}).success(function(data, textStatus, req ) {
if(data.msg==="redirect") window.location = data.location;
else {...}
}).error(function(data, textStatus, req) {
if(req=="Unauthorized") {
alert("Unauthorized!");
window.location = "/login";
} else if (data.responseJSON.msg==="redirect") window.location = data.responseJSON.location;
else {
//...
}
});
You can actually handle more statuses here, except for 302, which is automatically followed by JQuery and you get as response 200 from the page you wanted to redirect to. So I avoid sending the 302, sending 401 in my case, or any other status, for example 333, which will be handled as error.

Related

nodejs req.body undefined in case of ajax post requests

Strange issue and I have no explanation to that. I create ajax post request
$.ajax({
type: "POST",
url: "/like",
data: {"topic": "123123"},
success: function(dataString) {
console.log('123123');
}
});
Or like this:
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "/like", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send("fname=Henry&lname=Ford");
on server side (NodeJs) I have simple code
app.post('/like', function (req, res) {
console.log(req.body);
res.status(201).send('+1')
});
what I dont understand, is why I receive all the time undefined when I send ajax post request.
You need to use a body parser, see: nodejs.dev/learn/get-http-request-body-data-using-nodejs

nodejs express + create-react-app oauth2.0 error 401 'Invalid access token' Spotify API

I'm trying to build an app that allows me to call Spotify's API from a create-react-app client through a nodejs express server. I'm trying to use the Authorization Code Flow.
It works getting the authorization code using the following code to generate the URL, completely on client side (if and how using server-side is another question):
getSpotifyCodeUrl() {
const authEndPoint = 'https://accounts.spotify.com/authorize'
const clientId = CLIENT_ID;
const responseType = 'code';
const redirectUrl = 'http://localhost:3000/';
// TODO: state for cross-site request forgery protection
// cont state = '...';
const scope = 'user-read-private user-read-email';
return(
authEndPoint +
'?response_type=' + responseType +
'&client_id=' + clientId +
(scope ? '&scope=' + encodeURIComponent(scope) : '') +
'&redirect_uri=' + encodeURIComponent(redirectUrl)
)
}
The user simply clicks a link with the href as generated above.
{!this.state.token ? <a className="btn btn--loginApp-link" href={this.getSpotifyCodeUrl()}>
Login to Spotify
</a> : ""}
After the user gets redirected back, I use the following function to extract the authorization code from
componentDidMount() {
this.setState({code: new URLSearchParams(window.location.search).get('code')});
}
With the code I retrieve the access token. Call from client:
getSpotifyAccessToken() {
fetch('/auth?code=' + this.state.code)
.then((res) => res.json())
.then(data => {
this.setState({token: data.token});
localStorage.setItem('token', this.state.token);
});
}
API call on server:
app.get("/auth", (req, res) => {
let code = req.query.code;
let authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: 'http://localhost:3000/',
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer.from(clientId + ':' + clientSecret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body){
if (!error && response.statusCode === 200) {
token = body.access_token;
res.json({ token: "Token: " + body.access_token});
} else {
console.log("/auth response body")
console.log(body)
}
});
});
Strange thing is I get a token, but can also see the following error in my server terminal:
{
error: 'invalid_grant',
error_description: 'Invalid authorization code'
}
If I then try to use the token to do a (simple) request from client:\
getSpotifyMe() {
fetch('/me?token=' + this.state.token)
.then((res) => res.json())
.then(data => {
console.log(data);
});
}
And corresponding server call:
app.get("/me", (req, res) => {
let token = req.query.token;
console.log("Token: " + token);
let options = {
url: 'https://api.spotify.com/v1/me',
headers: { 'Authorization': 'Bearer ' + token },
json: true
}
request.get(options, function(error, response, body) {
console.log("/me request body");
console.log(body);
if (!error && response.statusCode === 200) {
res.json(body);
} else {
}
})
})
Which gives me a 401 error:
{ error: { status: 401, message: 'Invalid access token' } }
I've tried some things. Doing the call from client, no success. Refreshing tokens, deleting cookies, authorizations from account, but no success. The strange thing is I can use the token said to be invalid in the Spotify Web Console, doing the exact same call I'm trying to do in the application.
Do you know where I'm causing these errors (invalid_grant and 401) in my application? And how I could solve them?
I ended up following the example on Spotify GitHub more closely.
I changed my create-react-app to simply call the /login server route. Don't use fetch like I tried, you'll end up with a cross-origin error once the server calls the Spotify API from a different origin. For some unapparent reason I can't use href="/login", the server simply doesn't respond, but that's fruit for another SO question.
Login to Spotify
The server index.js is now simply the Authorization Code Flow app.js with my own variables and one minor tweak.
My redirect_uri is http://localhost:3001/callback, I struggled long and tiresome with redirecting back to my client create-react-app at http://localhost:3000, perhaps this is where something goes wrong with the auth code and access token, IDK. You wanna go straight into the server-side callback. Much more intuitive for the user as well: one click and bam, logged in, no messy redirects in between.
In the /callback route on the server, when the access_token and refresh_token have either been successfully retrieved or not, that's when you want to redirect back to your client, http://localhost:3000 for me. With the tokens of course. The example uses URL params, I'm guessing setting a cookie should also work, but have to do some research if that's a security no-go.
The small little tweak I made is to the CORS clause for the app. There's no need for incoming request at the server other than coming from my client, so I added a {origin: 'http://localhost:3000'} there, just in case.
app.use(express.static(__dirname + '../client/build'))
.use(cors({origin: 'http://localhost:3000'}))
.use(cookieParser());
That's it, works like a charm, I can see the response body of the /v1/me call coming in at the server (there's a console log in the example code) and tokens are coming back to the client.

Node.js service: Getting "Cannot GET /<path>" after POST request in microservice (works locally)

I've created a node.js microservice using the libraries express and request.
The idea is to forward a post-request by the client application from the first endpoint ("http://example.com/proxy/x") to the second one ("http://example.com/x"). If I do the request to /proxy/x via POSTMAN locally to my "localhost:/proxy/x" instance, it works just fine. The request gets the expected response without any problem. If I push it to the cloud environment using a containering system and ask the endpoint via "http://example.com/proxy/x" it says "CANNOT /GET /x" and fails with a 404 error.
To cover the "404" error, I've even created a app.get listener on "/proxy/x".
Now it returns "error = true". That means my POST is turned to a GET call.
The url is already hardcoded, before I've generated it with a combination of req.protocol and req.get('host'). Seems to generate a valid url, still does not work.
POST http://example.com/proxy/target
app.post('/proxy/target', (req, res) => {
// const currentURL = req.protocol + '://' + req.get('host');
const requestBody = req.body;
request.post({
url: 'http://example.com/target',
followAllRedirects: true, // I've also tried to outcomment it
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic user:password',
},
form: requestBody
},
(err, response, body) => {
if (err) {
return console.log(err);
} else {
// console.dir(response);
return res.status(response.statusCode).send(response.body);
}
}
);
});
app.route('/target')
.post(function (req, res) {
//some code
});
Expected result should be the response body the call delivers, if I'm callign /target directly.
{
'success':true
}
It actually is responding with:
Cannot GET /x/
As it works perfectly on my localhost, it might be the case that my cloud dev environment is the problem.
I would be glad for additional ideas. The cloudsystem is a containering environment called CloudFoundry.
Update:
It seems like CloudFoundry is using NGINX per default: https://docs.cloudfoundry.org/buildpacks/nginx/index.html
Other developers had similar issues when using NGINX: https://www.digitalocean.com/community/questions/post-request-redirects-to-get-in-nginx-proxy-and-nodejs

IIS authentication for http request with Nodejs

I have one problem with HTTP GET/POST request.
When I use the DHC/Postman, send the parameters to the URL + endpoint, works perfectly. 200 is returned.
But with code, like my example, show one 401 error.
I have searched about that and the problem is with the auth, not sure, see... Maybe is the same.
With this explanation, need to set the Authorization, I think. But the problem is when I access the site, the auth is automatic, see:
My code:
var jsonObject = JSON.stringify({ "UserName": login});
// prepare the header
var postheaders = {
'Content-Type' : 'application/json',
'Content-Length' : Buffer.byteLength(jsonObject, 'utf8')
};
// the post options
var optionspost = {
host: "xxxxxxxxxx.com",
// path: '/Home/endpoint', //send the data for the endpoit with Postma works fine
method: 'POST',
headers : postheaders
};
console.info('Options prepared:');
console.info(optionspost);
console.info('Do the POST call');
// do the POST call
var reqPost = http.request(optionspost, function(res) {
console.log("statusCode: ", res.statusCode);
// uncomment it for header details
// console.log("headers: ", res.headers);
res.on('data', function(d) {
console.info('POST result:\n');
process.stdout.write(d);
console.info('\n\nPOST completed');
});
});
// write the json data
reqPost.write(jsonObject);
reqPost.end();
reqPost.on('error', function(e) {
console.error(e);
});
Obs.: This website it's from my Company (.NET) and is Integrated with IIS (Active Directory login users for authenticate), when I access, automatically is logged... I really don't know how to solve this.
Obs II.: I Try to use one anonymous new tab and use DHC online, and my post doesn't work. This application just works inside network company and with Client side (Using postman with my computer).
Obs III.: The request is from Server and the login from my server have all permissions to access this site, and when I request, is like I'm anonymous, but if I did the same with REST Client/Postman, works perfectly. I need that it works with http request from my Server.
You can use a module like ntlm-webapi which will allow you to use NTLM auth. That way the request will go through. Just make sure the user you use is authorized for that server.
var Request = require('ntlm-webapi');
var request = new Request({
url: "http://some.restful.api.org/you/want/to/call",
username: 'username',
password: 'password',
domain: 'company_domain'
});
request.get(function(err, result){
if (err) console.log (err);
console.log (result);
});
It seems that you forgot to add the Authorization header in your code
// prepare the header
var postheaders = {
'Authorization' : 'Negotiate '+ yourAccessKey,
'Content-Type' : 'application/json',
'Content-Length' : Buffer.byteLength(jsonObject, 'utf8')
};

Attaching JSON web token to the http requests

I have a token named userToken, it's in localstorage. I need to attach this token to http requests (get/post) since I check whether a user is logged in or not using this token.
I implemented it for jQuery Ajax post requests, now I can work with them, but I have no idea how to attach it to all http requests (for instance, a simple get request), with or without ajax.
Here's what I've done so far:
Jade
#click Click here
script.js (Client Side)
var _token = localStorage.getItem('userToken');
$.ajaxSetup({
headers: { 'token' : _token }
});
$(document).ready(function () {
$('#click').click(function () {
$.post("/testurl2", {}, function (data) {
console.log(data);
});
});
});
NodeJS
app.get('/testurl1', function(req,res) {
var myPreciousToken = req.headers.token;
console.log(myPreciousToken); // it doesn't work.
});
app.post('/testurl2', function(req,res) {
var myPreciousToken = req.headers.token;
console.log(myPreciousToken); // it works
});
Try to change your ajaxSetup to
$.ajaxSetup({
//headers: { 'token' : _token },
beforeSend: function(xhr){
xhr.setRequestHeader('token', _token );
}
});

Resources