Hello,
I’m building a Meteor app that requires an Admin Interface. Since Meteor loads the full website at first connection, I’m wondering if there is a best practice to keep the Admin Interface separated from the Public interface of the website. The only solution I found myself is to build two different Meteor apps and connect them to the same mongodb instance, but that doesn’t sound really ‘elegant’.
Any suggestions?
So the short answer is you can never fully hide the templates from the user regardless of their user type. The exception would be lazy loading them after you’ve authenticated them. You can however prevent that admin data from being published.
Also, even if you have a separate app you’ll need to verify the logged in user is an admin.
However for most use cases you can safely use the Roles package to determine if a logged in user is an ‘admin’ or ‘normal’ user type.
To prevent normal users from seeing an admin template you can check in the router and redirect to a not authorized page if they are not an admin:
// this is just an example, may not work if copy/paste
FlowRouter.route('/dash/secrets/:secretId', {
action: function(params) {
if (Roles.userIsInRole(loggedInUser, ['admin'])) {
BlazeLayout.render("mainLayout", {area: "dashboard"});
} else {
BlazeLayout.render("mainLayout", {area: "notAuth"});
}
}
});
However a tricky hacker can manipulate their user object on the console to bypass this. This is why it’s important to check the role on the server in the publish function so that even if the hacker loads the admin page it will just be empty because the publish won’t return the data.
// Give authorized users access to sensitive data by group
Meteor.publish('secrets', function (group) {
if (Roles.userIsInRole(this.userId, ['view-secrets','admin'], group)) {
return Meteor.secrets.find({group: group});
} else {
// user not authorized. do not publish secrets
this.stop();
return;
}
});
Yes, as @SkinnyGeek1010 said, don’t build two apps just cause you want to hide some templates (what if you have different levels of admin? 3,4,5 apps?).
Do use pub/sub to limit what goes to the client. Do use allow/deny rules to prevent unauthorized access.
Don’t put sensitive stuff in the templates but rather return from the server via methods/pubs after authenticating the user.
I have a similar need as @Aselox . In our case, I’m thinking of having a public facing app and a internal app for company staff. The main benefit I can come up with for this separation is so that the public site is not affected when we are making changes to the internal app and vice versa.
The other thought I had was to see if it was possible to have the Accounts package use a different collection than the default ‘users’ one. It seems from the feedback here that that may be overkill and unnecessary. Using roles would be sufficient in this use case.
Yea this works well. I’ve done this mainly to prevent shipping dead code to users, but the security by obscurity is nice too.
You can also share the methods over DDP so that you’re not duplicating those. I don’t think you can use a different users collection. If it’s that sensitive a separate DB would be better.
I usually create 2 apps: front & backoffice, connected to the same mongo database.
It works like a charm and allows to use different packages & css files without sending everything to the client.
If you need to share collection/methods definitions, you can create a datastore package and add it to the package folder as a git submodule. Not very convenient (submodules are a pain to use) but it works well too.
Hi @SkinnyGeek1010, I use this same approach on my application, also using Allan’s Roles package. However, I found Meteor.publish not to be “Reactive”.
This means that if just after an Admin logs out from his account and a non-admin logs in. The later still can see the secret publication.
The opposite also happens, the an Admin logs in to “demonstrate” the application just after a non-admin user logs out, it gets no admin publications available.
Have you faced the same issue? Is there a workaround for that?
Publications are not reactive, and they only re-run when the currently logged in userId changes, which can be accessed through this.userId. Because of this, it’s easy to accidentally write a publication that is secure when it first runs, but doesn’t respond to changes in the app environment. Let’s look at an example…