Connecting 2 apps over DDP, how to login across?

@zafaransari I agree, that sounds cool… but only if it allowed you to limit builds like this:

  • server
  • client
  • lib (both)
  • web (only on web, not compiled for Cordova at all)
  • cordova (only on Cordova, not included for Web at all)

Since that hierarchy is not supported yet and would be a significant change for existing applications, I don’t know how it would be implemented… but perhaps if someone felt up to making an optional Meteor package which modified build to respect that type of directory layout… ???

Have you looked into meteorhacks:cluster for managing the different interfaces? cluster-wide authentication is in the pipeline, but for now you can authenticate by calling the main service’s Meteor.method login (just like you would using the standard ddp.connect approach.

1 Like

Thanks @rhyslbw - I’ve looked at meteorhacks:cluster, but have yet to try it.

I’ve seen that you can call Meteor.call("login") via DDP… but I’ve not seen a good example of it… nor do I know if it can “take over” the client’s authentication (which is also a goal).

Know of any good examples you can point me towards?

@slava We’re not using DDP.connect manually (just the --server flag). We don’t use hot code push on our mobile app though. We just submit updates to app stores.

@zafaransari I do think the best answer to this is defining multiple clients (not just web and cordova) … you should be able to define as many clients as you want and build / autoupdate should handle this accordingly.

@zeroasterisk Directory Structure

* appname
  * web - Meteor application 1
    * client - Web specific client 
    * server - Server code which works with web and mobile client
    * lib - Lib code which which works with web and mobile client
  * mobile - Meteor application 2
    * client - Cordova specific client
    * server - Symlink of `../web/server`
    * lib - Symlink of `../web/lib`

With this directory structure it allows me to share server and lib code between both clients for development and building. During development, I can just spin up meteor run ios-device for general dev or meteor run ios-device --mobile-server=https://example.com:443 if I want to test connecting to my production server. When I actually go to build my mobile application, I use meteor build --server=https://example.com:443 which points to my production web-client’s server.

5 Likes

I’ve not looked into the autoupdate issue, but regarding authentication:

The author of Job Collection demonstrates remote DDP auth in this issue using the SHA256 internal package.

Bulletproof Meteor has also been an invaluable resource for me, including this lesson on Microservices which includes authentication using the user’s token. This is a paid resource, but Meteorhacks puts so much into the community for free, so it’s a good investment in the Meteor ecosystem.

4 Likes

I added a description of what I figured out at the already cross linked topic I started a while ago. If you can live without autoupdate in your mobile app (or the desktop app, depending which way you set this up) then DDP_DEFAULT_CONNECTION_URL could be the solution.

I know @PolGuixe is playing a bit with this and ended u popening a couple of issues about this.
one is meteor#3909 and the other one, accidentally, is on useraccounts

He has a couple testing repos you can find the link for in the above linked issue and I’ve managed to get useraccounts autentication over DDP as explaind in this one

Essentially to get packages (any package) using Meteor methos working also over DDP connection you have to hack Meteor.call to get it executed on the remote DDP connection instead on the local one.

To do so, put the following code on some client file so to have it executed as soon as possible after app load:

remote = DDP.connect(remoteUrl);
Meteor.call = function(){
  return remote.call.apply(remote, arguments);
};

Hope this might help!

8 Likes

Ahoy. Seems like I found a solution to both use --mobile-server to connect to a remote server, and get hot code pushes from a server holding your actual Cordova-specific code.

Package here: https://github.com/gwendall/meteor-remote-autoupdate

What it does is allowing you to define the URL where Autoupdate will watch code changes from. Would be nice to have such an option in core though. Tested locally, seems to work fine.

3 Likes

I presume your package method needs to be called after mucking with meteor as in this linked thread?

connectToExistingBackend = function(url) {
  //
  // make a remote connection and set the global connection object to it
  Meteor.connection = DDP.connect(url);
  // make sure Accounts uses that connection
  Accounts.connection = Meteor.connection;
  //
  // this is copied from ddp/*/web.browser/packages/ddp.js
  // it makes sure all method calls are done with the correct connection
  //
  _.each(['subscribe', 'methods', 'call', 'apply', 'status', 'reconnect',                                         // 52
          'disconnect'],                                                                                          // 53
         function (name) {                                                                                        // 54
           Meteor[name] = _.bind(Meteor.connection[name], Meteor.connection);                                     // 55
         });                                                                                                      // 56
  //
  // we need re-declare the users collection so Meteor knows to use the remote one
  //
  Meteor.users = new Meteor.Collection('users');
  //
  // now that we have our act together, try to re-login
  // unfortunately Accounts seems to have already run before
  // we did the Meteor.connection = DDP.connect part, so we manually
  // need to re-check the loginToken or hot code pushes log us out every time
  //
  var token = Accounts._storedLoginToken();
  if(token)
  {
    Meteor.loginWithToken(token, function(err){
      // this is going to throw error if we logged out
      if(err) console.log(err);else console.log('loginWithToken');
    });
  }
}
3 Likes

It doesn’t deal with Meteor and Accounts’ connections at all. You may want to let --mobile-server handle it if you build a Cordova app.

All it does is patch the autoupdate method so that you can give it the URL you want to watch code changes.

Thank you @gwendall , very good point.

I think it is about time to be more specific about what kind of app is being developed.

My confusion about all of this stems from the fact that the original premise of Meteor+Cordova seemed to be that I can have ONE code base which will run in three different environments: desktop, mobile desktop and cordova.

Trying to separate out the mobile from the desktop will now require to sub-divide the mobile browser from the cordova version, in a way.

Over time I believe this will be flushed out more.

@jamgold I agree. An ideal solution would be to have a mobile/ folder on root (like client/) from where Meteor would automatically bundle your Cordova apps. And where it would watch code changes for them (+ eventually point your mobile visitors to).

As for the separation between mobile and desktop, I personally prefer having two separate code bases since in most cases the flow will be pretty different between those devices.

Totally agree about the flow, but you might still need/want to share certain logic, that’s at least the situation I am in right now. I am working with aldeed’s simple-schema and autoform packages, and a lot of the logic is exactly the same, just the flow and presentation are different.

I can’t wait for the day when the package.js logic, that lets you determine which of the package resources go where (server, client), are applicable to the entire application, either by virtue of directory structure or config files.

As @splendido mentioned. I have been playing with DDP.connect for some time now. My case is also very similar to everyone else´s. One DB, one server, one web client, one Cordova client.

At the beginning I was trying to put conditionals into the templates, but I started using Ionic for the Cordova client and Bootstrap for the web client, so two separate projects were a must.

My solution is very similar to @jamgold’s one, but I am still having issues with the user´s tokens onReconnect. Just one question? when are you calling your function? I just put all the code into the main.js in the client folder. In order to avoid code repetition between clients (functions, schemas, etc.) I have created a package that I have added to both projects.

I totally agree that the best would be to have a way to control which packages affect the different platforms and having support for a /cordova or /mobile folder.

I also have to say that I quite like @andrewreedy’s solution. I haven’t tried but it looks very simple.

@jamgold is the login with token working for you?

@PolGuixe, yes the token login is working (most of the time)

@jamgold, where and when are you calling the connectToExistingBackend('url') function? are you doing it at Meteor.startUp()?

No, contrary to popular belief (I believed it) Meteor.startup in the client doesn’t run when the app starts on the client, but when the DOM is ready on the client.

Since I have not checked out remote autoconnect yet, I call DDP.connect before I declare remote collections in the client code, basically in client/lib/bootstrap.js or some thing I know gets called early so as to not to mess with stuff I don’t understand

Meteor.remoteConnection = DDP.connect(RemoteDataUrl);
Meteor.remoteConnection.onReconnect = function() {
  console.log('Meteor.remoteConnection.onReconnect', arguments);
}
Accounts.connection = Meteor.remoteConnection;
Meteor.users = new Meteor.Collection('users',{connection: Meteor.remoteConnection});
//
MyOwnRemoteCollection = new Meteor.Collection('collection', {connection: Meteor.remoteConnection});
// etc.

I know longer use the connectToExistingBackend function or more specifically the hack therein that modifies the connection attribute of Meteor.methods like call, etc, because it messes with hot code push (see comment above).

I explicitly Meteor.remoteConnection.subscribe and Meteor.remoteConnection.call and so forth, and it works. Hot code pushes and Cordova.

5 Likes

And just in case you need to attempt to force an autologin
(I ran into issues with the auth token disappearing from my Meteor app, perhaps only when initializing the DDP inside Meteor.startup())

on login success:

    // backup the localStorage token/userId
    Meteor._localStorage.setItem(
      "remote.userId",
      Meteor._localStorage.getItem("Meteor.userId")
    );
    Meteor._localStorage.setItem(
      "remote.loginToken",
      Meteor._localStorage.getItem("Meteor.loginToken")
    );

on subsequent load:

// autologin with token if possible
autoLogin = function() {
  var userId = Meteor._localStorage.getItem("remote.userId");
  if (!userId) {
    userId = Meteor._localStorage.getItem("Meteor.userId");
  }
  if (!userId) {
    console.log('no autoLogin, no userId');
    return;
  }
  var token = Meteor._localStorage.getItem("remote.loginToken");
  if (!token) {
    token = Meteor._localStorage.getItem("Meteor.loginToken");
  }
  if (!token) {
    console.log('no autoLogin, no token');
    return;
  }
  userId && Accounts.connection.setUserId(userId);
  Meteor.loginWithToken(token, function (err) {
    if (err) {
      Meteor._debug("Error logging in with token: " + err);
      makeClientLoggedOut();
    }
    Accounts._pageLoadLogin({
      type: "resume",
      allowed: !err,
      error: err,
      methodName: "login",
      methodArguments: [{resume: token}]
    });
  });
}
2 Likes

I have these autoruns in my client

//
// autorun.startup._storedLoginToken
//
Tracker.autorun(function () {
  var token = Session.get('_storedLoginToken');
  // var started = Session.get('startup');
  // console.log('autorun.startup._storedLoginToken',token);
  if(token)
    Meteor.loginWithToken(token, function(err){
      // this is going to throw error if we logged out
      if(!err) console.log('loginWithToken ',token);
    });
});
//
// autorun.user
//
Tracker.autorun(function(){
  var user = Meteor.user();
  console.log('autorun.user');
  if(user)
  {
    Session.set('_storedLoginToken', Accounts._storedLoginToken());
  }
});
2 Likes