Meteor 1.4.2 Accounts DDP logs out when page is refreshed

I have two meteor apps A and B. A is the backend(server + client) and B which is the web/mobile app, is a remote client( cleint only). I want my mobile app to use the backend app’s Mongo instead of its own. To make it work I created a DDP connection and set it as Accounts.connection. Now, register and login seems work fine but when I refresh the page Meteor server logs me out with the following error.

Error logging in with token: Error: You've been logged out by the server. Please log in again. [403]

This is how I am achieving connection and login via ddp to remote server


export const DDPConnectToRemote = (Meteor.isClient) ? DDP.connect("http://localhost:3000/") : {};

if (DDPConnectToRemote.status().connected){
  	Meteor.connection, Accounts.connection = DDPConnectToRemote;
  	console.log("Connected. working on getting remote users now");
    Meteor.users = new Meteor.Collection('users', {connection: DDPConnectToRemote});
    DDPConnectToRemote.subscribe('users', function() {
      var users = Meteor.users.find();
      console.log(users.count());
    });
	var token = Accounts._storedLoginToken();
  	if(token)  {
  		console.log("token: "+ 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');
	      	}
    	});
  	}
  }

others Accounts DDP logs out when page is refreshed,
Accounts-password + accounts-ui + remote users trought DDP,
Error logging in with token in two separate applications
have posted similar issues with lower version of meteor, I have tried all solutions and still cnat get past this.
Please help.
@stubailo @benjamn @martijnwalraven @sashko

1 Like

Hi @nosizejosh,

You need to put your login code within the Accounts.onLogin callback. This is explained in more detail here.

@brucejo Thank you so much for replying. Unfortunately I still haven’t been able to get it to work.

without this line, Meteor.connection, Accounts.connection = DDPConnectToRemote; , I get login forbidden error.

this is the full code, i am still getting logged out when I refresh.

Meteor.connection, Accounts.connection = DDPConnectToRemote;
// let mServiceConn = DDPConnectToRemote;
let mServiceConn= DDP.connect("http://localhost:3000/");
let mServiceAccount = new AccountsClient({'connection': mServiceConn});

// On login:
// - login to the client server through the Accounts package
// - login to the microservice server
Accounts.onLogin(() => {
  let loginToken = Accounts._storedLoginToken();

  // Use the accounts loginToken to login to the mService
  if(loginToken) {
    console.log('Logging in with token');
    mServiceAccount.loginWithToken(loginToken,
      (loginErr, result) => {
        if(loginErr) {
          console.log(loginErr.message);
        }
      });
  } else {
    console.log('Unable to log in, no token found');
  }
});

please help, I have setup a reproduction here with meteor 1.4.2.6, if it will help

I saw this and remembered:

let mServiceConn= DDP.connect("http://localhost:3000/");

Are you trying to connect to http://localhost:3000 twice from the same client? I do not think that works. You need to start meteor with a different interface port for app B.

Here is what I did for my similar scenario:
A: primary: http://localhost:3000
B: primary: http://localhost:4000 2ndary: http://localhost:3000 (users & app B collections / methods from app A)

So I run B like this:
meteor -p http://localhost:4000

For A I run like this:
meteor
which uses the default port http://localhost:3000

Ok I get what you are saying, have streamlined the code to now read as below,
I create the ddp connection onces and then assign it to parts that need it.

DDPConnectToRemote = (Meteor.isClient) ? DDP.connect("http://localhost:3000/") : {};
Meteor.connection, Accounts.connection = DDPConnectToRemote; // unable to login without this line.
let mServiceConn = DDPConnectToRemote;
// let mServiceConn= DDP.connect("http://localhost:3000/");
let mServiceAccount = new AccountsClient({'connection': mServiceConn});

// On login:
// - login to the client server through the Accounts package
// - login to the microservice server
Accounts.onLogin(() => {
  let loginToken = Accounts._storedLoginToken();

  // Use the accounts loginToken to login to the mService
  if(loginToken) {
    console.log('Logging in with token');
    mServiceAccount.loginWithToken(loginToken,
      (loginErr, result) => {
        if(loginErr) {
          console.log(loginErr.message);
        }
      });
  } else {
    console.log('Unable to log in, no token found');
  }
});

@brucejo. Do you also add this line
Meteor.connection, Accounts.connection = DDPConnectToRemote; or its equivalent, which seeks to set the meteor connection and accounts connection to the ddp connection?

Hi @nosizejosh,

Sorry, I misunderstood what you are trying to do.

My solution assumes 2 servers, one for A and one for B. My B server is really only there to serve up the client files and support Hot Code Push. All the app related communications (pub/sub and methods) occur with my A server.

If you really want to build a client only solution, you should check out this thread. They reference a package to help accomplish this here. But you lose SEO and any Hot Code Push.

If you try this, let me know if this works for you. I may want to consider it for some of my clients.

@brucejo. I am creating a service where there is a blackened app to be used mostly by businesses. This same service has a mobile component that users only will need. The user app needs to access some functionality and also user db etc from the business admin app.How do you structure such an app with meteor and get login etc working across the various apps. I was thinking micrososervices. But I am not very sure how to implement it with meteor. The alternative will be lump it all together as a normal meteor app and server all the admin code to the mobile app and vice versa.

@nosizejosh,

I had been meaning to write up an explanation of my architecture for myself at some point. Also, I am a picture person. So I created some diagrams that show the architecture.

What I am trying to do:

  1. Create a main admin application that can manage the data in the system. This application has access to all of the data that Server A publishes via Meteor.publish.
  2. Create a user level application B that has access to a limited set of data. This application has access to a subset of the data that Server A publishes.

2 Server solution

Meteor does not prescribe how to create 2 client apps connecting to one server. Meteor can connect to only 1 app by default. (maybe I am wrong about this, but I could not find anything in the Meteor docs describing how to do this).

A solution is to create 2 apps each with their own server. And a shared MongoDB.

Servers and MongoDB

  1. Create a meteor project for Server A. This project will use the default meteor port http://localhost:3000.
  2. Do not use the default Mongo DB that Meteor creates. We want to create a Mongo DB that can be shared with multiple servers. For this you will want to set up an environment variable MONGO_URL, it is described here. You will also want to make use of Oplog tailing. TheMeteorChef does a good job of giving you step by step instructions. This is a good bit of work, but it is something you will likely need to eventually understand and will need to do it when you deploy. Good to learn early.
    In my case, I set up a running Mongo instance on my dev machine and set it to run at port 27017 (mongo default).
  3. Create another meteor project for Server B. This will also reference and use the shared MongoDB.

Servers and MongoDB Colllections

OK, now you can see that Server A stores all the necessary collections in the shared MongoDB. But Server B only has collections supporting the accounts package (e.g. users colection). What does this mean?

  1. Both Server A and Server B have the accounts package installed and they each reference the same MongoDB instance. So each one can read and write to the accounts.
  2. To make my DB access simpler, I only use Server B for logging in. I do not use it to make user changes (e.g. add users, reset paswords, add authorization capabilities, etc.)
  3. Server B does not access any of the Admin or App collections.

Servers and Client application interface ports.

Client A

Client A uses the default connection to its server, http://localhost:3000. And Server A publishes all of its data on that default connection (Accounts, Admin, App). This is pretty simple and straightforward Meteor.

Client B

Client B has 2 connections.

  • Its default connection is http://localhost:4000 to its own server. This connection authenticates the user using the accounts package. That is it. To set the default connection to http://localhost:4000 you need to run meteor with the port flag -p 4000.
  • Client B has another connection, http://localhost:3000. This is normally the default connection for a meteor app, but for client B it is a foreign connection. So in my example here, this is the microservice url: MICROSERVICE_URL.
    Client B needs to authenticate against its own server and the microservice server. So it connects to the microservice for all of its APP collections and methods.
  • Client B only subscribes to a subset of the publications from Server A and Server A makes sure that all users have the correct authorization to access any of the admin collections for app collections.

Now you have it. 2 servers each server with its own client app and a shared MongoDB.

Single Server solution

My reply here is pointing you to a solution that only requires one server. I was unaware of this solution before responding to your thread.

With the single server solution you have some advantages:

  1. No need to create a 2nd meteor server.
  2. No need to create a shared Mongo DB.
  3. You will need to create a 2nd Meteor project that defines the client and utilizes meteor-build-client.
  4. This solution does not serve the client bits, so you will need to host those bits somewhere. (you will need a 2nd server, but it can be simpler than a Meteor server)
  5. Client B will not need to authenticate with its server, but it will need to authenticate with Server A (not sure how the meteor-build client package would handle including the accounts package.
  6. Because this solution does not depend on Meteor to serve the bits it does not support hot code push.

I think there are drawbacks:

  1. No hot code push.
  2. Need to support another server technology.
  3. To be investigated: using the accounts package with a meteor-build-client constructed client.

Which to choose?

My take, for me it is easier to create 2 Meteor servers because I am not that familiar with other technologies. Plus I like the hot code push technology and I plan to take advantage of it.

I can certainly see for certain applications it would be very useful to be able to create a client only build of a meteor app and host it on another server.

@brucejo Happy Anniversary and Thank you so much for doing this. Very detailed.

First, I reliased with the code below, it works( in a way, since the first time you refresh, u get the error, you have been logged out by the server, however uder_id exists, meaning log in worked, subsequent refresh doesnt show the error and user is not logged out.)

Meteor.connection, Accounts.connection = DDPConnectToRemote;
let mServiceConn = DDPConnectToRemote;
let mServiceAccount = new AccountsClient({'connection': mServiceConn});

// On login:
// - login to the client server through the Accounts package
// - login to the microservice server
Accounts.onLogin(() => {
  let loginToken = Accounts._storedLoginToken();

  // Use the accounts loginToken to login to the mService
  if(loginToken) {
    console.log('Logging in with token');
    mServiceAccount.loginWithToken(loginToken,
      (loginErr, result) => {
        if(loginErr) {
          console.log(loginErr.message);
        }
      });
  } else {
    console.log('Unable to log in, no token found');
  }
});

And I wasn’t really looking for a client only solution, just a way to achieve my setup of an admin app and at least another mobile app that gets some data from this admin app. This mobile app will require some offline functionality as well, and from research, it looks t=like it will need its own server, so your solutions may work.

My questions are as follows;

  1. Are you using your proposed structure in a production environment and how is it scaling?
  2. I am thinking I can use this structure and have app 2 be the mobile app, however i feel the app may be slow since it will have to hit the mongo db hosted elsewhere so many times,
  3. Also, wont we be replicating a lot of code, such as methods for certain functionalities which will be common for both apps? as well as certain publications?
  4. how about data integrity? if all the apps have direct access to the db, there might be inconsistency as data write read is not harmonized(not sure how else to explain it).
  5. Is its possible to see some code? maybe a link to a repo where I can see just how you are implementing all this

I am only throwing out these questions because I am no expert and I have to build this service which has at east two app, the back-end/admin app and a mobile app.

A repo to your setup would be great to help me. I have read through your setup all day, and I am sure it will solve the architecture needs I have.

I am really grateful for all your help please continue :slight_smile:

1 Like

Hi @nosizejosh,

I looked at your repo and there is only 1 server. So what I think you are doing is simply double connecting to the server (meteor default connection and the connection you do in your code) and confusing your client.

Check out this youtube video. It is a classic and shows a very simple example of a client connecting to 2 Servers and an example of Server to Server communication. Worth understanding this.

If you want to test the 2 server solution. You should update your repro to have 2 servers each using a different port.

Your questions:

  1. I am using the proposed structure in a test app I have deployed. But I think it is a stretch to say that it is deployed in a production environment. So, no.
  2. For meteor apps and meteor hosting the Mongo DB is typically hosted elsewhere. You do not host it in the same container / server typically. You will need to account for this server to server latency anyway. But I think you are confusing things. The app hits the server, the server hits Mongo. When client B connects to Server A it has the ~same latency as connecting to Server B. From there on out it is server to server communication. So Client A and Client B should have comparable latency.
  3. You seem to be missing the point. Server B is very very simple. It has no methods, it has no publications. My Server B really only has browser policies and it includes the accounts package. So no, you are not replicating code, that is the point of connecting Client B to Server A.
  4. What you are worried about is concurrent access to a Mongo DB. See this.
  5. Unfortunately, the code I am working on is proprietary. Sorry, I don’t have the time to create a complete repo.

Good luck!

@brucejo sorry I didn’t put the other server in my repo, only the one that was having trouble connecting. Thank you so much for your help. I will check and learn up. I appreciate your help.