reactjsgoogle-drive-apiauthorizationgoogle-picker

Error 401 when trying to download a file after using Google Picker API


I have a react app that allows user to pipe in a particular Google Doc that we then analyze the content of. For step 1, picking the document, I'm using this nifty npm package, which also returns to me an "OAuth token", which I presume to be an access_token I can use to make API calls. I set the returned token and the data returned by the Picker API as state variables in my component, as seen below

<GooglePicker clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}
  developerKey={process.env.REACT_APP_GOOGLE_API_KEY}
  scope={SCOPES}
  onAuthenticate={(token: any) => {
    setGoogleToken(token)
  }}
  onChange={(data: any) => {
    setGoogleData(data);
    nextOnboardingStage();
  }}
  onAuthFailed={(data: any) => console.log('on auth failed:', data)}
  multiselect={true}
  navHidden={true}
  authImmediate={false}
  mimeTypes={['application/vnd.google-apps.document']}
  viewId={'DOCS'}>
  <MyCustomButton />
</GooglePicker>

I then try to call the Drive API export function to download said file in this function

const downloadGoogleDoc = async (authToken: string, fileID: string) => {
  const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
    headers: {
      'Authorization': `token ${authToken}`
    }
  });
  console.log(res);
};

However, I'm getting a 401 error when doing this. Can someone point out where I'm losing the auth? Thanks so much


Edit My entire component is below

const SCOPES = ['https://www.googleapis.com/auth/drive.readonly'];

const Project: React.FC<ProjectInput> = () => {
  const [googleToken, setGoogleToken] = useState<string>();
  const [googleData, setGoogleData] = useState<any>({});

  const [documentData, setDocumentData] = useState();

  const downloadGoogleDoc = async (authToken: string, fileID: string) => {
    try {
      const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
        headers: {
          'Authorization': `Bearer ${authToken}`
        }
      });
      console.log(res);
    } catch(e) {
      console.log(e);
      console.log(e.message);
    }
  };

  console.log(googleToken);
  console.log(googleData);

  const MyCustomButton = () => {
    return (
      <button className="mx-8 my-2 p-2 rounded bg-blue-500 text-white">
        Select From Google Drive
      </button>
    )
  }

  if (googleToken && googleData.docs) {
    downloadGoogleDoc(googleToken, googleData.docs[0].id);
  }

  return (
    <div className={`ml-${sideBarWidth}`}>
        <FadeIn>
          <div className="flex flex-col min-h-screen bg-gray-500 py-6 justify-center sm:py-12">
            <div className="py-3 sm:max-w-xl sm:mx-auto">
              <div className="bg-white min-w-1xl flex flex-col rounded-xl shadow-lg">
                <div className="flex">
                  <div>
                    <div>
                      <div className="px-8 py-5">
                        <h2 className="text-gray-800 text-3xl font-semibold">Great! Select the document you'd like us to scan</h2>
                      </div>
                    </div>
                    <GooglePicker clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}
                      developerKey={process.env.REACT_APP_GOOGLE_API_KEY}
                      scope={SCOPES}
                      onAuthenticate={(token: any) => {
                        setGoogleToken(token)
                      }}
                      onChange={(data: any) => {
                        setGoogleData(data);
                        nextOnboardingStage();
                      }}
                      onAuthFailed={(data: any) => console.log('on auth failed:', data)}
                      multiselect={true}
                      navHidden={true}
                      authImmediate={false}
                      mimeTypes={['application/vnd.google-apps.document']}
                      viewId={'DOCS'}>
                      <MyCustomButton />
                    </GooglePicker>
                  </div>
              </div>
            </div>
          </div>
        </div>
      </FadeIn>
    </div>
  );
};

export default Project;

EDIT

As @Tanaike said, I needed to include MimeType in my request, working function is below

  const downloadGoogleDoc = async (authToken: string, fileID: string) => {
    try {
      const params = new URLSearchParams([['mimeType', 'text/html']]);
      const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
        params,
        headers: {
          'Authorization': `Bearer ${authToken}`
        }
      });
      console.log(res);
    } catch(e) {
      console.log(e);
      console.log(e.message);
    }
  };

Solution

  • I believe your goal and your situation as follows.

    Modification points:

    When above points are reflected to your script, it becomes as follows.

    Modified script:

    From:
    const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export`, {
      headers: {
        'Authorization': `token ${authToken}`
      }
    });
    
    To:
    const mimeType = "###"; // Please set the mimeType.
    const res = await axios.get(`https://www.googleapis.com/drive/v3/files/${fileID}/export?mimeType=${mimeType}`, {
      // responseType: `arrayBuffer`, // or 'blob'
      headers: {
        'Authorization': `Bearer ${authToken}`
      }
    }).catch(err => console.log(err.response));
    console.log(res);  // or res.data
    

    Note:

    References: