I'm working on an application where only the admin should be able to create users for the system; meaning the user is restricted from creating an account but can login if login credentials were made for him/her.
I'm thinking about using houston:admin to manually create users, but how can I restrict users from creating an account using accounts-ui?
Should I use different packages to achieve this altogether?
You have several ways to prevent users from creating accounts:
throwing an error in the Accounts.onCreateUser() callback (server only):
Accounts.onCreateUser(function(options, user) {
if (/* some logic to figure out if current user is an admin */) {
return user;
}
throw new Meteor.Error("user creation disabled.");
});
This will prevent the account from being created if the current user is not an admin.
configuring Accounts to forbid account creation (both client and server):
Accounts.config({
forbidClientAccountCreation: true
});
which rejects calls to createUser() from the client (but will not prevent user creation using oAuth services).
A combination of both is a likely course of action.
Take a look at the linked documentation for more details.
Related
In express app where users login using their web identities (google, facebook, amazon, etc) using passport.js for this. I have created a route. but only want that a single user should have access to it.
I have it working with extensive testing, but not entirely sure if its in fact secure enough?
Here is my code for the route:
app.get("/superSecretPage", function(req, res){
console.log(req.user);
if (req.isAuthenticated()){
if (req.user.userId === "XXXXXXXXXXXXXXXX") {
User.find({}, function(err, users){
res.render("userlist", {
users: users,
});
});
} else {
res.render("invalid")
}
} else {
res.redirect("login")
}
});
XXXXXXXXXXXXXXXX = being the users uniqueID from Google/Amazon/Facebook or another ID provider
Like i said this work, and only allows the user that equals req.user.userId, but is this secure enough?
This is not a proper way to do that. What will you do If you want to allow another user later? Will you open code and edit the user Id?
Do not hard code the user ID.
You need to implement Role Base Access Control. You need to create a super power user role and assign it to the users, Who you want to allow to access your super secret page. And use middlewares to check the role permissions.
This will help you : Role based auth | NodeJS
Like i said this work, and only allows the user that equals req.user.userId, but is this secure enough?
You haven't really shown enough to know about the overall security of this. The security depends upon at least the following:
Implementation of req.isAuthenticated()
How the user session was established and protected. Most sessions use a browser cookie to identify the session and it must be run over https to offer any protection. Then, you must also have proper auto-logout to make it less likely the session is just left active where someone else can use it on that computer.
How the req.user.userId got set in the first place. You don't show this so we really have no idea what is behind that and whether it's following good security practices or not.
If there is any persistent storage of the user session, then the security of that storage mechanism is relevant.
Presuming there's a login mechanism, are secure (hard to guess) credentials required? Are there anti-hacking protections in place to attempt to thwart credential guessing.
If you are 100% sure that req.user.userId is valid and that this connection belongs to the appropriate user for the userId, then this bit of code works fine. But, the devil is in the details of making sure that req.user.userId is appropriately connected to an authorized user and that's where the hard work is of making security work.
Add a field in the db schema and set it to an array which contains all the route that the user has access to. When authenticating loop through the array to see if he/she can access the route he/she is trying to.
I want to allow users to sign in/up via GitHub using firebase by clicking on the same button.
I create a new authentication for every user in the server side.
With the little piece of code, I'm able to detect if either the user is new or not:
const provider = new firebase.auth.GithubAuthProvider();
firebase.auth().signInWithPopup(provider).then((result) => {
if (result.additionalUserInfo.isNewUser) {
// The user is new
} else {
// The user is old
}
But, when the function signInWithPopup is called, if the user is a new user, a new authentication is automatically created for him. How can I avoid this?
And if the user is already authenticate, how can the user sign in from the client side? Where is the link between the authentication done from the back end with the user that wants to sign in the front end?
This is not how OAuth works. If you use an authentication provider like GitHub, they handle auth flow for you. The only thing that you are left with on the frontend side is an idToken with your identity, basic profile info, and a signature so you can as a user using this token. There's no distinction between sign up/sign in actions.
As you have noticed, Firebase is an extra layer in this flow, it creates an account for a user who signs in for the first time. But there's no user limit or extra payment so I wouldn't bother too much about these extra accounts. You might consider periodical cleanups if you care about the security here.
If you want to actually check if the user exists you have to use firebase-admin e.g. in a Firebase Function before the signInWithPopup is called. But still, unless you want to prevent users from signing up, you can hook your server logic into functions.auth.user().onCreate trigger.
To answer your last question, when the user is already signed in, you'll get the user object in firebase.auth().onAuthStateChanged when a page is loaded. Login state is stored by Firebase.js so once you have called signInWithPopup, you don't need extra steps.
I'm trying to keep things simple and using auth0-js WebAuth to authenticate users. However, as there is a redirect involved, I'm not in control of the sign-up functionality at that point.
My specific use-case is to call a createUser graphql mutation using Graphcool to create a user in my database, but I only want to do this if the user is a new user, obviously.
MY QUESTION: Using auth0-js, is it possible to identify if a user is a new or existing user in my client application after the redirect from Auth0 back to my client application (assuming authentication is successful)?
There are two general approaches here, and both require you to persist the Auth0 token in local storage after receiving it. You can use a middleware for your GraphQL client that checks local storage for a token for every request and includes it as the Authorization: Bearer <token> header if present.
Let's now look at the two approaches.
Always try to create the user
Trying to create the user using the createUser mutation as soon as receiving the token is a fairly simple approach. This is how the mutation looks like:
mutation signUp($token: String!) {
createUser(authProvider: {
auth0: {
idToken: $token
}
}) {
id
}
}
Now, if the token is valid and matches the configuration of the Auth0 integration in Graphcool, there are two possible scenarios. Note, a token corresponds to a user if the auth0UserId it embeds matches.
there is already a registered user corresponding to the token. In this case, a GraphQL error Code 3023: CannotSignUpUserWithCredentialsExist will be returned (compare with the error reference documentation). In your application you can catch this error to proceed normally.
there is no registered user yet corresponding to the token. The createUser mutation will return id and all is good!
Check if the user is already signed in
If you have a more elaborate sign up flow, you might want to redirect your users to a sign up form, which is not really possible with the first approach. Instead, we can check if the currently used token corresponds to a registered user before proceeding. You can use the user query to do that:
query {
user {
id
}
}
Again, there are the same two scenarios as above:
there is already a registered user corresponding to the token. In this case, a the query returns a user object with the corresponding user id. So we can proceed the flow in the app normally.
there is no registered user yet corresponding to the token. The date returned from the user query will be null, so we need to call the createUser mutation, or switch to a sign up form or similar.
Compare this to this FAQ article in the Graphcool documentation.
In that case, the simplest solution will be to use auth0 rule and use context.stats.loginsCount field to detect the user is new or not.
https://auth0.com/docs/rules/references/context-object
You can add context.stats.loginsCount field value as a custom claim in the token using rule. Therefore, in your application, you can make a HTTP request to /userinfo endpoint to get the token data.
function (user, context, callback) {
const count=context.stats.loginsCount;
context.idToken["http://mynamespace/logincounts"] = count;
callback(null, user, context);
}
https://auth0.com/docs/api-auth/tutorials/adoption/scope-custom-claims
If the counts are equal to 1, create the user in your DB.
I am building an application that has two types of users. Professional users and their clients. The account types are completely distinct with no overlap. So, emails registered as Pro could still be used to register as a Client, and if a Pro user tried to log in using the client form, their account would not exist.
The problem:
Meteor automatically prevents duplicate emails from being used when creating user accounts. My thought was to use a custom validation to allow the behavior and essentially create two different sets of users. Here is an example of the validation I tried for a 'Pro' user.
Accounts.onCreateUser(function(options, user) {
var email = user.email;
if (Meteor.users.find({emails: email, isPro : true}).count() > 0) {
throw new Meteor.Error(403, "This email address is already registered");
}
user.isPro = true;
return user;
});
But meteor still uses its default duplicate email rejection instead. Any ideas on how I can override this behavior, or is there a better way to create two distinct sets of users?
You can do this in a mongo shell
db.users.dropIndex('emails.address_1');
Doing this is probably not the best idea and may have unintended consequences. I too think the solution discussed in the comments would be better.
I'm building a Meteor app for contracts. A signed in user can create a contract. But I'd like for them to be able to send a contract to another party, who can view the contract without signing up or creating an account. The problem is that, even if the URL is long and random, www.example.com/docs/bM7GjrRq1wABYdxws3 -- I'm not sure it is private enough -- because maybe all these contracts could be easily crawled or end up in search like this.
I'd like the creator of the contract to set a password that can be emailed to the other party along with the link to the contract. Before the party can view the contract, they need to enter the password.
My solution is to allow a user to password protect the page (but without a username/email). But I'm not sure how to do it. How can I password protect a page with a password set by a user?
You could save password within the page object on mongodb.
Then when he creates or edit the page, he might choose to share publicly or with password.
The publish method could look somewhat like this.
Meteor.publish("page", function (){
if (!this.userId) {
return Pages.find({
_id: Session.get("pageId"),
private: true,
password: Session.get("pagePassword")
});
} else {
// code for logged in users
}
});
Of course it's a good idea to store a hash for the password and compare to the hash the user entered, instead of storing the raw password.