I'm running my app on localhost:3000/#!/, and trying to get URL parameters for use with Express, with no luck. I've created a new server routing file that contains the following:
admin.server.routes.js
'use strict';
module.exports = function(app) {
// Admin Routes
var admin = require('../../app/controllers/admin.server.controller');
// Both of these routes work fine.
app.route('/admin/test').
get(admin.populate).
put(admin.update);
// First attempt, didn't work.
app.route('/admin/test').get(admin.doSomething);
// Second attempt, didn't work.
app.param('foo', admin.doSomething);
// Third attempt, didn't work.
app.param('foo', function(req, res) {
console.log('Found foo!');
return res.status(400).send();
});
};
On my admin page, my admin.client.controller.js sends an $http.get request on loading to populate the data. I have a form with a button the sends an $http.put request to update the populated values. Both of these requests work fine.
The problem is when I try to visit my app using a URL with the foo parameter, like so: http://localhost:3000/#!/admin/test?foo=bar. I've tried each of the three attempts noted above in my code (commenting out the others out so I could try them one by one), but cannot seem to get the variable.
In my admin.server.controller file, in addition to the populate and update functions, I simply have this code:
admin.server.controller
exports.doSomething = function(req, res) {
console.log('Server - found foo!');
};
Using none of these efforts have I actually been able to demonstrate that I've successfully "grabbed" foo for server-side use. What am I missing?
In your
http://localhost:3000/#!/admin/test?foo=bar
all hashbang urls are handled by angularjs, this /admin/test?foo=bar wouldn't be considered as a request. To add a query string in your request, you can do it like this in angularjs resource:
function ($resource) {
$resource('/admin/test').query({foo: 'bar'});
}
This would be expressed as this http://localhost:3000/admin/test?foo=bar
Your issue relies mostly on how you send your request on your client-side.
By the way, in your express routes you can get the foo value like this: Pre-routing with querystrings with Express in Node JS
If you wanted to get the query string and use it in your request, refer to this: How can I get query string values in JavaScript?
A URL is in the form <protocol>://<hostname>?<query string>#<fragment>.
Any part of a URL after (and including) the first # is considered a fragment. Fragments are only used on the client (in this case the browser), and are not typically used by the server.
From section 3.5 of RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax:
Fragment identifiers have a special role in information retrieval systems as the primary form of client-side indirect referencing, allowing an author to specifically identify aspects of an existing resource that are only indirectly provided by the resource owner. As such, the fragment identifier is not used in the scheme-specific processing of a URI; instead, the fragment identifier is separated from the rest of the URI prior to a dereference, and thus the identifying information within the fragment itself is dereferenced solely by the user agent, regardless of the URI scheme.
Traditionally fragments are used to link to anchors on the same page. A good example is the link provided above to the RFC page, which will jump you down to the section 3.5. Fragment automatically in your browser.
The URL to the section is https://www.rfc-editor.org/rfc/rfc3986#section-3.5, so the fragment is section-3.5. In the HTML for the page there is an anchor within the <h3> denoting the section with:
<a class="selflink" name="section-3.5" href="#section-3.5">3.5</a>
The interesting property here is name, which names the anchor, allowing the browser to identify and jump to it because it matches the value requested by the fragment. In effect, the anchor is a client side 'secondary resource' that is being requested by the URI.
Client side javascript applications and frameworks such as angularjs take advantage of fragments; the fact that they are not for use on the server; and the fact they are included in the browser history stack; as a way to reference resources on the client side, for example to use a client side routing system.
(Aside: #! has risen as a popular idiom for denoting routes in client side applications. This is possibly related (grammatically but not technically) to the #! header used in *nix based executable scripts. These are typically followed by a path to an interpreter for the script. For example #!/bin/bash.)
So in the URI http://localhost:3000/#!/admin/test?foo=bar, the segment ?foo=bar is after the # denoting the fragment, and is therefore being defined as part of the fragment rather than the query string. A query string for a remote resource should always be defined before the fragment. For example:
http://localhost:3000/admin/test?foo=bar#!/route/in/angularjs
Related
Not sure if the title summarises my question well.
Basically, I am trying to authenticate routes such as checking if user exists etc. I only want to allow
requests coming from my frontend application to be approved, but, since no user is signed in there is no token to send.
Api request -
mywebiste/checkUser/email
This route is unprotected on my backend because no user is logged in.
BUT I want to protect this route, in such a way that it's accessible only from the frontend.
Some ideas I came up with were adding specific headers tag from the frontend and check them on the backend, but that could be easily replicated, is there something more secure like using tokens etc.
I am using React and Node.js
Same origin policy is going to give you some basic protection, but basically if an API endpoint is exposed publicly, it's exposed publicly. If you don't want that route to be publicly accessible you need to add access control.
If you use that route to check if a user is already registered, you could, for example, merge it with the user registration route and send a different error code if the user already exists (which is not a great idea because it leaks which emails are registered on your system).
You can verify that a request was originated by a user (by authenticating him) but you cannot verify that a request comes from a particular client because of these two reasons :
If you include some API key in your client (web page or other), it's easily retrievable by everyone (the best thing you could do is offuscate it which makes things slightly harder but still possible)
If you send an API key over the network it's easily retrievable as well
The only thing you could do is prevent other web pages from calling your backend on behalf of the user, by using CORS (which is actually active by default if you dont specify an Access-Control-Allow-Origin header)
I ended up creating a kind of working solution, so basically, I create a new base64 string on my frontend and attach that to the header while making a request to the backend. The base64 string is different every minute, so even if the header is copied, it differs every minute and is combined with your secret key.
I have made a package so that people can use it if they want - https://github.com/dhiraj1site/ncrypter
You can use it like so
var ncrypter = require('ncrypter');
//use encode on your frontend with number of seconds and secret key
var encodedString = ncrypter.encrypt(2, 'mysecret1')
//use decode on your backend with same seconds and secret
var decodedString = ncrypter.decrypt(encodedString, 2, 'mysecret1');
console.log('permission granted -->', decodedString);
I've used code splitting to seprate restricted parts of my app into different chunks. This is working great so far, now I would like to ensure that the files themselves don't get served unless authenticated. I was thinking of using ngx_http_auth_request_module
http://nginx.org/en/docs/http/ngx_http_auth_request_module.html#auth_request
Which allows to send a sub-request before serving certain files. How can I ensure that certain headers are always send as part of the HTTP request when React wants to fetch the necessary chunks?
I have trouble understanding why you would need to prevent unauthenticated malicious users to have access to your static chunks.
Dynamic imports and code splitting are mainly used to reduce the bundle size for large applications as users won't necessarily need everything.
In order to secure your app you need to prevent users from seeing or tampering with data they do not have access to. This means the security lies with the API your app is talking to.
What I do:
Reject unauthenticated requests to the API
Keep a token client-side on authentication
Pass and check the token on all requests
Burn the token when obsolete and redirect to login
Notify, redirect users when they do not have access to some data or better not displaying content they do not have access to
I'm sure you already did what I wrote above, what I want to emphasize is that chunks are basically empty UI filled with data from the secured API.
Let's say I have bad intentions and I bypass client-side routing in order to have access to the restricted chunk. It will be an empty UI with secured API routes, I won't be able to do anything with it.
In case you have a very specific need, you might need to write a webpack plugin.
about the ensure request
One of webpack 's properties is that it can fetch only necessary chunks when loading pages.You can just use like require.ensurn to query chunks when necessary,so there is no need to ensure the certain headers.
ngx_http_auth_request_module
Ngx_http_auth_request_module and sub-request are always used to fetch web file in server.It's always used as backend authentication module.Here is the data flow direction in nginx.
When you download file, the download request will be passed to the server, then server return the override Http Request to Nginx,then Nginx will find the exact file.
The ngx_http_auth_request_module allows to send request to back server(like php .tomcat), and based on the request to pass or not, if pass, you will be able to fetch file in the back server.
nginx-----load speed
The nginx always fetch static file, like index.html.If have to validate the permission for every js/css everytime,then fetch it throw,thd loading speed for page will be very slow.
about how to authenticate
Since you have separated app.Here is a little suggestions.You can get the authenticated request by only import restricted parts in the authenticated file.And the webpack will automatically handle the rest.
fetch data from the server in the non-restricted part with information to authenticate like this:
http://.../api/auth?info=...
based on the infos in server to authenticate, and pass other infos like type back to the frontend
based on the type information to view .
if (this.props.type === "restrict"){
<restrict component/>
} else {
<non-restrict component/>
}
In my express app, I have two routes as follows:
router.post('/:date', (req, res) => {
// if date exists, redirect to PUT
// else add to database
})
router.put('/:date', (req, res) => {
// update date
})
If date already exists on a POST call, I want to redirect to PUT. What is the best way to do this using res.redirect?
In the docs, all redirects are to different URL patterns. I would like to keep the URL same and change the rest verb from POST to PUT.
I had a look at this SO question, and added this line in POST:
res.redirect(303, '/:date');
but it did not redirect me to PUT.
What you're trying to do here will not work for several reasons, but lickily you don't need to do any of that - see below.
First problem
The 303 "See Other" redirect that you're using here, by the spec should always be followed by a GET (or HEAD) request, not PUT or anything else. See RFC 7231, Section 6.4.4:
https://www.rfc-editor.org/rfc/rfc7231#section-6.4.4
The relevant part:
The 303 (See Other) status code indicates that the server is
redirecting the user agent to a different resource, as indicated by a
URI in the Location header field, which is intended to provide an
indirect response to the original request. A user agent can perform a
retrieval request targeting that URI (a GET or HEAD request if using
HTTP), which might also be redirected, and present the eventual result
as an answer to the original request. Note that the new URI in the
Location header field is not considered equivalent to the effective
request URI. [emphasis added]
Second problem
The other popular types of redirects - 301 "Moved Permanently" and 302 "Found" in practice usually work contrary to the spec as if they were 303 "See Other" and so a GET request is made.
See List of HTTP status codes on Wikipedia:
This is an example of industry practice contradicting the standard. The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 to distinguish between the two behaviours. However, some Web applications and frameworks use the 302 status code as if it were the 303. [emphasis added]
Third problem
There is a 307 Temporary Redirect (since HTTP/1.1) but it explicitly disallows changing of the HTTP method, so you can only redirect a POST to POST, a PUT to PUT etc. which can sometimes be useful but not in this case - see Wikipedia:
In this case, the request should be repeated with another URI; however, future requests should still use the original URI. In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. For example, a POST request should be repeated using another POST request.
This 307 redirect is still not what you want and even if it was, it is not universally supported as far as I know so it needs to be used with caution.
See also this answer for more info:
Redirect POST to POST using Express JS routes
Your options
You can abstract away your controllers - which you will usually do anyway, for anything complex:
// controllers - usually 'required' from a different file
const update = (req, res) = {
// update date
};
const add = (req, res) => {
if (date exists) {
return update(req, res);
}
// add to database
};
router.post('/:date', add);
router.put('/:date', update);
Or you can abstract parts of your controllers as functions.
Universal controllers
Also, note that you can write universal controllers called for every HTTP method that might work here:
router.use('/:date', (req, res) => {
});
REST
Note that what you're doing here is not a usual RESTful way of naming your paths and it may make sense to use only PUT in your case for both new and updated dates.
Contrary to what many people think PUT doesn't mean UPDATE. It means to put a resource (new or not) to a certain URL (overwriting the old one if it already exists). It's pretty much like writing this in the shell:
echo abc > /the/path/to/file.txt
which will "update" the file if it exists, but it will also create a new file if it doesn't.
So for example, if you have /users/:id path, then you use:
GET /users to get a list of users
GET /users/:id to get a specific user with that ID
POST /user (not /users/:id) to create a new user without providing ID
PUT /users/:id to overwrite an existing user or to create a new user providing an ID
PATCH /users/:id to update the provided fields of user with that ID
Here, as I understand your :date is like an ID, ie. you want to overwrite the record if it already exists and create if if it doesn't exist. In both cases you are providing the :date path component so you might use PUT for all cases just as well.
In other words, you cannot redirect from one HTTP method to another HTTP method (except for GET) but you don't need to do it in this case.
I am running a MEAN framework with express routing requests. I have two main routes to public/ and app.
With the APP being an API and public being a set of web pages which reads data from the API.
// Setting the app router and static folder
app.use(express.static(path.resolve('./public')));
I have two controllers in the public folder, home and header.
In the home controller I am using Angular JS to call the API and return the results.
The API allows for filtering through the use of query strings:
$http.get('http://search?sumBoth=1&customer=' + customer)
I would like to build up a route specific to this controller along the lines of
http://url/customers/CustomerName
Where CustomerName would be set as the customer variable
Question
a) Is this best done in Angular or Express?
b) Where and how do I implement such routing?
I hope this question is well received, please comment, if you need more information.
I understand that the response of $http.get('http://host/path?sumBoth=1&customer=' + customer) is a list of search results. In that case the path should be a collection path, it's not really a best practice to have search terms in the path. Subcollection paths are pretty standard (something like http://host/customers/search?params, but still the specific search terms should go in the query string)
If on the contrary you expect to retrieve just one result by some identificator (provided for instance the customer name is unique) that's a different story, you should really use http://host/customers/:identifier.
In any case you can use angular resources, both parts of your application need to be aware of the routing. In the front-end you define an additional verb that adds the filters (or just use the standard query one - see https://docs.angularjs.org/api/ngResource/service/$resource). In the back-end you need to route the call and parse the parameters. If it's a list, parse the query string and render your result array, if it's a single resource, parse the identifier, find the corresponding resource and render it back.
I want to allow an authenticated client in Express to access to other web applications that are running on the server, but on different ports.
For example, I have express running on http://myDomain and I have another application running on say port 9000. I want to be able to reach the other app through http://myDomain/proxy/9000.
I had a little bit of success using node-http-proxy, for example:
function(req, res) {
var stripped = req.url.split('/proxy')[1];
var path = stripped.split('/');
var port = path.shift();
var url = path.join('/');
req.url = url;
proxy.web(req, res, {
target: 'http://127.0.0.1:' + port
});
}
However, the big problem is that when the web app makes GET requests, such as for /js/lib.js, it resolves to http://myDomain/js/lib.js, which is problematic because express is not aware of those assets. The correct request would be to http://myDomain/proxy/9000/js/lib.js. How do I route all these additional requests?
What you need to do is to replace URLs in the initial page with the new URL pattern. What is happening is that the initial page that your reverse proxy returns has a reference to:
/js/lib.js or http://myDomain/js/lib.js
so when the browser makes a second request it has the wrong pattern for your reverse proxy.
Based on the incoming request you know what the pattern should look like. In your example it's http://myDomain/proxy/9000. You then fetch the appropriate page from the other server running on http://127.0.0.1:9000/. You do a string replace on any resources in that file. You'll need to experiment with the pattern but you might look for 'script src="/' or 'href="/' and you might find regex helps with the pattern if, for example, the src attribute isn't the first listed in a script tag.
For example you might find 'scr="/' and then you replace it with 'src="/proxy/9000/' that way when the browser asks for that local resource it will come through with the port that you're looking for. This is going to need experimentation and it's a great algorithm to write unit testing around to get perfect.
Once you've done the replacement you just stream that page to the client. res.send() will do this for you.
Something else that you might find useful is that ExpressJS gives you a way to pull out the port number with a little less hassle than you're doing. Take a look at this example:
app.get('/proxy/:port', function(req, res){
console.log('port is ' + req.params.port);
});
I don't think http://myDomain/proxy/9000 is the correct way to do it. Web pages are going to assume the site's domain to be just myDomain and not myDomain/proxy/9000, because that is what the standard says.
Your use case would be better served by using subdomains like 9000.proxy.myDomain.