How can I utilize `this.userId` from a Meteor method call?

Currently I have two apps, ‘Alpha’ and ‘Bravo’, split-up to reduce code size. However now I need ‘Bravo’ app to call a Meteor method written in the ‘Alpha’ app and it needs to utilize the this.userId for security. However, currently this.userId is null if called from ‘Bravo’ app.

The ‘Bravo’ app creates users by duplicating createUser code and sharing the same database. The user can log into each app with no issues. It also connects to Alpha using:
export const AlphaDDP = DDP.connect(alpha_server_url)

However when in Bravo app if I call AlphaDDP.call('updateDoc', docId, newData)
the this.userId value prints out null in the Alpha logs. I assumed that when I connect with DDP.connect it would authenticate the connection. However this doesn’t seem to be the case.

After googling I came across Meteor.call("login") but it appears this won’t work if the user isn’t using email/password to log in.

Any ideas on how to do this? This is a big blocker for me. Any help is much appreciated!!

1 Like

Taken from http://docs.meteor.com/#/full/accountsclient

Note that AccountsClient is currently available only on the client, due to its use of browser APIs such as window.localStorage. In principle, though, it might make sense to establish a client connection from one server to another remote accounts server. Please let us know if you find yourself needing this server-to-server functionality.

So first things first, you will have to call your Alpha method from client code running in Bravo.

And to make sure that the Bravo client is logged in to the Alpha server, you can use the new AccountsClient to get a reference to a client and then use loginWithToken on it to use the already existing token in the browser localstorage to initiate authentication.

If you want to login from your server, though, I guess you can use something like this.

I also have a few apps that are split up. To be honest, I evaluated and found this whole remote-DDP-connection-based approach very cumbersome.

Instead I place all my shared code into shared packages and use them natively, relying on mongodb and the oplog to carry over the inter-app and inter-instance communication for me.

I believe a remote DDP connection has its value for smaller integrations between two apps that don’t necessarily share their databases, or those that are based on microservice architectures and some of those services do not connect to a mongodb instance.

Either way, server to server authentication over DDP is a problem and DDP authentication from a client to a remote server is still not a very straightforward thing to achieve once you begin having non-standard or non-trivial authentication requirements.

What I would have loved as an enhancement to the accounts package is to have a global configuration option to send over all authentication request to a remote server and Meteor.loginWith… methods using that config.

In fact, an overall “Send down hot code pushes from the local server but point the client app to a remote server for anything else” configuration would be extremely helpful in separating large apps or creating multiple clients to a server.

Wow thanks @serkandurusoy ! I didn’t realize this existed. Perhaps it’s something to make upgrading to 1.2 worthwhile for that app :smiley:

Also I just need to login to each on on the client, so that seems to make things easier.

However in my spike i’m stuck.

I was hoping that the instance of AccountsClient, AlphaAccounts would have connectWithPassword and just pipe everything though. I don’t understand how a user on the Bravo app can log into Alpha… if they’re not logged in then the token isn’t in local storage is it?

I tried creating an account which logs the user in. Then (while logged in) I took the token to login with AlphaAccounts.loginWithToken but unsurprisingly it errors with: “You’ve been logged out by the server. Please log in again.” (though it never logs me out on Bravo) also I tried logging out and using the same token but it gives the same error.

Another issue is that the account has to be created on the Bravo client (one way or another). AccountsClient doesn’t seem to handle creating an account in a way that makes this work (that I can see).

To make issues further complicated I eventually need the SMS login package to work on Bravo app so i’m not sure exactly how much additional complexity this adds.

Any ideas on what i’m doing wrong?

I wish we could just get a JWT back from the server and call it a day :frowning:

I was hoping that the instance of AccountsClient, AlphaAccounts would have connectWithPassword and just pipe everything though.

You could in fact kind of do this within your login method where you take the username/password combo and call the loginWithPassword methods on both connections.

AlphaAccounts.loginWithToken but unsurprisingly it errors with: “You’ve been logged out by the server. Please log in again.”

It’s my bad, I suspect you won’t be able to use a token from one app to log in to another even though they share a database.

There is also the kadira:login-state package from @arunoda which kind of looks as if it would solve your problem regardless of your login provider, be it a password or sms.

@serkandurusoy I didn’t know `kadira:login-state existed… pretty cool! However after looking at it the package can only be called from the client and not in the Alpha servers meteor method.

You’re right, using multiple apps is a major hassle.

This is a perfect example of how Meteor gets you going quickly then slowly sucks the life out of your productivity… :confused:

I think i’m going to have to roll my own secondary auth and create a JWT token and pass that back on login. Then use the token for all meteor methods so I can bypass the closed off auth. Such a bummer.

On the plus side this should make cross communication between all apps super simple. I’m going to open up an issue to see if using JWT could be a future feature for doing this. Could make things much more simple.

There seems to be some packages utilizing JWT logins which may give you a headstart.

Why don’t you place that remote method in a common package so that you can call it natively and have the oplog do the syncing for you?

1 Like

Cool, i’ll check those out!

I’ve thought about a common package but the method is for the data model so it’s a bit awkward to use a package for that, especially with Webpack (bravo only really needs a create/update on one resource).

1 Like

So after talking with Sashko in a Meteor issue I stumbled upon a possible solution for this. Instead of using the this.userId for authentication, have the client (in any app) pass the loginToken from local storage as a param. The meteor method on Alpha can just query that token to find it’s user and then you have the user ID to work with.

I doubt this will get folded into core but it seems like a sane way to authenticate client to many server method calls. Since all servers share the same user, all will have access to the token (which also expires according to the rules setup).

Well, is that not actually what loginWithToken should have been doing all along?

That makes sense :smile: I’m not sure how the this.userId is derived but it must be used in some other way.

I’ve just posted on the github issue. That may very well be a bug, or perhaps a feature request.

1 Like

Is there an API call to get the Meteor.userId from localstorage?
I use localstorage.getItem now, but I realise that makes it prone to break in case the Meteor team decides to rename the local storage entries.

You’ll just have to use that for now. You can write a test in your acceptance framworke (like Chimp/Capybara/etc…) to look for the value after logging in.