I'm trying to query Google Search Console on behalf of my users, authenticated with Google, with the scope required for GSC.
I enabled Google Sign In, in Firebase Authentication, using the client id & secret in my script. Furthermore, I have set up my consent screen with a test email, the authorized domains, and the scopes "https://www.googleapis.com/auth/webmasters", "https://www.googleapis.com/auth/webmasters.readonly".
Here is my code
React + Firebase Client
Note that the user is already authenticated by email (we connect the email account with the Google account).
import {Grid, IconButton, List,
  ListItem, Tooltip, Typography} from "@mui/material";
import {GoogleAuthProvider, linkWithPopup,
  unlink} from "firebase/auth";
import React, {useState} from "react";
import {
  useUser,
} from "reactfire";
import {defaultErrorMessage} from "../../utils/constants";
import {log} from "../../utils/logs";
import {LoadingButton} from "@mui/lab";
import {Check, Clear} from "@mui/icons-material";
const GoogleSignIn = () => {
  const {data: user} = useUser();
  console.log("user", user);
  const onGoogleSearchConsole = async () => {
    const provider = new GoogleAuthProvider();
    provider.addScope("https://www.googleapis.com/auth/webmasters");
    provider.addScope("https://www.googleapis.com/auth/webmasters.readonly");
    provider.setCustomParameters({
      access_type: "offline",
    });
    linkWithPopup(user!, provider).then((result) => {
      console.log("onGoogleSearchConsole result", result);
    }).catch((error) => {
       console.log("onGoogleSearchConsole failed", error);
    });
  };
  const onDisconnectGoogleSearchConsole = () => {
    unlink(user!, "google.com").then(() => {
      // Auth provider unlinked from account
      console.log("onDisconnectGoogleSearchConsole");
    }).catch((error) => {
      console.log("onDisconnectGoogleSearchConsole failed", error);
    });
  };
  return (
    <React.Fragment>
      <Grid
        container
        direction="column"
      >
        <List>
          <ListItem
            secondaryAction={
              user &&
              user?.providerData.some((e) => e.providerId === "google.com") &&
              <Tooltip title="Unlink Google Search Console">
                <IconButton
                  onClick={onDisconnectGoogleSearchConsole}
                >
                  <Clear />
                </IconButton>
              </Tooltip>
            }
          >
            <Button
              color="primary"
              variant="contained"
              onClick={onGoogleSearchConsole}
              startIcon={
                user?.providerData.some((e) => e.providerId === "google.com") &&
                <Check/>
              }
              disabled={
                !user ||
                user?.providerData.some((e) => e.providerId === "google.com")
              }
            >
            Google Search Console
            </Button>
          </ListItem>
        </List>
      </Grid>
    </React.Fragment>
  );
};
export default GoogleSignIn;
Script to try to query GSC.
#!/usr/bin/python
from googleapiclient.discovery import build
import google.oauth2.credentials
CLIENT_ID = 'MY_CLIENT_ID'
CLIENT_SECRET = 'MY_CLIENT_SECRET'
# Here I currently copy paste my access token and refresh token from client
at = "MY_ACCESS_TOKEN_COPY_PASTED_FROM_CLIENT"
rt = "MY_REFRESH_TOKEN_COPY_PASTED_FROM_CLIENT"
tu = "https://oauth2.googleapis.com/token"
scopes = [
  "https://www.googleapis.com/auth/webmasters",
  "https://www.googleapis.com/auth/webmasters.readonly",
]
c = google.oauth2.credentials.Credentials(
    None, # seems that we can just use refresh token, otherwise still fail with "at"
    scopes=scopes,
    refresh_token=rt,
    token_uri=tu,
    client_id=CLIENT_ID,
    client_secret=CLIENT_SECRET,
)
# v3 or v1 both fail
# webmasters = build('webmasters', 'v3', credentials=c)
webmasters = build('searchconsole', 'v1', credentials=c)
# FAIL HERE
site_list = webmasters.sites().list().execute()
print("site_list", site_list)
The error is
google.auth.exceptions.RefreshError: ('invalid_grant: Bad Request', {'error': 'invalid_grant', 'error_description': 'Bad Request'})
Which is supposed to say an issue with the tokens? I tried all solutions from invalid_grant trying to get oAuth token from google without better results
I tried the sample here https://developers.google.com/webmaster-tools/v1/quickstart/quickstart-python (which seems to work only with a Desktop oauth client id), and it works, somehow, I tried to tweak their code, but it's kind of different scenario.
Did I do anything wrong? Thanks a lot :).