Problem Interacting With Mojang Authentication Server - node.js

I am trying to get a client token from the Mojang Authentication API, which can be found here https://wiki.vg/Authentication. However, whenever I try to make a request, I get the following response:
{error: 'ForbiddenOperationException', errorMessage: 'Forbidden'}
The API indicates this is because my credentials are invalid but the errorMessage that I am getting does not match any of their examples. I tried doing the same request through python's Requests module, and it worked well, which leads me to believe I am not sending my https request properly. I am aware there is probably something very basic I am overlooking, but I would appreciate it if someone tells me what I am doing wrong.
Here is my code:
Python Code that works:
import requests
url = 'https://authserver.mojang.com/authenticate'
data = {"username":"--username--", "password":"--password--"}
res = requests.post(url, json=data)
print(res.json())
Javascript Code that Doesn't Work:
var https = require('https');
var options = {
host: 'authserver.mojang.com',
path: '/authenticate',
method: 'POST',
headers: {"username":"--username--","password":"--password--"}
}
https.request(options, (res)=>{
var body = ''
res.on('data', (d)=>{
body+=d;
});
res.on('end', ()=>{
resp = JSON.parse(body);
console.log(resp);
});
}).end();

The problem is that you're sending your credentials as HTTP headers directly instead of as POST data. Try this instead:
var https = require('https');
var data = JSON.stringify({"username":"--username--","password":"--password--"})
var options = {
host: 'authserver.mojang.com',
path: '/authenticate',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
var req = https.request(options, (res)=>{
var body = ''
res.on('data', (d)=>{
body+=d;
});
res.on('end', ()=>{
resp = JSON.parse(body);
console.log(resp);
});
});
req.write(data);
req.end();

Related

Core https library vs. npm 'request' library

I am running into a very strange issue when trying to use the built-in node https library.
Request Headers:
let requestDetails = {
hostname: 'api.domain.com',
method: 'POST',
path: '/endpointIWant/goHere
headers: {
'Client-ID': clientId,
'Content-Type': 'application/json',
Authorization: bearerToken
},
};
Request body:
let body = JSON.stringify({
"content_type": "application/json",
"message" : message
});
This is my standard call using the default https library of node:
let req = https.request(requestDetails, function (res){
let responseBody = undefined;
res.on('body', function(res) {
responseBody = '';
});
res.on('data', function(chunk) {
responseBody += chunk;
});
res.on('end', function() {
console.log(responseBody);
});
});
req.write(body);
req.on('error', function(e) {
console.log(e);
});
req.end();
Now whenever I send this request to the relevant server I get a:
Your browser sent a request that this server could not understand.
Reference #7.24507368.1554749705.3185b29b
However when I use the popular 'request' library on NPM it works fine and I get the response I expect.
This leads be to believe there is something different in maybe the 'encoding' or 'chunking' of the requests between these two libraries, but I cannot figure out what.
Does anyone have experience with the Node https library and understand any gotcha's there?
I prefer to use built-in libraries as much as possible to keep my package size low.
When using native http or https modules, you need to use the querystring module to stringify your body.
const querystring = require('querystring');
let body = querystring.stringify({
"content_type": "application/json",
"message" : message
});
//also include the content length of your body as a header
let requestDetails = {
hostname: 'api.domain.com',
method: 'POST',
path: '/endpointIWant/goHere
headers: {
'Client-ID': clientId,
'Content-Type': 'application/json',
'Content-Length' : body.length
Authorization: bearerToken
},
};
'request' is built on top of the native modules and does this internally when you pass it a json body

Setting request headers in Node.js

I have been working with node.js to set up a proxy server that will handle incoming client request and will verify that they have the proper certificates to get connected to the server.
What I want to do is to be able to add the client's certificate to their header to craft a user name, that I will pass on to the server.
function (req, res) {
//Here is the client certificate in a variable
var clientCertificate = req.socket.getPeerCertificate();
// Proxy a web request
return this.handle_proxy('web', req, res);
};
What I want to be able to do is this : req.setHeader('foo','foo')
I know that the proxy.on('proxyReq) exist, but the way the code is set up, I need to be able to use the req parameter.
Is there a way to do this?
Please let me know if I need to clarify my question.
You can craft your own http request with the headers provided in the original request plus any extra headers that you'd like by using http.request. Just receive the original request, copy the headers into the new request headers, add the new headers and send the new request.
var data = [];
var options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
data.push(chunk);
});
res.on('end', function() {
console.log(data.join(""));
//send the response to your original request
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// Set headers here i.e. req.setHeader('Content-Type', originalReq.getHeader('Content-Type'));
// write data to request body
req.write(/*original request data goes here*/);
req.end();

Twitter 1.1 user_timline with Bearer Authorization returns 400 in node.js

I have a bearer token and try to get a user's timeline from behind a proxy with the following code.
var parsedUrl = url.parse( 'https://api.twitter.com/1.1/statuses/user_timeline.json:443/?count=2&screen_name=twitterapi', true, true );
var options = {
'host': parsedUrl.host,
'path': parsedUrl.path,
'method': 'GET',
'headers': {
'Host': parsedUrl.host,
'Authorization': 'Bearer ' + settings.accessToken
}
};
var adapter = https;
if(settings.proxy)
{
options.host = settings.proxy;
options.port = settings.proxyPort;
options.path = parsedUrl.path;
options.headers['Proxy-Connection'] = 'Keep-Alive';
adapter = http;
}
var body = '';
var req = adapter.request(options, function(res) {
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
try
{
var result = JSON.parse( body );
}
catch(ex)
{
//...
}
});
});
req.end();
This request always return a 400 with no body or error message. If try the acces with cUrl it works like a charm. I retrieve the bearer token in the same way. Why does Twitter respond with a 400?
curl --get 'https://api.twitter.com/1.1/statuses/user_timeline.json' --data 'count=2&screen_name=twitterapi' --header 'Authorization: Bearer <token>' --verbose --proxy http://proxy.example.com:8080
What does cUrl differently?
EDIT: The status code is NOT 404 but 400! Sorry.
EDIT2: When I leave the Authorization header out I still get 400 even though I expect a authorization failed message. Is any special enconding needed?
EDIT3: Now the status code is 401. Something smelly goes on :(
Here is the trick: You have to tunnel the HTTPS connection on the HTTP request using CONNECT to get through the proxy. The initial way is wrong.
var http = require('http');
var https = require('https');
var connectReq = http.request({ // establishing a tunnel
host: 'localhost',
port: 3128,
method: 'CONNECT',
path: 'github.com:443',
}).on('connect', function(res, socket, head) {
// should check res.statusCode here
var req = https.get({
host: 'github.com',
socket: socket, // using a tunnel
agent: false // cannot use a default agent
}, function(res) {
res.setEncoding('utf8');
res.on('data', console.log);
});
}).end();
See following node.js bug/issue for details: HTTPS request not working with proxy

Req.data not showing up once sent to server with POST method (using connect middleware)

I'm unable to get the var data I sent in via a POST method. This should be easy (right?), but I'm clearly missing something (either conceptually or a setting).
At this stage, I simply want to check to see if the server side code will output the data to the console. The array is being stringify-ed correctly, eg. ['one','two','three'] becomes 0=one&1=two&2=three
but I can't pull it out on the server side.
What am I missing?
Client side
var qs = require('querystring')
, http = require('http');
var some_array = ['one','two','three'];
var data = qs.stringify(some_array);
var options = { host: 'localhost',
path: '/search',
port: '3000',
method: 'POST',
headers: { 'content-length': Buffer.byteLength(data),
'Content-Type': 'application/json' }
}
function go_post(data) {
req = http.request(options, function(res) {
// do something with response
});
req.write(data);
req.end();
};
go_post(data);
Server side
var connect = require('connect');
var qs = require('querystring');
var server = connect.createServer();
server.use(function(req,res,next) {
if ( '/search' == req.url && req.method == 'POST' ) {
// quick check to see if data came through
console.log('BODY IS ' + req.data);
} else {
next();
};
});
These objects arent available because they are still in the "raw" request. You have to use a middleware like connect().use(connect.bodyParser()) in order to get them from the request via req.data.

Twitter Streaming API - Node.js returning unauthorised error (OAuth)

I'm attempting to connect to Twitters Streaming API over OAuth using http.get although I'm having a slight problem.
The script keeps returning unauthorised
The code I'm using follows, can anybody tell me if I'm missing something stupid or my headers are incorrect.
var https = require('https');
var options = {
host: 'stream.twitter.com',
path: '/1.1/statuses/filter.json?track=bieber',
method: 'GET',
headers: {
authorization: '
OAuth
oauth_consumer_key = "",
oauth_nonce = "",
oauth_signature = "",
oauth_signature_method = "HMAC-SHA1",
oauth_timestamp = "",
oauth_token = "",
oauth_version = "1.0"
'
}
};
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log(chunk);
});
});
req.on('error', function(e) {
console.log('Oops... ' + e.message);
});
req.write('data\n');
req.write('data\n');
req.end();
The problem I had here was that the OAuth request was NOT being signed, which ment the authorisation was failing.
OAuth is a complicated process and it's best to use a library or NPM module that has already been developed.
The particular NPM I used in this instance was node-oauth
try this:
var options = {
host: 'stream.twitter.com',
path: '/1.1/statuses/filter.json?track=bieber',
method: 'GET',
auth : "YOUR_ID:YOUR_PASS"
};
var https = require('https');
https.get(options,function(res){
res.on("data",function(trunk){
//YOUR CODE
}).on("end",function(){
//YOUR CODE
}).on("error",function(e){
//YOUR CODE
});
}

Resources