401 unauthorized except through browser - javascript

I've been trying to do an HTML GET request, using a URL that works in the browser but somehow results in a 401 Unauthorized error when I run my code. The problem is that I did provide authentication; the URL is of the form
http://username:password#url?param=value
It succeeds in Firefox and Chrome (I also went through Incognito mode to avoid cookies), as well as Google's Postman app, but despite trying several other HTTP GET request methods they all return unauthorized error. I've run it through REST API and XMLHttpRequest, as well as command line.
Any ideas on what could be causing this? Or, even better, if someone's had a similar problem and has a solution?
(Note: I'm pretty new to this whole thing, not sure if I was clear/detailed enough. I'll do my best to elaborate if anyone needs.)
Edit: Here's some idea of the original code I was running:
var func = function() {
var options = {
baseUrl: SERVER,
uri: '/device.cgi',
qs: {
param1: value1,
param2: value2
}
};
request(options, function(err, response, body) {
if (err) console.log(err);
else console.log(body);
});
};
I also ran
function httpGet(theUrl)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
from this question and got the same Unauthorized result.
And because apparently the device I'm using is fine with POST as well,
var func = function () {
var formData = {
form: {
param1: value1,
param2: value2
}
};
request.post(SERVER + 'device.cgi', formData, function (err, response, body) {
if (err) {
console.log(err);
}
else console.log(body);
}).auth(userID, userPass);
};
still with exactly the same result.

http://username:password#url?param=value
The browser is taking that URL, and creates the Basic HTTP Authorization header for you.
Node.js does not do that for you, and thus you must set the Authorization header yourself in code.
var http = require('http');
var options = {
host: 'www.tempuri.org',
path: '/helloworld/',
headers: {
'Authorization': 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'
}
};
http.request(options, function(response) {
var body = '';
response.on('data',function(c) {
body+= c;
})
response.on('end',function() {
console.log(body)
})
}).end();
The following links discuss basic auth, how the browser encodes the data in the URL, and how you could implement it yourself in Node.js:
Basic access authentication (Wikipedia)
Can you pass user/pass for HTTP Basic Authentication in URL parameters? (serverfault)
Basic HTTP authentication in Node.js using the request module (Hay Kranen)

Related

Add authentication header via XMLHttpRequest Web API

There is a need to write an interceptor for XMLHttpRequest Web API, I have written it to this stage
const { serverUrl, bearerToken } = this.config;
const XMLHttpRequestOpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function (
method: string,
url: string
) {
if (url.match(new RegExp(`^${serverUrl}`)) !== null && bearerToken) {
this.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.OPENED) {
this.setRequestHeader(
'Authorization',
`Bearer ${bearerToken}`
);
}
};
}
return XMLHttpRequestOpen.apply(this, arguments);
};
Unfortunately, even though in dev console I see authentication header I still receive 401 server response.
What am I missing? Bearer token is 100% correct, so something is wrong with my implementation.
As it turns out my solution was 100% correct, the problem was with CORS policy on proxy.

Get Current Users: Google Real Time API with NodeJS & gtoken: "Invalid Credentials"

I would like to use a NodeJS Server to obtain the current users on my Website from Google Analytic using the Real Time Reporting API:
So far I try to do this via an HTTP request with request and gtoken. The getToken-Part works. I get a token. But the HTTP-Request doesnt work. I get an "Invalid Credentials" Error with Code 401.
Does anybody have an idea what to do? Maybe this is the completely wrong approach to get these data.
var received_token;
var url = "https://www.googleapis.com/analytics/v3/data/realtime";
var paramsObject = { ids:"ga:123456789"};
const gtoken = new GoogleToken({
keyFile: 'pathToServiceAccountJSONKeyFile:',
scope: ['https://www.googleapis.com/auth/analytics.readonly']
});
gtoken.getToken(function(err, token) {
if (err) {
console.log(err);
return;
}
received_token = token;
console.log(token);
request({
url: url,
qs: paramsObject,
headers: {
'Authorization': received_token
}
}, function(err, response, body) {
if(err) { console.log(err); return; }
// console.log(response);
console.log(body);
});
I found the error :-)
In the Authorization Header "Bearer" was missing, now it works like charm and I receive data from the Google Real Time API.
headers: {
'Authorization': "Bearer " +received_token
}

How to use http request headers in Got?

I have a very simple goal in mind. I want to make an API request from an API known as Zomato from my node.js server application. I'm using an https request framework known as Got, which is supposed to be a lighter version of request API.
var got = require('got');
var httpheaders = {
'Accept': 'application/json',
'user-key': '**********************',
'json': true
}
got('https://developers.zomato.com/api/v2.1/geocode?lat=35&lon=34', {httpheaders}).then(response => {
console.log('We got something');
console.log(response.body);
}).catch(error => {
console.log('We got nothing');
});
When I attempt to run this I catch an error and print, "We got nothing". I don't seem to know how to actually include http request headers, but I can't figure out what the proper syntax would be based off the documentation. Any help would be appreciated. Thanks!
https://github.com/sindresorhus/got/blob/HEAD/documentation/2-options.md
You could use options, like this
import got from 'got';
const options = {
headers: {
foo: 'bar'
}
};
const data = await got(url, options).json();

angularjs and googles url shortener

I have an issue at the moment with the google url shortener.
I have set up this service:
angular.module('widget.core').service('urlShortener', service);
function service($log, $q, $http) {
var gapiKey = '<MyApiKey>';
var gapiUrl = 'https://www.googleapis.com/urlshortener/v1/url';
return {
shorten: shorten
};
//////////////////////////////////////////////////
function shorten(url) {
console.log(url);
var data = {
method: 'POST',
url: gapiUrl + '?key=' + gapiKey,
headers: {
'Content-Type': 'application/json',
},
data: {
longUrl: url,
}
};
return $http(data).then(function (response) {
$log.debug(response);
return response.data;
}, function (response) {
$log.debug(response);
return response.data;
});
};
};
As far as I can tell, this should work. I have put in the correct API key and when I run this method I get this error:
{
error: {
code: 401,
message: 'Invalid credentials'
}
}
But, if I use postman and set it up exactly like this method:
Make it post
Add the content-type header and set it to application/json
set the url to https://www.googleapis.com/urlshortener/v1/url?key=myapikey
set the body to:
{
longUrl: 'myreallylogurl.com'
}
When I post this, it works with no issues.
I have checked my application on the google console and it is definitely set to unrestricted.
Has anyone come across this issue before? Does anyone know how to solve it?
I figured this out, it was nothing to do with the code above, but I thought I would answer my own question because someone else may run into the same issue.
In the project I have an httpInterceptor set up that adds the authetentication token to each request for talking to my API. This was what was causing the issue.
It so happened that I already defined a constant for my apiUrl, so I just updated the interceptor to check to make sure that the request url was my api before trying to append the token.
Like this:
angular.module('widget.core').factory('authInterceptor', factory);
function factory($q, $location, $localStorage, apiUrl) {
// The request function
var request = function (config) {
// If we are querying our API
if (config.url.indexOf(apiUrl) > -1) {
// Get our stored auth data
var authData = angular.fromJson($localStorage.get('authorizationData'));
// Set our headers to the request headers or a new object
config.headers = config.headers || {};
// If we have any auth data
if (authData && authData.authenticated) {
// Set our authorization header
config.headers.Authorization = 'Bearer ' + authData.token;
}
}
// Return our config
return config;
};
return {
request: request
};
};
I hope that helps someone else. Took me hours to figure it out :/

Basic authentication (or any authentication) with fetch

Couldn't find any documentation on this, so before I dig deep in code does anyone out there know how to use basic authentication when making a REST request using 'fetch' (https://github.com/github/fetch).
Just tried the following line, but the header was not set in the request:
fetch('http://localhost:8080/timeEntry', {
mode: 'no-cors',
headers: { 'Authorization': 'Basic YW5kcmVhczpzZWxlbndhbGw=' }
})
.then(checkStatus)
.then(parseJSON)
.then(function(activities) {
console.log('request succeeded with JSON response', data);
dispatch(activitiesFetched(activities, null));
}).catch(function(error) {
console.log('request failed', error);
dispatch(activitiesFetched(null, error));
});
The username and password is my own first and last name, using curl it works.
If I put { 'Accept' : 'application/test' } Accept is set, just not Authorization... strange.
Just for me to able to continue I added credentials: 'include' which makes the browser to prompt for username and password which is used for communicationg with the REST backend. Just for testing, will use OAuth further on.
fetch('http://localhost:8080/timeEntry', {
mode: 'no-cors',
credentials: 'include'
})
.then(checkStatus)
.then(parseJSON)
.then(function(activities) {
console.log('request succeeded with JSON response', data);
dispatch(activitiesFetched(activities, null));
}).catch(function(error) {
console.log('request failed', error);
dispatch(activitiesFetched(null, error));
});
no-cors mode prevents the headers from being anything other than simple headers.
"Authorization" header doesn't fit to simple headers. See more here: https://developer.mozilla.org/en-US/docs/Web/API/Request/mode
Note that if you use fetch with Authorization header you will NOT establish a session. You will have to manually add that header for every request. Navigating to secured path would also not be possible.
So to make this work You should pre-authenticate with XMLHttpRequest. You can do this like so:
var authUrl = location.origin + '/secured-path/';
var http = new XMLHttpRequest();
http.open("get", authUrl, false, login, pass);
http.send("");
if (http.status == 200) {
//location.href = authUrl;
} else {
alert("⚠️ Authentication failed.");
}
Note that above is synchronous so you don't need a callback here.
So after doing this you can use fetch without headers e.g. this request should be successful:
fetch(authUrl, {
method: 'get',
}).then(function(response) {
console.log(response);
});
Since it looks like the library you are using is a polyfill for Fetch API, I'm going to work off of the assumption that the syntax should carry through as well.
The samples I found on Mozilla's page indicate that the fetch method signature is fetch('API_ENDPOINT', OBJECT) where object looks like:
myHeaders = new Headers({
"Authorization": "Basic YW5kcmVhczpzZWxlbndhbGw="
});
var obj = {
method: 'GET',
headers: myHeaders
})
So the method becomes:
fetch('http://localhost:8080/timeEntry', obj)
.then(checkStatus)
.then(parseJSON)...
I have not tested this code, but it seems consistent with what I was able to find. Hope this points you in the right direction.

Categories