Limit Meteor.js built-in Google authentication to a domain - meteor

I'd like to use the Meteor.loginWithGoogle() tool to authenticate users, but is there any way to limit it to a specific (Google Apps) domain?
I could check after the user is authenticated using the returned email, but is there a way to do this at the login stage with some parameter for Google login?

I dont think its possible right now.
There is a pull resquest to partly add that functionality: https://github.com/meteor/meteor/pull/1332
The issue with that pull request seems to be that it only fixes the client side of thinges (ie. it only shows accounts from the selected domain when the user logs in).
But it does not add any server side checks.
Im using the following workaround:
In a .js file in the sever folder I have the following code:
Accounts.validateNewUser(function (user) {
if(user.services.google.email.match(/example\.org$/)) {
return true;
}
throw new Meteor.Error(403, "You must sign in using a example.org account");
});
This prevents accounts from being made for domains different from example.org.

If you want to only allow certain users from your domain, you could also add a whitelist collection that defines user ids from your Google Apps account. This way you can restrict access to only certain users, get single sign-on functionality, and can pre-set user roles and properties for your app before users even create their accounts.
Use the Accounts.onCreateUser(function(options, user){}) callback for that since it allows you to define additional user properties.

Related

Have one user signup another user with custom fields in firebase/flutter

I am trying to determine if the following scenario is possible with flutter and firebase:
we have users within the company who will be given access to the app, where on the homepage will be a signup another user button where they enter in that user's email and password, they get signed up, and then the original user specifies custom fields for the 2nd user, such as company name, role, position, etc.
Is this possible with flutter and firebase?
Have asked the flutter google group and was told about custom authentications, but from what I see that is just an external authentication system and doesn't show me how to let one user create another users profile with fields.
Any ideas?
The first thing to consider is whether those properties need to be in the user profile at all. The user profile is sent with every request, and should only contain information that is relevant for securing access. If you have additional information to store about the user, you should store it elsewhere (such as in one of Firebase's databases) using the UID of each user as its key.
Assuming that the information is about security (such as the role seems to be, there is no secure way to let one user set security properties (typically referred to as claims) from client-side code. As soon as this is allowed from client-side code, anyone could set such properties for anyone else. That's why setting custom claims for a user is only possible with Firebase's Admin SDKs, which are designed to run in a trusted environment - such as your development machine, a server you control, or Cloud Functions.
There are a few other options, but it's important to realize they're all implemented on top of the above approach.
There is an experimental extension that allows you to set auth claims by writing a document into Firestore, which something like this (JavaScript syntax, but the Flutter code will be similar):
db.collection("user_claims")
.doc("abc123")
.set({
role: "admin",
groups: ["example1", "example2"],
});
Now of course you'll want to make sure that you secure writing to the user_claims collection, as otherwise you'll end up with the same security risk I mentioned in the first paragraph, where everyone can claim any role they want.
Alternatively you can write your own server-side API (for example on Cloud Functions) that you expose to your application, and that then calls the Admin SDK. Here too, it is important to secure access to this API, to ensure only authorized users can call it.

How do I allow a role insert into a table (for signup), but after that deny further inserts for that user?

That's my question. I am using Hasura, and defining 'user' permissions.
Users are of course allowed to modify their own information, and not allowed to insert new records into my users table.
But when they signup, they should be allowed to insert themselves. So how can I define this permission?
To make my scenario more clear:
I have a React app, that uses an external OpenID provider. So a new user signs up there, and the provider returns a JWT to my app, containing a user I've never seen before.
My app does not know that, it just uses the access token to send to the Hasura backend to retrieve further info about this user, using the 'user' role. But it uses a query which will automatically insert the user if not found.
There's really not a safe way to allow sign-ups without involving a backend service. It is a very bad idea to allow anonymous inserts into your user table, even if you added a unique constraint against a user ID or email address.
If you have the option of using NextJS, see the Hasura example for configuring NextAuth. This works by configuring your app with a protected API route that uses your Hasura app's ADMIN_SECRET to insert new users who have authenticated with a third-party.
If NextJS isn't an option, Hasura's Auth0 example similarly uses a callback method to insert an authenticated user if they don't exist.
In the user table, for the user role, you need to add a permission with custom check. And the check should be user_id equals x-hasura-user-id.
{"id":{"_eq":"x-hasura-user-id"}}
For non-logged-in users, leverage the anonymous role by setting the permissions that make sense for your use case: https://hasura.io/docs/1.0/graphql/manual/auth/authorization/common-roles-auth-examples.html#anonymous-not-logged-in-users
Edit after the comment:
Ah, I see. When the user comes to your app, your app goes and retrieves some data that it expects every user should have (for example perhaps the user info store on the user table). But since it's a new user, this info is not there.
At this point, your React app knows that:
there's someone with a legitimately signed JWT cookie (use a library to verify the signature) and
there's no user info from the backend. Therefore, the React app shows
a "Welcome new user, wait while we're setting up your account".
Then
the React app makes a mutation to a signup Hasura action you'll
prepare. Once that returns, you proceed as usually (redirect the user to their home page).
use hasura action handler instead. Inside your handler, do a check if the user already exists or not. If not then insert a new row.

Meteor User Account Settings email validation

I am using Meteor user accounts api to create user accounts.
https://github.com/meteor-useraccounts/core/blob/master/Guide.md
How to add email restriction to particular domain such as only #mydomain.org so that only those users with the domain will be allowed to log into the system while other users with other domains such as #gmail.com would not be able to log into the system?
There is this (unfortunately) undocumented Accounts.config which is part of accounts-base. It allows you to set a email domain restriction for accounts creation. This your app won't allow any accounts to be created that are not part of this domain:
Put the following in server and client startup code to configure the accounts package:
Accounts.config({
restrictCreationByEmailDomain: 'mydomain.com'
})
The source documentation says on this particular option
#param {String | Function} options.restrictCreationByEmailDomain If set to a string, only allows new users if the domain part of their email address matches the string. If set to a function, only allows new users if the function returns true. The function is passed the full email address of the proposed new user. Works with password-based sign-in and external services that expose email addresses (Google, Facebook, GitHub). All existing users still can log in after enabling this option. Example: Accounts.config({ restrictCreationByEmailDomain: 'school.edu' }).
Source code of the Account.config method: https://github.com/meteor/meteor/blob/devel/packages/accounts-base/accounts_common.js#L170
I would recommend using accounts-password package to manage user creation and authentication.
With Accounts.createUser method you can easily create an user where you can apply any kind of check. In your case add a regex check to make sure the email address comes from your domain before calling the Account.createUser method.

How to use allow deny rules with userId when you have implemented your own user login/logout?

I've implemented my own user login and logout system by just setting a session variable and checking it in my main template so I will display a login screen if not logged in and display the app if I am logged in (this is typically how I did things with PHP).
I did this instead of using any of the built in user account systems because I needed to implement my own login password check to a legacy web service.
Because of this, it seems I'm running into trouble now because things like allow/deny rules for my file uploads don't seem to have the userId:
download: function (userId, doc) {
console.log("userId ", userId);
return true;
}
This prints : userId null
So, I'm unable to implement any logic here based upon whether the user is logged in or not.
So, is there a way for me to tell the meteor accounts system what my userId is when I perform my custom login? --- which I presume would then make it available here in the download allow/deny rule?
EDIT: I've had to implement a custom login handler with Accounts.registerLoginHandler. Doing this caused the meteor system to set the userId in the built in accounts system, which allowed it to be passed into the allow/deny functions... but my question still stands. I would like to know if there is a way to alternately provide some information (say, the values of a session variable) to these allow/deny rules instead of being limited to using the built in accounts system.

get account profile after logging in?

is it possible to get the user profile pic after the user has signedup and then signed in using meteor accounts. I tried querying
Meteor.user().services.google
or
Meteor.user().services.facebook
but I Meteor.user().services comes out as undefined. So I'm not quite sure how to access the profile pic.
Note I don't want to rewrite the signup process and Accounts.onCreateUser it would be much easier to simply add in user details after a user logs in.
There are two options but the best would be to write Accounts.onLogin because there is a security reason you can't access it with Meteor.user().services.
The stuff inside Meteor.user().services is sensitive, it contains the OAuth keys, your Meteor access tokens and your password hashes, this is why its blocked from access to the client. Its a very short CSF attack to stealing your account (and possibly get access to your facebook, google+, twitter, etc)
You can make it 'open'
Server side code
Meteor.publish("userdata", function() {
return Meteor.users.find({_id: this.userId});
});
Client side code
Meteor.subscribe("userdata")
More Secure Way
The more secure way would be to create the onLogin/onCreate callback, grab their profile picture url/other profile data and put it in profile and not have any publish functions like the one above
In your onLogin callback
var avatar = .. get your avatar url for whatever service you're using
//Assuming you've called the onLogin callback parameter 'info'
Meteor.users.update({_id: info.user._id}, {$set:{'profile.avatar':avatar}});
Then you can just do this when you need their profile pic
<img src="{{currentUser.profile.avatar}}"/>
This way you could do the same for the other data too! And standardize it in a way that works accross multiple services (facebook, google+, twitter, etc)

Resources