Skip to content
Advertisement

How to refresh Instagram Basic Display API token automatically?

I’m trying to set up an instagram feed (just images and links) of a public instagram account for my Nextjs app.

I know I need to use the Instagram Basic Display API and get a Long-Lived Access Token but it expires after 60 days and I don’t want to have to manually refresh it. Does anyone know a good, preferably free, way of doing this automatically?

I have looked at instagram-token-agent but that setup uses Heroku and an add-on that costs $30 a month which seems high.

Any ideas or links would be really helpful, thanks!

Advertisement

Answer

I eventually ended up using Google Cloud Secret Manager.

Overview: Secret Manager stores long-lived token and every rotation triggers a pub/sub that then triggers a cloud function. The cloud function refreshes the token for a new one and then adds a new version to the secret.

Create New Secret

Name it “instagram-token” and add your long lived token as the secret value. For now leave everything else default and create secret.

Create a service account for secret manager

In your terminal:

gcloud auth login

then

gcloud beta services identity create --service "secretmanager.googleapis.com" --project "YOUR_GCP_PROJECT_ID"

It may ask you to install gcloud beta commands.

IMPORTANT: Make sure you note down the full name of the service account returned in the terminal. If you have lost it, run the same command again.

Create pub/sub topic

Create a new topic and name it “instagram-token-refresh”, untick ‘add a default subscription’.

Give secret manager permission to publish pub/sub

In your new pub/sub topic go to permissions -> Add Principle. Search and add the service account name added above. service-{id}@gcp-sa-secretmanager.iam.gserviceaccount.com. Add the new role Pub/Sub Publisher

Add rotation and pub/sub to secret

  1. Go to your “instagram-token” secret and “edit secret”.
  2. Rotation -> custom -> every 50 days
  3. Notifications -> Add Topic -> Select “instagram-token-refresh”
  4. Save

Now every 50 days your “instagram-token-refresh” pub/sub will be triggered.

Create Cloud Function

  1. Search cloud functions -> enable -> create cloud function
  2. Function name: “Refresh-Instagram-Token”
  3. Trigger: pub/sub -> Select “instagram-token-refresh”
  4. click next
  5. Entry Point: “refreshInstaToken”
  6. Edit files:

You might need to enable to cloud build API

package.json

{
  "name": "refresh-instagram-token",
  "version": "0.0.1",
  "dependencies": {
    "@google-cloud/pubsub": "^0.18.0",
    "@google-cloud/secret-manager": "^3.10.1",
    "axios": "^0.24.0"
  }
}

index.js

// Import the Secret Manager client
const { SecretManagerServiceClient } = require("@google-cloud/secret-manager");
const axios = require('axios');

// name of function is the same as entry point
exports.refreshInstaToken = async (event, context) => {
  // check pub/sub message is rotation to prevent infinte looping
  const event_type = event && event.attributes.eventType;
  //allowing SECRET_VERSION_ENABLE lets you manually trigger this function by disabling the secret and then enabling it (rather than waiting for rotation trigger)
  if (event_type != "SECRET_ROTATE" && event_type != "SECRET_VERSION_ENABLE") {
    return null;
  }
  // secret name
  const parent = event.attributes.secretId;
  const name = parent + "/versions/latest";
  // Instantiates a client
  const client = new SecretManagerServiceClient();
  // get latest secret
  const [version] = await client.accessSecretVersion({
    name: name,
  });
  // Extract the payload as a string.
  const secret = version.payload.data.toString();

  // refresh token
  const requesturl = `https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=${secret}`;
  const response = await axios.get(requesturl);
  const data = await response.data;
  // data = {"access_token", "token_type", "expires_in"}

  // check access_token isn't null
  if (data && data.access_token) {
    // Payload is the plaintext data to store in the secret
    const newSecret = Buffer.from(data.access_token, "utf8");
    // add new secret version (the refreshed token)
    const [newVersion] = await client.addSecretVersion({
      parent: parent,
      payload: {
        data: newSecret,
      },
    });
    console.log(`Added new secret version ${newVersion.name}`);
    // get new secret version number
    let newVersionN = newVersion.name.split("/");
    newVersionN = newVersionN[newVersionN.length - 1];
    if (newVersionN > 1) {
      // if is a second version delete one before it
      const nameToDestroy = parent + "/versions/" + (newVersionN - 1);
      const [deletedVersion] = await client.destroySecretVersion({
        name: nameToDestroy,
      });
      console.info(`Destroyed ${deletedVersion.name}`);
    }
  }
};

Adding/Accessing Secrets Ref

Consume event notifications with Cloud Functions Ref

Give cloud functions permissions to Secret

  1. Go to your secret -> permission
  2. Add -> {project-id}@appspot.gserviceaccount.com
  3. Add role “Secret Manager Admin”

Accessing Secret Manager from service account

  1. Create new service account name “instagram-token”.
  2. In new service account -> keys -> add keys -> save to desktop
  3. Go to your secret -> permission -> add -> “instagram-token…gserviceaccount.com” and give the role of “Secret Manager Secret Accessor”

Setup credentials environment variable

  1. create .env.local file in next js root directory

  2. add new empty value GOOGLE_APPLICATION_CREDENTIALS=

  3. Convert JSON file to Base64 key and copy to clipboard MAC

    openssl base64 < /Users/{username}/Desktop/service-account.json | tr -d 'n' | pbcopy Convert JSON file to Base64 WINDOWS

certutil -encode service-account.json encoded.txt

  1. paste to variable so you will have something like GOOGLE_APPLICATION_CREDENTIALS=faGdfdSytDsdcDg...

Authenticating GCP in Next.js

Install @google-cloud/secret-manager npm i @google-cloud/secret-manager

const {
  SecretManagerServiceClient
} = require("@google-cloud/secret-manager");

export const getInstagramToken = async() => {
  // parse your base 64 env variable to a JSON object
  const credentials = JSON.parse(
    Buffer.from(process.env.GOOGLE_APPLICATION_CREDENTIALS, "base64").toString()
  );

  // TO DO -> CHANGE
  const projectId = "eleanor-daisy";
  const secretId = "instagram-token";

  // set up credentials config
  const config = {
    projectId,
    credentials,
  };

  // init secret manager with credentials
  const client = new SecretManagerServiceClient(config);

  const secretName = `projects/${projectId}/secrets/${secretId}/versions/latest`;

  // Access the secret.
  const [accessResponse] = await client.accessSecretVersion({
    name: secretName,
  });

  const instaToken = accessResponse.payload.data.toString("utf8");

  return instaToken;
};

Add GOOGLE_APPLICATION_CREDENTIALS and key to vercel when deploying.

Done! I might make a video tutorial on this as there’s not much out there, let me know if that would be helpful 🙂

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement