DEV Community 👩‍💻👨‍💻

Jérôme Pott
Jérôme Pott

Posted on • Updated on

Create a service account to authenticate with Google

Introduction

When you want to use Google APIs on your website, you first have to authenticate to Google. For services like Google Map, you can simply generate an API key and pass it along with your requests. However, for services like Google Drive, the API key option is not available. Instead, you can create a service account (sort of a bot account).

Service accounts are different from user accounts (normal accounts) in that they do not have passwords (but have emails!) and cannot log in via browsers. In the case of Google Drive, for example, it means that you cannot manage the files of the service account using the website (google.drive.com), but only through the Drive API. For authentication to Google, they make use of a private/public RSA key-pairs.

How to create a service account

You can create such an account in your Google Cloud Console.
If not already done, create a new project and head to APIs and Services. Under Credentials, you can click the Create credentials button and choose Service account in the dropdown.
Once done, you will be prompted to download a JSON file containing the credentials. Keep this file safe and do not commit it to your GitHub repo.

The JSON file should look like this:

{
  "type": "service_account",
  "project_id": "xxxx",
  "private_key_id": "xxx",
  "private_key": "xxxx",
  "client_email": "xxxx@xxxx.iam.gserviceaccount.com",
  "client_id": "xxxx",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "xxxx",
  "client_x509_cert_url": "xxxx"
}

Enter fullscreen mode Exit fullscreen mode

Example: using a service account to access Google Drive

Under APIs and Services in the Cloud Console, look for the Drive API and enable it. Now you can programmatically manage files in the Drive linked to the service account you created.
However, as mentioned before, you won't be able to see them in the browser. What you can do though is to log into google.drive.com with a "real" user account, then create a folder (using the UI) and finally share that folder with the service account by providing the value of the "client_email" property in the JSON file.
Now if you upload files with the service account to this specific folder, you will see them in your user account on google.drive.com 😃
The owner of these files is still the service account, meaning that if you delete them from the user account, they will still exist in the service account.

Show me some code

For this example, we'll write code in Node.js with the help of the official npm package googleapis which contains helpers for all of Google APIs.

We first need to initialize a new client by providing our credentials and the access permissions we need (called "scopes" here). For the permission, we'll give the scope https://www.googleapis.com/auth/drive.file (more info about scopes here).

For the credentials, we need to pass the data from the JSON file. You can either read it directly from the JSON file or add it as a string to an env file and then parse it.
The getCredentials function below uses the first approach and fallbacks to the second.

Authenticating

function getCredentials() {
  const filePath = path.join(__dirname, 'credentials.json')
  if (fs.existsSync(filePath)) {
    return require(filePath)
  }
  if (process.env.CREDENTIALS) {
    return JSON.parse(process.env.CREDENTIALS)
  }
  throw new Error('Unable to load credentials')
}

async function getDrive() {
  const credentials = getCredentials()
  const client = await google.auth.getClient({
    credentials,
    scopes: 'https://www.googleapis.com/auth/drive.file',
  })

  return google.drive({
    version: 'v3',
    auth: client,
  })
}

Enter fullscreen mode Exit fullscreen mode

Uploading a file

Don't forget to specify the shared folder ID as the upload location, otherwise you won't be able to see the uploaded file in your user account.
You can find this ID easily in the address bar:
shared folder id

The following code snippet uploads a text file with a content consisting of the string "Hello World".

getDrive().files.create({
    requestBody: {
      name: 'Test',
      mimeType: 'text/plain',
      parents: [SHARED_FOLDER_ID],
    },
    media: {
      mimeType: 'text/plain',
      body: 'Hello World',
    },
}).catch(e => console.error(e))
Enter fullscreen mode Exit fullscreen mode

If everything went well, you should be able to see this file in your shared folder.

In a future blog post, I will share a real-world case about backing up data to Google Drive using Netlify Cloud Function.

Stay tuned!

Resources:
https://medium.com/@bretcameron/how-to-use-the-google-drive-api-with-javascript-57a6cc9e5262
https://cloud.google.com/iam/docs/service-accounts

Top comments (6)

Collapse
 
samuelkarani profile image
Samuel Karani Mbaabu

you can set GOOGLE_APPLICATION_CREDENTIALS=file.json to avoid parsing credentials

Collapse
 
syltty profile image
Syltty

Hi,
How can I delete files from Drive with a service account?

Or how can I get back a folder ownership once I gave it to the service account?

Thank you!

Collapse
 
mornir profile image
Jérôme Pott Author

Hi! Thanks for reading and sorry the late reply.

1) In the complete function on GitHub, I delete older backups.

2) In the share modal on drive.google.com, click on advanced and then you can click on the X to stop sharing with the service account.

Collapse
 
ajeebkp23 profile image
Ajeeb.K.P

"Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."

I'm getting this error. Any clue ?

Collapse
 
dev_user profile image
Dev User • Edited on

Hi, thank you for the article.
How to get image from drive, how can I do it, is it possible?

Collapse
 
vjnvisakh profile image
Visakh Vijayan

Awesome articles. Helped me a lot.

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post