I am working on creating a Node.js REST API, using the Express module, that redirects HTTP GET and PUT requests to another server. However, when running test queries in Postman, I always get HTTP 401 Unauthorized responses. Yet, when I try the same on query on the Chrome browser I get a successful response (HTTP 302). I read through some documentation on the HTTP request/response cycle and authorization. The server I am redirecting to uses HTTP Basic authentication. In my code I am redirecting the API call to my application server using the res.redirect(server) method. In my Postman request I am setting the username/password in Authorization tab for my request. I know this is gets encoded using base64, but I am guessing this isn't being passed on the redirect when done through Postman.
The following code snippets show what I've created thus far.
This is the Express route I created for GET requests
app.get('/companyrecords/:name', function(req, res) {
var credentials = Buffer.from("username:password").toString('base64');
console.log(req);
var requestURL = helperFunctions.createURL(req);
res.redirect(requestURL);
});
I define a function called createURL inside a file called helperFunctions. The purpose of this function is set up the URL to which requests will be directed to. Here is the code for that function.
module.exports.createURL = function (requestURL) {
var pathname = requestURL._parsedUrl.pathname;
var tablename = pathname.split("/")[1];
var filter = `?&filter=name=\'${requestURL.params.hostname}\'`;
var fullPath = BASE_URL + tablename.concat('/') + filter;
console.log(fullPath);
return fullPath;
}
Where BASE_URL is a constant defined in the following form:
http://hostname:port/path/to/resource/
Is this something I need to change in my code to support redirects through Postman or is there a setting in Postman that I need to change so that my queries can execute successfully.
Unfortunately you can't tell Postman not to do what was arguably the correct thing.
Effectively clients should be removing authorisation headers on a redirect. This is to prevent a man-in-the-middle from sticking a 302 in and collecting all your usernames and passwords on their own server. However, as you've noticed, a lot of clients do not behave perfectly (and have since maintained this behaviour for legacy reasons).
As discussed here however you do have some options:
Allow a secondary way of authorising using a query string: res.redirect(302, 'http://appServer:5001/?auth=auth') however this is not great because query strings are often logged without redacting
Act as a proxy and pipe the authenticated request yourself: http.request(authedRequest).on('response', (response) => response.pipe(res))
Respond with a 200 and the link for your client to then follow.
Related
I have a very basic UI for a login page:
Upon clicking the LOGIN button, the following methods gets called:
async function loginPatient(){
let item ={username:userName, password};
let result = await fetch("http://localhost:8000/users/login",{
method:'POST',
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body: JSON.stringify(item)
});
alert(result);
alert("breakpoint")
result = await result.json();
localStorage.setItem("user-info",JSON.stringify(result));
nav('/patient')
}
At this point I simply want it to change the page when the button is clicked. My API returns the following information from the database:
To test I did console.log("hello world") in the first line of the function and it works
However, If I run console.log("hello world") after the let result = await fetch(...) part it does not work. How can I test this to see why it's not working ?
Here are the errors from the console:
I did not write the API and do not know how Node works yet, I am just doing the front end for this
The issue is code is never reaching after fetch line, basically request is failing, the error on console is saying the due to CORS issue, the request failed, and in your loginPatient function, you have not handled the failed case, if you just wrap your fetch call inside try/catch block, you will see your code will fall into fail block, as api failed.
You need to enable CORS on your server or backend, Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.
You can read more about cors at:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Looks like your client is on some other domain or port(if your are developing locally) than your server. You need to enable CORS permission for your client url.
And if you are using express for your backend, you can check the following url to enable cors.
https://expressjs.com/en/resources/middleware/cors.html
And last thing why Postman is getting success response, because it is by passing this cors check, as Postman is making request from it's server to your direct server instead of browser.
First initialize you navigation variable as follows
const navigate =useNavigate()
then navigate to you specific route by returning you navigation variable as follows.
return navigation("/");
Happy Coding!
I have this code with session check and i want to pass data from server to client
I want to display the session name in the lobby.html page
but im new to nodejs express and i dont know how to.
app.get('/lobby', (req, res) => {
// check session
sess = req.session;
if(sess.name) {
// redirect to lobby.html passing the session name
}
else
res.sendFile(path.resolve(__dirname, './public/client/error.html'));
});
```
Redirects are by their definition GET requests. The ways to get data from one get request to the next are as follows:
Add a query parameter to the URL you're redirecting to and then have the client or the server parse the data out of the query parameter and adjust the content accordingly.
Set a custom cookie with the data, then do the redirect and have the client or server pull the data from the cookie on the redirected request.
Have the server put some data into the server-side session and have the subsequent redirected request get the data from the session and adjust the content accordingly.
Both #2 and #3 are vulnerable to subtle race conditions if there are multiple incoming requests, the cookie or session data may get acted upon for some other incoming URL rather than the redirected URL unless the data also includes exactly which URL it is supposed to apply to. Option #1 is not susceptible to that type of race condition.
Looks like req.sess is passed from browser, so why not get name through document.cookie on the browser side directly?
I want to send a request to this Amazon Alexa API.
That page contains the last 50 activities I made with my Amazon Echo. The page returns JSON. Before you can request that page, you need to authorize your account, so the proper cookies are set in your browser.
If I do something simple as:
const rp = require("request-promise");
const options = {
method: "GET",
uri: "https://alexa.amazon.com/api/activities?startTime=&size=50&offset=-1",
json: true
};
rp(options).then(function(data) {
console.log(data);
}).catch(function(err) {
console.log(err);
});
I can send a GET request to that URL. This works fine, except Amazon has no idea it's me who's sending the request, because I haven't authorized my NodeJS application.
I've successfully copied ~10 cookies from my regular browser into an incognito tab and authorized that way, so I know copying the cookies will work. After adding them all using tough-cookie, it didn't work, unfortunately. I still got redirected to the signin page (according to the error response).
How do I authorize for this API, so I can send my requests?
I have been looking for a solution for this too. The best idea I have is to use account linking, but I haven't try it yet. Looks like ASK-CLI has interface for this also, but I can't figure it out how to use it (what is that URL?). For linking account to 3rd party server is not easy, but link it back to Amazon for the json API should not be that complicated.
I'm currently developing my app and I'm at the stage where I can start testing messages from Twilio. I configured my server on digital ocean with a public facing IP address and my Nodejs app is listening to calls from Twilio.
I also configured my phone number's message "request url" to "http://username:password#198.xxx.xxx.xxx/messages" with "HTTP POST".
When I debug the headers, I don't see the "authorization" headers. I'm I missing something here?
Any help is much appreciated!
Below is the code.
var headerValues = bag.req.headers.authorization.split(' ');
console.log(bag.req.headers);
var scheme = headerValues[0];
if (scheme === 'Basic') {
var credentials = headerValues[1];
var decoded = new Buffer(credentials, 'base64').toString().split(':');
bag.req.creds = {
userName: decoded[0],
password: decoded[1],
authType: 'basic'
}
}
I use the same setup as you do in several call centers I have built.
If you are using a proxy setup which requires username:password# before the IP address then your issue is likely with that proxy if you can access the code by going directly to the actual server ip address as I note below. However, you did not mention using a proxy just using a digital ocean droplet so I am responding assuming you do not have a proxy setup.
So if you do have a proxy setup make sure you can access the IP address of the server directly first.
Also if those are just extra variables you need to pass over you may be better off appending them after the IP address
for instance xxx.xxx.xxx.xxx/username/password
Then get them with req.params
for instance (and yes this will work with post data since its merely part of the URL and not an actual get command post)
router.post('/sms/:username/:password'), function(req, res, next){
username = req.params.username;
}
First you would not want to direct your request URL at "http://username:password#198.xxx.xxx.xxx/messages" with "HTTP POST".
If you do not have a domain directed at your IP address yet you want your request URL to be
https://198.xxx.xxx.xxx/inbound/sms
{Replacing /inbound/sms with whatever route you are using}
Then at the top of your route (I am using express so my setup may look different than your)
I have the node.js twilio library
, twilio = require('twilio')
, capability = new twilio.Capability(sid, auth)
, client = require('twilio')(sid, auth)
Then here is an example of my /sms route
router.post('/sms', function(req, res, next){
var sid = req.body.SmsSid;
var from = req.body.From;
var to = req.body.To;
var date = Date();
var body = req.body.Body;
if(req.body.NumMedia > 0){
code to handle MMS
}
Code to handle SMS data
res.send("Completed");
});
I ran into this this week and discovered that behavior surrounding Basic Auth in the URL is very cloudy. For one thing, it appears to be deprecated from the URI spec as it pertains to HTTP:
...
3.2.1. User Information
...
Use of the format "user:password" in the userinfo field is deprecated.
...7.5. Sensitive Information
URI producers should not provide a URI that contains a username or password that is intended to be secret. URIs are frequently displayed by browsers, stored in clear text bookmarks, and logged by user agent history and intermediary applications (proxies). A password appearing within the userinfo component is deprecated and should be considered an error (or simply ignored) except in those rare cases where the 'password' parameter is intended to be public.
...
Because of this, both Firefox and Chrome appear to just strip it out and ignore it. Curl, however, seems to convert it to a valid Authorization header.
Whatever the case, I believe this functionality is actually the responsibility of the HTTP user agent, and it appears that Twilio's user agent is not doing its job. Thus, there is no way to make basic auth work.
However, it appears Twilio's preferred method of auth is to simply sign the request using your account's secret auth key, which you can then verify when handling the request. See here.
On researching the raw NodeJS Request and IncomingMessage classes, there appears to be no way to get at the full, raw URL to compensate for Twilio's non-conformity. I believe this is because the actual data of an HTTP request doesn't contain the full URL.
My understanding is that it's actually the HTTP user agent that's responsible for extracting and formatting the auth info from the URL. That is, a conformant HTTP user agent should parse the URL itself, using the hostname and port portion to find the right door on the right machine, the protocol portion to establish the connection with the listener, the verb combined with the URL's path portion to indicate what functionality to activate, and presumably it is then responsible for converting the auth section of the URL to an official HTTP Authorization header.
Absent that work by the user agent, there is no way to get the auth data into your system.
(This is my current understanding, although it may not be totally accurate. Others, feel free to comment or correct.)
okay.
I think I have failed to understand an elemental part of token based authentication.
I am using node with express and am using jwt to prevent access to my site if you haven't logged in. I can create a token on the login page, and I can send it back to the client and store it in localStorage/cookie. Now if the user wants to navigate to another page they will type in a url and trigger a get request.
How do I access that token from localStorage/cookie and pass it to the server before I load the page as part of the get request. My assumption is that there should be a way of passing the token to the server - intercepting it in the middleware - and loading the page if the token is legit, or redirecting to the login page if the token isn't validated correctly.
On a post request this would be much simpler as you can fetch the token and pass it as part of an ajax call, after the page has loaded.
I have seen references to including the token as part of the request header (authorization bearer). I assume this only works for post, because if you were able to set the header parameter 'globally' then why would you bother storing on the client side in a cookie/localStorage.
So as you can see I am a little confused by the workflow. It seems like I am going against the grain somehow. Any clarity would be much appreciated.
If you are using localStoage in order to store the JWT, then the easiest way to pass it to the server is by retrieving first the token from the localStorage with localStorage.getItem('token') (or whatever your token name is) and then inserting it in the header of the request (either it is GET or POST/PUT/DELETE). Depeding on the library you are using to handle your http requests on the client, there are different ways of doing so. In jQuery for example, you can do the following inside the AJAX request:
$.ajax({
url: API_URL + "/endpoint",
method: "GET",
beforeSend: function(request){
request.setRequestHeader("Authorization", "BEARER " + localStorage.getItem('token'));
}
})
After this, on the server side simply access the parameters by accessing request.header options just as you would normally do. Hope this helps!