Today I was experimenting with APIs , and landed on https://developers.google.com/drive/api/v3/quickstart/js while learning about Google Drive API. After reading the code example on that page , I was concerned about exposing my API Key and Client ID to public.
<!DOCTYPE html>
<html>
<head>
<title>Drive API Quickstart</title>
<meta charset="utf-8" />
</head>
<body>
<p>Drive API Quickstart</p>
<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" style="display: none;">Authorize</button>
<button id="signout_button" style="display: none;">Sign Out</button>
<pre id="content" style="white-space: pre-wrap;"></pre>
<script type="text/javascript">
// Client ID and API key from the Developer Console
var CLIENT_ID = '<YOUR_CLIENT_ID>';
var API_KEY = '<YOUR_API_KEY>';
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';
var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
}).then(function () {
// Listen for sign-in state changes.
............. βοΈ ...........
Top comments (9)
This is a common problem with client applications. The same problem also affects mobile apps. Consider API keys and other secrets not to be secret anymore when you decide to bundle them with your client apps. See also: developer.okta.com/blog/2019/01/22...
However, when you set up Google Drive API credentials for the first time Google guides you through a small questionnaire and also asks you how and from where you want to access their APIs:
One of those options is "Web browser (JavaScript)". When you select that option the only credential you are offered is the client ID, which is not a secret. It's basically just a unique identifier for your application. You can safely embed that in your JS code. Nothing to worry about.
Have you tried that instead? Does it work in your case?
Other than that, just in principle when you're dealing with 3rd party API keys and secrets, your best bet is to run your own web server and web API, and let that talk to 3rd party APIs on behalf of your client app.
That way you don't expose your secrets. However, it's up to you to implement your own auth schemes in order to limit and guard access to your own web API.
In case of Google Drive access, you're basically asking your end users to trust your app with their confidential documents. Here it's a bit more reassuring to let your client app do the talking directly with Google's API, and not through your service as a man in the middle.
I wouldn't use SECRETS on the Client-Side since they risk exposing for the end-users. You can write a wrapper in Back-End stack like nodejs, c# etc and call those from your front-end and allow only your particular origin to that wrapper api and makesure your wrapper api has authentication so people cant bypass CORS from postman and such things.
Unfortunately they can be bypassed:
medium.com/netscape/hacking-it-out...
So basically you cannot spoof it directly with JavaScript in the browser, but you can do it in a programmatically way.
Regarding Postman I am not aware if it allows or not to fake the origin header.
The lesson to take from here is that the CORS is a good protection on the browser side but not that hard to bypass.
The WHO vs WHAT Is Accessing the API Server
A usual misconception among developers it's WHO vs WHAT is accessing the API server.
For a better understanding off the difference between WHO vs WHAT is accessing your API server, I recommend you to read this section of my article, but I will extract here some lines of it:
Without going into more detail, I want to say that any API server will have a hard time to figure out the WHAT bit, that will misuse and abuse of WHO the API server thinks is talking with.
I know we can bypass CORS from tools like postman since it is browser based protection only so I wanted to say to add authentication since cors will be bypassed from other means of making calls. Maybe my framing of sentence made you think I was saying cors canβt be bypassed
Yes, it made me thought that you believed that CORS was enough to protect the API.
Yes it's safe. You aren't exposing any secret key. And your app key is linked to your domain (myapp.com or whatever). So a hacker can create a malicious duplicate of your app only if they also serve it from your domain, which they can't.
This is not true at all.
API_KEY
should definitely be kept secret. The "domain check" only works inside a browser and is easily spoofed by tools like Postman.The reference OP provided explicitly states that this method is not suited for production:
As I understand it, both the id/key are just ways of identifying the user to Google (developers.google.com/maps/premium...).
In any situation if you can put an identifier on the server, so that all calls from the browser (client) have to go through the server and the identifier is kept secret from the browser, that's great. But some apps don't have a server - they are purely client side (browser based) and in that case you have to put your identifiers for your app somewhere in the app's code. There's no choice.
I think itβs always good to put your API keys in .env file or other places. Safety concerns if you put it in the client side.