Reputation: 338
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
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;
#!/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 :).
Upvotes: 0
Views: 91
Reputation: 338
I solved the problem by using "gapi" (Google JS client) instead of Firebase. I used https://github.com/ph-fritsche/react-gapi for simplicity.
// component ...
const scopes = [
"https://www.googleapis.com/auth/webmasters",
"https://www.googleapis.com/auth/webmasters.readonly",
];
const gapi = useGoogleApi({
scopes: scopes,
});
const gAuth = gapi?.auth2?.getAuthInstance();
gAuth?.currentUser.listen((e) => console.log("user", e));
const onGoogleSearchConsole = async () => {
const refreshToken = await gAuth!.grantOfflineAccess();
console.log("refresh token", refreshToken);
};
const onDisconnectGoogleSearchConsole = () => {
gAuth!.currentUser.get().disconnect();
};
// ...
Upvotes: 1