DEV Community

Ivan
Ivan

Posted on

Protecting your API keys with Next JS

This is the story of Max.

Max thought his API keys were safe because he put them inside an .env file.

Max didn't know his keys were visible in the network tab.

Screenshot from 2021-02-12 15-25-47.jpg
🥲

Max is actually me, Max is actually everyone.

Especially when we are just learning and start playing with public APIs that require a private key.

There is not a good (or any) way to protect your keys in the frontend.
One good alternative is to create a proxy in your own server, since backend code is not exposed to the browser. But many people using these APIs are beginners that don't know much about backend yet.

Well, Next JS comes to save the with its integrated and ready to use API Routes. Which is basically a Node JS environment that you can use to create API endpoints, a server ready to go.

Let's take a look.

Initial setup

First let's create a next js app.

In the terminal run



npx create-next-app next-api-key
# or
yarn create next-app next-api-key


Enter fullscreen mode Exit fullscreen mode

Then move to the app folder



cd next-api-key


Enter fullscreen mode Exit fullscreen mode

A different .env file

Once inside the project we can create a .env.local file. This looks the same as your normal .env files if you are familiar with them. The difference is that the variables inside .env.local are only available in the Node JS environment of Next. This means the browser doesn't have access to them.

So where can we use these variables?

Inside pages, there is another folder called api, any file inside here will be treated as an API endpoint.

It works more or less like this.

On the server side we have pages/api/hello.js



export default (req, res) => {
  res.status(200).json({ name: 'John Doe' })
}


Enter fullscreen mode Exit fullscreen mode

On the client side we can call.



const fetchHello = async (page) => {
  const res = await fetch(`/api/hello`); // notice the naming
  const data = await res.json();
// data = { name: "John Doe" }
  return data;
};


Enter fullscreen mode Exit fullscreen mode

Protecting the keys

So now that we know how that works, let's add a key in the .env.local file.

Let's pretend I have a key that I need to send in the url of my request.

Let's add the key inside .env.local



SECRET_KEY=someSecretKeyThatNoOneShouldSee


Enter fullscreen mode Exit fullscreen mode

And instead of using our key on the client side, we use it inside hello.js.

This variable won't work on the client anyways, read more here



// pages/api/hello.js
import axios from "axios";

export default async (req, res) => {
  const URL = `https://api.i.require.keys/?&api_key=${process.env.SECRET_KEY}`;
  const response = await axios.get(URL);
  res.status(200).json({ data: response.data })
}


Enter fullscreen mode Exit fullscreen mode

You will need to install axios, node-fetch or a similar library to manage the fetching, since fetch API is not available in Node.

Nothing really changes on the client side, but let's use axios since we already installed it for the server.



const fetchHello = (page) => axios.get('/api/hello')


Enter fullscreen mode Exit fullscreen mode

And that's about it, our key is nowhere to be seen in the network tab.

That wasn't too hard right?

Please like and share if you found this helpful.

Until next time!

Top comments (15)

Collapse
 
dance2die profile image
Sung M. Kim

And make sure the .env.* isn't included in the source code controls :)

Collapse
 
iamwebwiz profile image
Ezekiel Oladejo

Good point 👍🏽

Collapse
 
ivanms1 profile image
Ivan

For sure!

Collapse
 
marklai1998 profile image
Mark Lai

Sorry that I don’t see the point here.
What you’re doing here is just combine the proxy part and the request part together using next js

But the main issue is about the authentication method itself (aka api key) not the disclosure of it.

Google map api for example also issue api keys for frontend maps. And the api key is also well expose to the internet. But google limited the domains of request origin. Thus I cannot steal your key and put it on my site.

If you want to track user access, why don’t issue a jwt to the user(or session etc)? Why api key at the first place?

Even you secure your api key, user can still call your api unauthorised if you don’t take any security measures.

Collapse
 
ivanms1 profile image
Ivan • Edited

Hey thanks for commenting!
I think you are overthinking this a bit, this is just meant to help people do what the title says.

Let's say I am learning to code and I created a weather app and I got key to use an API. Do I "issue a jwt to the user to track user access and usage"? I don't think I know how to do that yet.

Is hiding the API key a perfect and infallible solution?

Of course not, but it's better than just having my key there for everyone to see.

Collapse
 
abdulsalam profile image
Abodunrin AbdulSalam

Totally agree with this as a beginner.
A friend once built a YouTube clone with react and because the api key is exposed, someone else went ahead and started using it which later result in some unexpected breakdown of the clone.

This is really helpful.
Thanks.

Collapse
 
marklai1998 profile image
Mark Lai • Edited

I know what you’re trying to solve. Since not all public api except you call directly from the frontend, the api key is a sensitive thing you don’t want to expose(like AWS key)

But what sounds weird is both the situation and the solution. NextJs is a React framework that built for server side rendering.
If you’re not a SSR and react developer, using NextJs will create more problems than it solve.
If you’re React/NextJs developer, I still feel weird since NextJs already a backend server that render webpage and serve to the user

What I feel about this article: You don’t wanna build a proxy server? Okay, you can use NextJs(but it’s also a server)!

Collapse
 
j0hannr profile image
j0hannr • Edited

Thanks! I used headers to pass the SECRET_KEY.

const response = await axios.get(`/api/secret/index/`, { headers: {"secret_key" : process.env.SECRET_KEY}});
Enter fullscreen mode Exit fullscreen mode
Collapse
 
bishnu47 profile image
Bishnu Das

hey I just wanted to ask is NEXT_PUBLIC prefixed secret when used in frontend.. is the secret vulnerable to the Web?

Collapse
 
ivanms1 profile image
Ivan

Yes, it is.

Collapse
 
bishnu47 profile image
Bishnu Das

Then what is the best way to hide the api-key, if I require to use some api at front-end?

Thread Thread
 
ivanms1 profile image
Ivan

Using the api folder, you can read the post for more information.

Collapse
 
ishanpro profile image
Ishan Tiwari

Can I use NEXT_PUBLIC prefix and still keep the keys safe?

Collapse
 
ognjenmarinkovic27 profile image
Ognjen Marinkovic

Cannot anyone just access the api endpoint end get the API key?

Collapse
 
winston_jademolit_a3cc56 profile image
Winston Jade Molit

Hi! In the "Protecting the Keys" section, is it acceptable to use Axios on the client side and in API routes? Won't this cause a delay due to latency?