Giter Club home page Giter Club logo

Comments (53)

stubailo avatar stubailo commented on June 2, 2024

The initial outline:

Meteor guide: Accounts

Meteor's login system works pretty well out of the box, but there are a few tips and tricks to set things up just the way you want them.

  1. Picking an accounts UI package
  2. Setting up password reset, email verification, and enrollment emails
  3. Setting up OAuth login services
  4. Building your own login service
  5. Adding custom fields to the users collection and using them

from guide.

stubailo avatar stubailo commented on June 2, 2024

Let's go over this!

Accounts UI package

So accounts-ui is great for a simple demo or prototype, but isn't really customizable enough for a production app.

For production apps, we'll recommend useraccounts, which seems to be mostly maintained by @splendido. It's possible that we should actually deprecate accounts-ui if useraccounts is easy enough to use. It looks like you can just include a template, although it doesn't come with a dropdown. That seems to indicate you basically need routing in your app to use it, but that doesn't matter for the guide.

Email customization

This should be pretty straightforward, barring some of the bugs people seem to have reported with this system. We should make sure we list all of the things people might reasonably want to do here.

OAuth

If there is a Meteor package for your desired service, it's trivial. If there isn't, it's close to impossible. I should try building a new OAuth login handler and see what I run into; maybe it's not as hard as it sounds using the oauth1 and oauth2 packages.

Custom fields in user collection

I think the right thing to recommend here is making a new collection called UserProfiles or something. Adding fields to Meteor.users and messing around with them just seems dangerous, and will certainly not work if we add new fields to the collection, or switch out the database at some point.

Other possible things

  • How do you do login in a native app? Like if you're using meteor-ios?
  • What about authenticating static files?
  • Should we move a lot of the auth logic from #15 (URLs and routing) here? It might fit better in a discussion of the user system than routing. Or maybe it goes in security. It's a big cross-cutting concern! Either way, we should link there from here.

from guide.

SachaG avatar SachaG commented on June 2, 2024

I think the right thing to recommend here is making a new collection called UserProfiles or something. Adding fields to Meteor.users and messing around with them just seems dangerous, and will certainly not work if we add new fields to the collection, or switch out the database at some point.

Oh really? Actually the approach I used was to add a telescope object to Meteor.users and then have my custom properties as user.telescope.customProperty1… Although I realized later that this was a very bad idea because of Meteor's less-than-stellar support for nested fields…

A better way might've been simply prefixing the names, i.e. user.telescope_customProperty1. Or yes, using a separate collection (although I feel this might make the overall app's logic more complex and not always work with third-party packages, for example if they expect to look for a property on a user object).

Another thing we might want to cover is how to share credentials between multiple Meteor apps?

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

I'm very interested to hear from anyone that has tried the "dual" collection approach (i.e. Users and UserProfiles).

I tried it once years ago with a project and it was kind of a pain, but on further reflection it seems like the right approach. I'd love to hear what the challenges are with it.

from guide.

robfallows avatar robfallows commented on June 2, 2024

OAuth

If there is a Meteor package for your desired service, it's trivial. If there isn't, it's close to impossible.

Is that true? It took me about an hour to refactor the accounts-google stuff for our Oauth service. A good part of that time was getting the button graphic to look right for the loginButtons helper!

But, maybe some boilerplate Oauth1 and Oauth2 code would be useful.

from guide.

splendido avatar splendido commented on June 2, 2024

Hey guys, I had no time to check this new (awesome!) project and I still have no idea how you're organizing it, but I'm available to help describing how to use useraccounts packages if needed.

It looks like you can just include a template, although it doesn't come with a dropdown. That seems to indicate you basically need routing in your app to use it, but that doesn't matter for the guide.

@stubailo the atForm template works changing it's state to show sign-in, register, etc. even without routing packages so I guess it should be easy enough to put it inside a dropdown even if I never tried so far.
In case you'd really like to deprecate accounts-ui in favour of useraccounts we could try to add a loginButtons template and get something similar to the one provided with accounts-ui

as a side note, obvioulsy, I'd be more than happy to see the useraccounts project becoming the official proposed solution for the accounts UI :-)

from guide.

pcorey avatar pcorey commented on June 2, 2024

I'm not a big fan of the dual-collection approach when you're using Mongo. On a relational DB, it makes complete sense and is an awesome approach, but Mongo makes things more difficult.


Say I have 10000 users in my system. Each user has a Users document and a UserProfiles document. Users holds their email address and accounts data, UserProfiles holds their name and other settings.

Imagine I have an admin tools that shows all users in a sortable paginated table and lets me search through them by email, name, favorite color, etc... If all of this data was in Users, this search is really easy and performant:

Users.find({
  $and: [
    {"emails.0.address": {$regex: email}},
    {"profile.name": {$regex: name}},
    {"profile.favoriteColor": {$regex: favoriteColor}}
  ]
}, {
  skip: skip,
  limit: limit || 20,
  sort: {
    ...
  }
});

But because these pieces of data are broken into two different collections, that search suddenly becomes really difficult...

Are they just searching and sorting on email? Well then I can just find on the Users collection.

Are they just searching and sorting on name or favorite color? Well then I can just find on UserProfiles.

Are they searching and/or sorting on email _and_ name/favorite color? What do I do here? I'd have to pull down _all of Users_ that match their email search, do a secondary sort in memory on name/favorite, and then filter the results that don't match their name/favorite color search until I get limit results (or the other way around). Suddenly, we're pulling 10-20k documents into memory and sorting them. That's no good.


Because Mongo doesn't do joins, to make queries more performant you'd want to denormalize the email address into UserProfiles, or denormalize name/favorite color into Users. Following this to its end, why have separate collections at all? Doesn't it make more sense to denormalize all of UserProfiles into Users, and not have the separate collection at all?

Honestly, I feel like 1-1 collection mappings in Mongo are a bit of an anti-pattern in most cases. The lack of efficient joins make it really hard to work with them.

I like @SachaG's approach of using nested object or namespaced fields.

My $0.02.

from guide.

splendido avatar splendido commented on June 2, 2024

from a post in Security by @stubailo:

The thing about profile goes with some musings in the Accounts section about suggesting that people should use a separate UserProfile collection instead of attaching extra properties directly to the user object.

Just to say that useraccounts packages makes it easy to add extra fields to the user profile object at registration time.
I think suggesting the use different collections to keep users' data and promoting a package wich does exactly the opposite could appear a little illogical

from guide.

stubailo avatar stubailo commented on June 2, 2024

could appear a little illogical

Fair enough, but we need to work with what we have. As far as I know, useraccounts is the best option for the UI right now.

from guide.

splendido avatar splendido commented on June 2, 2024

makes sense!

let me know if you need some help setting things up for this chapter.

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

@pcorey the idea would be to use the UserProfiles collection for everything bar login. So you'd definitely denormalize email (and username) onto UserProfiles and then never touch the Meteor.users collection again.

So I don't think joins would be an issue..?

from guide.

zimme avatar zimme commented on June 2, 2024

I worked on a solution with a separate Profiles collection where a user could have one profile per organization. It worked out quite well with collection-helpers actually.

I had to abandon it though because of lack of time and the project was never finished so I never got the chance to go back working on that approach.

from guide.

stubailo avatar stubailo commented on June 2, 2024

@zimme do you have any data from that attempt that would be helpful here?

from guide.

stubailo avatar stubailo commented on June 2, 2024

There's one thing I haven't considered - merging accounts from different oauth providers. How important is this to people? Do many serious apps need this functionality?

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

Yeah, I've done it a few times over the course of things.

I think at least a basic outline of how to do it is useful.

from guide.

stubailo avatar stubailo commented on June 2, 2024

@tmeasday do you have a preferred package or approach for it?

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

I'm not sure what packages are out there. Our approach has tended to be something like:

Client side:

    Google.requestCredential(options, function(tokenOrError) {
        var secret = OAuth._retrieveCredentialSecret(tokenOrError);
        Meteor.call('updateAccountWithGoogleService', tokenOrError, secret, cb);
    });

Server side:

Meteor.methods({
  updateAccountWithGoogleService: function(token, secret) {
    var result = Google.retrieveCredential(token, secret);
    Meteor.users.update(Meteor.userId(), 
        { '$set': { 'services.google': result.serviceData } });
  }
})

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

Actually merging two existing accounts is probably way harder and not a can of worms I'd advise getting into (as advice to a dev).

from guide.

stubailo avatar stubailo commented on June 2, 2024

Oh, yeah for sure. I mean for new logins.

from guide.

zimme avatar zimme commented on June 2, 2024

@stubailo, I don't have anything working to show you code wise but what I was doing was.

Setup a Profiles collection, setup a .profiles() helper on the user object with matb33:collection-helpers.

This app only had enroll signup. e.g. an admin of an organization would invite a user, what would happen then were that a new user was created and on creation time a profile was created for this user and organization was added to Profiles and an enrollment email was sent out.

When the user logged in he was going to get the option to choose a profile/org after login or by default login to previously chosen profile/org, this part was never implemented.

from guide.

zimme avatar zimme commented on June 2, 2024

Me, @brettle and @splendido are working together on getting a "sane" way of adding services onto accounts here https://github.com/meteor-useraccounts/one-account/issues and https://github.com/meteor-useraccounts/one-account/wiki/Architecture.

All three of us have packages for solutions of adding services to accounts (mine isn't published) and one solution also tries to merge two existing accounts. Merging two existing accounts is an approach which pretty much all of us have discarded as a proper solution, because it's so hard to keep track of userId references that needs updating on merge.

What we're trying to do is see if we can't do changes to the accounts-* packages to facilitate the adding of multiple services to one account.

@tmeasday solution seems like a pretty easy and non-intrusive way of adding a service to an account which only seem to work if one is already logged in.

/cc @stubailo

from guide.

zimme avatar zimme commented on June 2, 2024

The only reason I wanted to use a separate Profiles collection was because of support for multiple profiles per user, i.e. one per organization.

If the app at that time didn't need support for that I would have just used the ´profile` field and populate that with strictly profile related stuff. (The insecure editing of users own profile isn't a good default as new developers misuse that functionality, but that's another discussion).

If I would have ever needed to set some user settings I would have just created a settings field and used that.

On the other hand if I were to write a package for handling user profiles or user settings. I would give the developer a choice of using the Meteor.users collection or to specify which collection to use for profiles and which to use for settings.

I'm not too fond of having 1-1 mappings of collections when there's no "extra" need for it, e.g. a Profiles collection which can just as easily live in profile on the user object. But having a Settings collection which can hold user settings and organization settings and other kinds of settings is fine.

edit:
Using Profiles and denormalizing the username and email and/or other info isn't the worst solution either.

from guide.

stubailo avatar stubailo commented on June 2, 2024

Me, @brettle and @splendido are working together on getting a "sane" way of adding services onto accounts here https://github.com/meteor-useraccounts/one-account/issues and https://github.com/meteor-useraccounts/one-account/wiki/Architecture.

What's the best way for me to keep track of the progress? Let me know if you need anything to make this a success.

from guide.

zimme avatar zimme commented on June 2, 2024

I'd say following the issues on https://github.com/meteor-useraccounts/one-account is a good start, but now that I know you're interested I'll try and keep you in the loop too.

One thing that would be very good for us to know is how likely it would be to get a PR through that breaks backwards compatibility with an option to turn on the old behaviour?

Meteor.loginWith<Service> creates a new account if it does not exist yet. This is something we wanna change, the developer could then just call the Meteor.createUserWith<Service> method if the login failed to get the old behaviour.

Our current thinking, to keep backwards compatibility, is to add an option {..., createAccount: false, ...} to have Meteor.loginWith<Service> not create an account if it didn't find one. But I'd prefer to have the new behaviour as default and specifying createAccount: true to get the old behaviour or use the above approach.

This would make loginWith<Service> behave as loginWithPassword.

from guide.

stubailo avatar stubailo commented on June 2, 2024

@zimme let's take this conversation to a different thread, perhaps open an issue on meteor/meteor?

from guide.

zimme avatar zimme commented on June 2, 2024

Fair enough, we'll probably open a WIP PR or Issue once we have gotten to the point that we're happy with the new API/behaviour.

from guide.

stubailo avatar stubailo commented on June 2, 2024

I guess I'm saying an issue would be a great place for us and others to discuss any such API changes or improvements, rather than on this thread.

I'm really excited that you guys are working on this!

from guide.

zimme avatar zimme commented on June 2, 2024

I see the benefit of that, but I think it will be more productive once we at least have a somewhat comprehensive suggestion =)

edit:
Skipping the free for all suggestions and have people come up with more hands-on alterations

from guide.

stubailo avatar stubailo commented on June 2, 2024

Okay, I have created a more complete and detailed outline for this article. See here: #68

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

One thing that occurred to me a little later is -- if we are recommending people use a Profiles collection to actually work with user data, then does it matter if they have multiple Meteor.users?

i.e it could be a 1-many mapping from Profiles -> Meteor.users.

In fact, I'm pretty sure that was the approach I took on the MentorLoop project years back. @dburles -- is that still the way the codebase works there? What are your thoughts on the approach, having worked with it a lot more than I ever did?

from guide.

dburles avatar dburles commented on June 2, 2024

That's right @tmeasday Profiles contained an array of associated userIds.

From my experience the seperate collection makes it a bunch more difficult to manage throughout the application for most cases rather than simply attaching information against the user document, but it comes down to the application.

collection-helpers/transforming the collections can certainly ease references between the two collections. You also have the issue of dealing with joined reactive publications.

I'm not sure I can really recommend one way or another, it would be helpful to illustrate the pitfalls and ways to work with either approach, (is that a cop out?) :)

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

Probably the biggest issue I see with it is the ubiquity of Meteor.userId() as a pattern. If really you want to refer to the Profile as the "user", which can have multiple userIds, then you have a bit of an issue.

In the 1-1 world, you can just set profile._id = user._id and not think about it.

from guide.

tmeasday avatar tmeasday commented on June 2, 2024

Then again, it would be pretty easy to do something like

Profiles.current = () => {
  return Profiles.findOne({userIds: Meteor.userId()});
}

from guide.

zimme avatar zimme commented on June 2, 2024

I've also thought about the approach with using a separate Profiles collection and using the profile as the "user" in the system, this would completely circumvent the problem we're seeing with having multiple accounts services for a user and trying to merge them.

Gonna play around with this approach on Sunday if I'm not too hungover.

edit: Thinking about it, it might not be as easy as that, you still need to choose which profile to connect the new account with.

from guide.

splendido avatar splendido commented on June 2, 2024

I also think the ubiquity of userId and user properties/methods won't be playing well with a separate collection. But might be my personal feeling only...

from guide.

stubailo avatar stubailo commented on June 2, 2024

OK, I think all of this discussion has convinced me that having a separate collection for user profile information is not feasible at the moment. I've rolled it back to recommending avoiding profile and adding top-level fields to the users collection. Please pull-request the outline if you have other ideas and concerns.

@splendido:

Just to say that useraccounts packages makes it easy to add extra fields to the user profile object at registration time.

I'm worried that the profile field is too tainted at this point by being public by default, plus it doesn't work well with DDP sub-object publications - it's hard to correctly publish only some fields of profile. Would it be possible to enable adding top-level fields to the users collection in your package? Do you think it's a good idea? We could also go with @SachaG's idea of prefixing the fields.

from guide.

zimme avatar zimme commented on June 2, 2024

I have no problem with using the profile field, if and only if you use it for things like first/last name, address, age, and other stuff that the user should be able to change them selfs at any time.

But it's too over used by other stuff which is pretty bad.

from guide.

stubailo avatar stubailo commented on June 2, 2024

other stuff that the user should be able to change them selfs at any time

The problem I have with this is, I can't think of any world where you wouldn't want to validate that data, which isn't possible if you use profile.

from guide.

zimme avatar zimme commented on June 2, 2024

Oh, I guess I didn't think about this stuff since I use simple-schema/collection2/autoform a lot which uses deny rules for validating data which probably override the default allow rule for profile

from guide.

splendido avatar splendido commented on June 2, 2024

@stubailo so far additional fields have been put under profile but we might think about suggesting to leverage the onCreateUser hook to move stuff in a different place.
UserAccounts takes care about additional fields validation (enforced on the server side...), so once the new user will be created with data moved to an appropriate place everything should be good to go.
Makes sense?

from guide.

splendido avatar splendido commented on June 2, 2024

See #82

from guide.

serkandurusoy avatar serkandurusoy commented on June 2, 2024

It would also be awesome if there were a suggested way of changing the name of the underlying mongodb collection, and the collection itself. Although the term "users" is self explanatory, I've come to meet teams who are not comfortable with it while onboarding with meteor and asked for ways to change it. So a "do this if you want that" note would go a long way for those coming from other disciplines.

from guide.

stubailo avatar stubailo commented on June 2, 2024

I've come to meet teams who are not comfortable with it while onboarding with meteor and asked for ways to change it.

Can you explain more? Seems like this is a pretty standard term for people who are using your app. Also, do you mean changing one of more of the following:

  1. The name in the database
  2. Meteor.users
  3. The schema of the collection

from guide.

serkandurusoy avatar serkandurusoy commented on June 2, 2024

I mean 1 & 2. I have no problem with it but had people ask me about it. So I figure, a small note about that in the guide might come in handy.

I did not hear about nor had the need myself to "change" the schema, apart from adding fields, so I can't comment on that. It is fine for me. Although, maybe a note about the use of mandatory ObjectId could be a heads up for people trying to use a common database and accounts collection between a meteor app and a non-meteor app.

from guide.

stubailo avatar stubailo commented on June 2, 2024

@robfallows, you said:

Is that true? It took me about an hour to refactor the accounts-google stuff for our Oauth service. A good part of that time was getting the button graphic to look right for the loginButtons helper!

I'm currently in the middle of writing this article, do you have any resources you could point to for some of that boilerplate code? Perhaps there's a blog post or article somewhere that talks about it?

from guide.

robfallows avatar robfallows commented on June 2, 2024

Hmm. No. I guess if you add in the time I spent looking for guidance on the web, it went way over the hour! In the end I knew that our Oauth service flows were very similar to Google's Oauth2 flows, so I stood on the shoulders of giants (i.e. I took MDG's code and made a bunch of minor changes). For our use case that was a pragmatic, if somewhat lazy, answer.

However, I'm happy to contribute. As long as the service you're wanting to write a login package for is well documented and you understand the principles of Oauth and how to hook into the flows, it's not hugely difficult. The underlying principles are fairly solid, it's just the variety of implementations which causes headaches.

I have to add that I am somewhat opinionated about Oauth - including what it's supposed to be used for, and why 3rd party service providers are not always a good idea.

from guide.

robfallows avatar robfallows commented on June 2, 2024

You know, the more I look back at what I did to implement this, the more I think that we could have a tool to generate the package code for an OAuth2 provider (well, most of the standard stuff anyway). My UI skills are legendary (in their awfulness), but I could have a play around with that and see where it takes me.

I've not looked into OAuth1 stuff at all, so have no comment on that.

from guide.

stubailo avatar stubailo commented on June 2, 2024

I'd say the best first step would be to just write down a paragraph or two about the concepts one would need to understand to build an oauth handler. If a tool pops up later we can mention it, but I'd rather have something vague that goes over the concepts rather than some long content that could get outdated fast or have small inaccuracies.

from guide.

robfallows avatar robfallows commented on June 2, 2024

I'd say the best first step would be to just write down a paragraph or two about the concepts one would need to understand to build an oauth handler.

Working on that.

from guide.

robfallows avatar robfallows commented on June 2, 2024

Help!

I'm struggling with knowing how to stop writing this thing! I covered the basic concepts of OAuth and then started in on a step-by-step guide to implementing an Imgur OAuth handler as a vehicle to demonstrate the concepts. That sounds more like a blog than a "paragraph or two" for the guide.

from guide.

serkandurusoy avatar serkandurusoy commented on June 2, 2024

@robfallows that would be a much needed blog post anyway. I'm sure @stubailo could then use the parts he feels fit for the guide as well.

from guide.

stubailo avatar stubailo commented on June 2, 2024

Yeah sounds great! I'll happily link to the detailed post.

from guide.

robfallows avatar robfallows commented on June 2, 2024

I have posted this.

(Constructive) criticism is appreciated 😄

from guide.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.