Meteor accounts with no passwords

Hi everyone,

I’m building an APP interacting with an API that has none of the classic cross website / SSO possibilities.

The user enter their email/password, my app requests the API and it answers with a token. What I want is for that to be enough to create an account on my app. No passwords.
I’ve read on the subject and a solution I’ve found is: when the user requests, I create an account (with Account.create(), server side, with a “secret key” as password). Now what I want to do is, when the API replies with the token, I login the user/client. But logically, the server cannot call Meteor.login().

Passing the secret key to the client is obviously not the right thing to do. Is there a method the server can call that will login/identify a client ? Or should I go a completely different route ?

Preface: I think no-password login is an important login model. And also, security is important (and interesting!).

You simply want the user to submit an email without a password and somehow the user is logged in…at least in a limited respect until they can verify their email? I’m not aware of a way to do this with Meteor Accounts.

However with this approach someone could create an account with a phony email, or alternatively they could guess someone else’s email and gain privileges to supply junk.

To make this more secure, I would include a step that verifies the email address, such as a verification token or link. Both a copy/paste verification token or a verification link would go through the client (please tell me you’re using https!).

Meteor has a function sendVerificationEmail and an event handler onEmailVerificationLink. Since sendVerificationEmail requires a userId, perhaps you could register the user initially with const userId = Meteor.users.insertOne({ email }) and then you’d have what you need for your login flow. (However sendVerificationEmail is part of meteor add accounts-password – argh!!)

Another fairly secure way to do this would be with an HTTP call from the frontend using fetch(), and then use Meteor’s connect integration WebApp.connectHandlers() to register HTTP middleware which responds with a Set-Cookie header that is httpOnly=true and secure=true. (This is more secure than using localStorage, which is susceptible to XSS hacking from your application code. Meteor accounts uses localStorage for its login token.) Once you successfully set the cookie, all HTTP fetch() calls from your frontend will receive that cookie, which you can use to authenticate with your server middleware.

humm no my users are typing correct email/password and authentication is done by the external API. I “just” don’t want to store that password on my side nor for them to create a new password for my app.

I essentially want to replicate the logic of a login via facebook.

Here’s what I’m doing to authenticate against an external API:

//server/main.ts
Accounts.registerLoginHandler("custom", (loginRequest:LoginRequest) => {
    if(!loginRequest.custom) return undefined;
    try {
      //Here you're doing your auth logic. Mine errors out with wrong credentials hence the try...catch.
      // make sure to use synchronous calls. Didn't test what to watch out for when using async...await 
      // not saying that it doesn't work just that I didn't need to test for it
    } catch(err) {
      console.log("Loginrequest errored");
      return undefined;
    }
    
    //some more sanity checks
    //example service data
    const serviceData = {
      id: loginRequest.user,
      firstname: firstname,
      lastname: lastname,
    }

    const serviceName = "ldap";
    return (Accounts as any).updateOrCreateUserFromExternalService(
      serviceName,
      serviceData
    );
  });

On the client:

const callback = (err:any, res:any) => {
    //err != null if failed login
    //other stuff you do after a successful login like a redirect
}
const loginRequest = {
            custom: true,
            user: user,
            password: password
        };
Accounts.callLoginMethod({
            methodArguments: [loginRequest],
            userCallback: callback,
});

It’s basically using the same methods the other accounts-modules for OAuth are using as well. But because I’m accessing an API I know I can trust (and which in turn knows that any requests come from only one particular server), I can forgo the usual cross-checking.

1 Like

Hi, thanks for the reply.

I don’t really understand the goal of your "registerLoginHandler ". Is it what is called by the client ?

I’m trying to use Accounts.callLoginMethod but unfortunately, it still requires a password, which is what I want to avoid.
How come you use a password if you identify through another API ?

There are specific atmosphere packages for magic links or login links and you can easily adapt their code to your use case.

After testing, there’s no way calling a login without a password from the client can be secured, as the client can be modified so that this login function can be called from anywhere in the code, even before the API authentication…

It of course requires a second factor in order to be secure. This is where magic links or one time passwords come into play that are sent via email to the registered user. When the one-time-password in the link is verified the user can be seen as authenticated and you can assign a login token.

I saw that Titan has a magic link authentication, maybe that approach can help you: