Search code examples
node.jsaxiosgoogle-cloud-functionsinstagram-api

Instagram API keeps returning "You must provide a valid client_secret and code" error


Inside google cloud function, I need to exchange code for a token using instagram API.

    const code = req.body.code;
    const config = {
      headers: {
        "Content-Type": "application/x-www-form-urlencoded"
      }
    };

    const prepReq = () => {
      return axios.post(
        "https://api.instagram.com/oauth/access_token",
        qs.stringify({
          client_id: "my_insta_app_id",
          client_secret: "my_insta_secret",
          grant_type: "authorization_code",
          redirect_uri: `my_redirect_uri`,
          code: code
        }),
        config
      );
    };

    try {
      const response = await prepReq();
      if (response.data.access_token) {
        return res
          .status(200)
          .type("application/json")
          .send({
            success: true,
            access_token: response.data.access_token,
            user_id: response.data.user_id
          });
      } else {
        throw new Error(
          "Something went wrong during authorization. Please try again."
        );
      }
    } catch (e) {
      console.log(e);
      return res
        .status(400)
        .type("application/json")
        .send({
          success: false,
          message: e.response.data.error_message
        });
    }

However, it still returns error:

data: {
    error_type: 'OAuthException',
    code: 400,
    error_message: 'You must provide a valid client_secret and code'
}

I checked my secrets and ids and it's correct. Also, the API returns success response when I'm testing it using Postman.

Also, I don't know why, but sometimes, but really like once in a hundred attempts, my function returns correct response.

// edit - posting the whole google function code. As it has changed the way I pass the data (found that as a solution in axios documentation, but it's also not working), I'm posting it separately:


exports.socialAuthorizationHandler = functionsRegion.https.onRequest(
  (req, res) => {
    cors(req, res, async () => {
      const code = req.body.code;
      const config = {
        headers: {
          "Content-Type": "application/x-www-form-urlencoded"
        }
      };

      const prms = new test.URLSearchParams({
          client_id: "my_insta_app_id",
          client_secret: "my_insta_secret",
          grant_type: "authorization_code",
          redirect_uri: `my_redirect_uri`,
          code: code
      });

      const prepReq = () => {
        return axios.post(
          "https://api.instagram.com/oauth/access_token/",
          prms.toString(),
          config
        );
      };

      try {
        const response = await prepReq();
        if (response.data.access_token) {
          return res
            .status(200)
            .type("application/json")
            .send({
              success: true,
              access_token: response.data.access_token,
              user_id: response.data.user_id
            });
        } else {
          throw new Error(
            "Something went wrong during authorization. Please try again."
          );
        }
      } catch (e) {
        console.log(e);
        return res
          .status(400)
          .type("application/json")
          .send({
            success: false,
            message: e.response.data.error_message
          });
      }
    });
  }
);

Solution

  • As explained in the doc, to terminate HTTP functions you need to call one of the following method on the response object: redirect(), send(), or end().

    So you don't need to return anything (as you would do for any background Cloud Function), just call one of these Express.js methods.

    So instead of doing:

    exports.socialAuthorizationHandler = functionsRegion.https.onRequest(
        (req, res) => {
            cors(req, res, async () => {
                //...
    
                try {
                    const response = await prepReq();
                    if (response.data.access_token) {
                        return res
                            .status(200)
                            .type("application/json")
                            .send({
                                success: true,
                                access_token: response.data.access_token,
                                user_id: response.data.user_id
                            });
                    } else {
                        // ...
                    }
                } catch (e) {
                    console.log(e);
                    return res
                        .status(400)
                        .type("application/json")
                        .send({
                            success: false,
                            message: e.response.data.error_message
                        });
                }
            });
        });
    

    you should do

    exports.socialAuthorizationHandler = functionsRegion.https.onRequest(
        (req, res) => {
            cors(req, res, async () => {
                //...
    
                try {
                    const response = await prepReq();
                    if (response.data.access_token) {
                        res
                            .status(200)
                            .type("application/json")
                            .send({
                                success: true,
                                access_token: response.data.access_token,
                                user_id: response.data.user_id
                            });
                    } else {
                        // ...
                    }
                } catch (e) {
                    console.log(e);
                    res
                        .status(400)
                        .type("application/json")
                        .send({
                            success: false,
                            message: e.response.data.error_message
                        });
                }
            });
        });
    

    As per you comment above, it seems that by correctly ending the Cloud Function, you don't encounter the previous problem.