How to add API Secret keys without revealing them to the client?

I’ve got a cloud based image host which requires authentication in the form of API public and secret keys.

In PHP the concept is simple, the Secret key is stored away from prying eyes and delivered via a CURL request in the background for example. But, as I’m new to the world of Meteor / Node.js / React I can’t figure out how I would do this?

I’ve read about the settings.json file, which can be used to store things just like this, and I know how it is defined :

{ 
  "privateKey": "Only Server can see this",
  "public" : {
    "publicKey": "Client can see this" 
  }
}

Then these vars can be accessed like so :

const PUBLIC_KEY = Meteor.settings.public.publicKey;
const PRIVATE_KEY = Meteor.settings.privateKey;

But there are 2 issues that I can’t get my head around:

  1. How do I call the PRIVATE_KEY?

I tried :

if (Meteor.isServer) {
  console.log(PRIVATE_KEY);
}

But nothing happens (in the server console). Can this only be run in the server folder?

  1. How do I use this PRIVATE_KEY in my component without revealing it to the client?

I have an image upload component, running client side, that needs access to the PRIVATE_KEY.
Do I have to move the whole component server side to do this?

Hi, yes, whole file has to be only server side to allow access to Meteor.settings.

Meteor.isServer used on client side is just condition which does not affect the fact that you are still on client so only Meteor.settings.public is accessible.

Sorry, I hit the space bar and it published the post before I had time to finish it. Couldn’t find a delete button to remove it, so had to just whip the rest up quick time :wink:

Thanks for the reply Jiri,

Forgive my ignorance but how would I even start to use a server side component on the client side?

Is it coded up as usual and then imported into the client side script inside an if(Meteor.isServer) conditional?

I don’t really understand how it works in this respect.

{ 
  "private": {
    "privateKey": "Only Server can see this"
  },
  "public" : {
    "publicKey": "Client can see this" 
  }
}

const PRIVATE_KEY = Meteor.settings.private.privateKey;

But that const can only be called server side right?

And if that is the case (as I suspect) can it be called client side with :

if (Meteor.isServer){
}

Or not?

I’m still a bit confused as to when and where Meteor.isServer could / should be used.

Only server side.

Meteor.isServer will force the compiler to only build the code for the server, so, no, it cannot be called in the client.

1 Like

What 3rd party API calls are you using? All my 3rd party API calls originate from the server (similar to PHP).

Ahh thank you that clears it up perfectly. I just couldn’t find that simple straight to the point explanation anywhere!

I assumed it meant that any code inside if (Meteor.isServer) would only execute server side, the client could still see it but not be able to run it. The fact that it’s only compiled server side makes so much more sense now and has alleviated a lot of fears that I was starting to get bogged down with in terms of security.

So basically, anything I put inside of if (Meteor.isServer) {} won’t ever get compiled into the client source?

Out of interest how does it render in the client? If I throw a function in there in a component and that function is essentially invisible to the client, how does it know what to do?

I’m using the cloudinary api.

Not sure where @rjdavid got that idea from, but code sent to the client, even in a Meteor.isServer block will be compiled and sent to the client.

The only thing Meteor does is set Meteor.isServer to false, and then it behaves like any old if statement on the client.

The only way to totally prevent compiling and sending code the the client is to put it in a /server/ folder. (Using imports in the imports folder works as well, but requires you to be careful where you import what files)

With all browser applications you have code executing in two places, the server and the client. In Meteor the files that are compiled and executed in each place depends on if they are in a server or client folder, or with imports, if they were imported from a file loaded on the client or server.

Once they are loaded Meteor sets isServer and isClient and executes the files completely

This means if you have Meteor.isServer code in a client only file, it won’t run on the server at all (it will be loaded on the client but never run).

2 Likes

Okay back to being confused again now…

What I can’t seem to understand is the following…

If anything I render in the Server folder has to be imported before the client can use it how do I keep anything secure?

For example, I need to supply API keys for a file upload component to work.
From what I’m gathering here, the entire file uploading component will need to be created server side (so the keys remain secret) so how does the client ever get to access the upload component when it exists only on the server?

Sorry if this sounds dumb, and I’m missing something really obvious, but I’m really having trouble trying to understand how things like this work with a pure JS stack. As I mentioned before, wth PHP it’s almost out of the box that nothing sensitive will ever get rendered to the browser unless you do something really silly, all I seem to have found today after much googling are tutorials and posts that have gone off on wild tangents detailing lists of packages that need to be installed, and folders and files that need to be created just to accomplish this simple task. But more than that, all of it left me more confused than where I was where I started looking!

Just found this in the docs (pretty much what @coagmano said above).

Meteor.isServer can be used to limit where code runs, but it does not prevent code from being sent to the client. Any sensitive code that you don’t want served to the client, such as code containing passwords or authentication mechanisms, should be kept in the server directory.

But it doesn’t mention how I use anything that is in the server directory?

Do I export functions? Components? Methods? And if so, how does the sensitive data inside these things remain sensitive once it hits the client?

@coagmano, thanks for the correction

When you want the client to tell the server to do something, use a method.

Methods are the Meteor equivalent of an ajax / API call to the backend

Once the server has done the thing you want it to do, you can return a status or value to the client, just as you would with a HTTP API

1 Like

I was just re-reading these Meteor docs.

This is the part I can’t seem to get my head around…

Create a file server side :

// In a server-only file, for example /imports/server/mmr.js
export const MMR = {
  updateWithSecretAlgorithm(userId) {
    // your secret code here
  }
}

Now call a method client side :

// In a file loaded on client and server
Meteor.users.methods.updateMMR = new ValidatedMethod({
  name: 'Meteor.users.methods.updateMMR',
  validate: null,
  run() {
    if (this.isSimulation) {
      // Simulation code for the client (optional)
    } else {
      const { MMR } = require('/imports/server/mmr.js');
      MMR.updateWithSecretAlgorithm(this.userId);
    }
  }
});

The docs say

Note that while the Method is defined on the client, the actual secret logic is only accessible from the server.

But isn’t this line…

const { MMR } = require('/imports/server/mmr.js');

… going to expose anything you have in here :

updateWithSecretAlgorithm(userId) {
    // your secret code here
  }

Or does require not import the file?
If that is the case it makes sense, just want to be sure.

Okay that makes sense! I think this is what’s been so confusing for me.

Pretty much everything I’ve read, watched and listened to seems to relate Methods primarily to MongoDB calls.

Is there a really super simple example of creating and using a Method somewhere that you could point me to please? The one I referenced above in the Meteor docs is just confusing as it includes calls to the user object, other functions and validation packages to be installed. I know I will need to use something to validate the data, but I just want to see a bare bones example of how Methods work.

One other thing, it says the Method code in the example above must be available on both the client and server. Does that mean I have to run the exact same code in two places?

Sure thing!

Say I want a method that does something trivial like add a number defined in settings.json to the supplied argument:

// settings.json
{
  "numberToAdd": 42
}
// imports/server/methods.js
import { Meteor } from 'meteor/meteor'; // not strictly needed but good practice

Meteor.methods({
  addToSecretNumber: function(input) {
    const secret = Meteor.settings.numberToAdd;
    // Edit: this line was a silly mistake:
    // return input + numberToAdd
    // it should be:
    return input + secret;
  }
});
minor note - Object method shorthand

// Note the meteor docs extensively use the ES6 object method shorthand that looks like this:
Meteor.methods({
addToSecretNumber (input) {
const secret = Meteor.settings.numberToAdd;
return input + secret;
}
});

// imports/client/example.js
//          ↓ Method name        ↓ argument to method
Meteor.call('addToSecretNumber', 10, function (error, result) {
  //                                 ↑ callback function
  // Here result === 52 
  // and Meteor.settings.numberToAdd === undefined
});

The example you quoted from the docs does a couple of tricky things to make sure the secret doesn’t leak

  • It puts the secret code in a server folder (so it wont be sent to the client despite the require statement (usually any import/require from a client file will include the imported file in the client bundle)
  • It checks for this.isSimulation inside the method to determine if it’s running in the client or the server before checking to run the secret code (Meteor.isServer/isClient works here too, but this example is specific to Methods)

The use of validated methods is also unhelpful when just learning how standard methods work

1 Like

Thank you sooooo much!

That’s all I needed to understand how they work.
If only the docs explained things in such a straight forward way that newcomers can understand.

There’s been more than one occasion on my Meteor journey so far that’s left me feeling like the village idiot not being able to grasp what appear to be simple concepts. A lot of it has been down to overly complicated examples and missing information. That code from the docs is a perfect example. Try it and it will fail as ValidatedMethod is not defined, and they give you no clue where to import it from.

I can’t thank you enough, just this simple thing has brought so much into focus for me :slight_smile:

One thing for anybody else reading this in the future. You need to swap out the following line in @coagmano’s code :

return input + numberToAdd;

For this one :

return input + secret;

Whoops! That’s what I meant. I’ve edited that in

I found that the Meteor Guide is meant to be read top to bottom. They do explain on that page what a validated method is, how each part of it is constructed onto a simple Method declaration, and what advantages it brings, as well as how to install and use the ValidatedMethod class.
If you jump into the section on hiding server side code, you miss the context and it won’t make sense.

If you want a fast answer, look at the Meteor Docs, which give an example using the simple form and explains what values and methods it exposes to you to build with

That said, when I started working with Meteor, a senior dev recommended that I should read through the guide as part of my training and it was immensely useful

3 Likes