С помощью библиотеки google oauth2 я могу успешно аутентифицировать пользователя при их первом проходе, получить их токен обновления и первый токен доступа. Пока токен не истечет, все работает как положено.
Однако, когда срок действия токена доступа истекает, мне нужно получить новый токен доступа и сохранить эти токены в моем хранилище данных, используя существующий токен обновления. Я знаю, что в документации указано, что токены должны повторно загружаться, когда срок их действия истекает, но поскольку я создаю нового клиента для каждого вызова (чтобы токены не использовались повторно между пользователями), я думаю, что клиент срывается до того, как токен получает шанс освежиться.
Проверяя, что делает библиотека, вызывая фактический API Google, я должен получить новые токены доступа, вызвав метод client.refreshAccessToken()
, ответ на этот вызов дает мне ошибку invalid_grant
Bad Request
. Я сравнил фактический запрос api, который делает этот метод, с запросом на google oauth2 Playground, и эти два вызова идентичны - хотя их призыв обновить их токены работает, а мой - нет.
Приложен мой код в том виде, в каком он сейчас стоит. Пожалуйста, пришлите помощь - у меня не осталось волос, которые нужно выдергивать!
const { google } = require('googleapis')
const scopes = [
'https://www.googleapis.com/auth/spreadsheets.readonly',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/drive.readonly'
]
module.exports = (env, mongo) => {
const getBaseClient = () => {
const { OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL } = env.credentials
return new google.auth.OAuth2(
OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL
)
}
const getNewAccessTokens = async (authId, refreshToken) => {
const { tokens } = await getBaseClient().getToken(refreshToken)
await mongo.setAccessTokensForAuthUser(authId, { ...tokens, refresh_token: refreshToken })
return tokens
}
const getAuthedClient = async (authId) => {
let tokens = await mongo.getAccessTokensForAuthUser(authId)
if (!tokens.access_token) {
tokens = await getNewAccessTokens(authId, tokens.refresh_token)
}
const client = getBaseClient()
client.setCredentials(tokens)
if (client.isTokenExpiring()) {
const { credentials } = await client.refreshAccessToken()
tokens = { ...credentials, refresh_token: tokens.refreshToken }
await mongo.setAccessTokensForAuthUser(authId, tokens)
client.setCredentials(tokens)
}
return client
}
const generateAuthUrl = (userId) => {
return getBaseClient().generateAuthUrl({
access_type: 'offline',
scope: scopes,
state: `userId=${userId}`
})
}
const getUserInfo = async (authId) => {
const auth = await getAuthedClient(authId)
return google.oauth2({ version: 'v2', auth }).userinfo.get({})
}
const listSheets = async (authId) => {
const auth = await getAuthedClient(authId)
let nextPageToken = null
let results = []
do {
const { data } = await google
.drive({ version: 'v3', auth })
.files.list({
q: 'mimeType = \'application/vnd.google-apps.spreadsheet\'',
includeItemsFromAllDrives: true,
supportsAllDrives: true,
corpora: 'user',
orderBy: 'name',
pageToken: nextPageToken
})
nextPageToken = data.nextPageToken
results = results.concat(data.files)
} while (nextPageToken)
return results
}
return {
generateAuthUrl,
getUserInfo,
listSheets
}
}
1 ответ
Я решил свою проблему.
Я объединял access_codes
с refresh_tokens
и полагал, что код, который вы получаете от URL-адреса аутентификации, был refresh_token, сохранял его и пытался повторно использовать, чтобы получить больше access_tokens
. Это не верно. Не делай этого.
Вы получаете access_code
из URL-адреса аутентификации, и при первом использовании его с методом client.getToken(code)
вы получаете refresh_token
и access_token
.
Я приложил обновленный и рабочий код, если кто-то захочет его использовать.
Я также должен упомянуть, что я добавил prompt: 'consent'
к URL-адресу аутентификации, чтобы вы всегда получали access_code
, которое можно использовать для получения refresh_token
, когда кто-то повторно аутентифицируется (как если бы вы не , то вызов client.getToken()
не возвращает refresh_token
(часть того, что меня изначально сбивало с толку).
const { google } = require('googleapis')
const scopes = [
'https://www.googleapis.com/auth/spreadsheets.readonly',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/drive.readonly'
]
module.exports = (env, mongo) => {
const getBaseClient = () => {
const { OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL } = env.credentials
return new google.auth.OAuth2(
OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL
)
}
const getAuthedClient = async (authId) => {
let tokens = await mongo.getAccessTokensForAuthUser(authId)
const client = getBaseClient()
client.setCredentials(tokens)
if (client.isTokenExpiring()) {
const { credentials } = await client.refreshAccessToken()
tokens = { ...credentials, refresh_token: tokens.refresh_token }
await mongo.setAccessTokensForAuthUser(authId, tokens)
client.setCredentials(tokens)
}
return client
}
const generateAuthUrl = (userId) => {
return getBaseClient().generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: scopes,
state: `userId=${userId}`
})
}
const getUserInfo = async (accessCode) => {
const auth = getBaseClient()
const { tokens } = await auth.getToken(accessCode)
auth.setCredentials(tokens)
const { data } = await google.oauth2({ version: 'v2', auth }).userinfo.get({})
return { ...data, tokens }
}
const listSheets = async (authId) => {
const auth = await getAuthedClient(authId)
let nextPageToken = null
let results = []
do {
const { data } = await google
.drive({ version: 'v3', auth })
.files.list({
q: 'mimeType = \'application/vnd.google-apps.spreadsheet\'',
includeItemsFromAllDrives: true,
supportsAllDrives: true,
corpora: 'user',
orderBy: 'name',
pageToken: nextPageToken
})
nextPageToken = data.nextPageToken
results = results.concat(data.files)
} while (nextPageToken)
return results
}
return {
generateAuthUrl,
getUserInfo,
listSheets
}
}
Похожие вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript / JS) и его различных диалектах / реализациях (кроме ActionScript). Включите все соответствующие теги в свой вопрос; например, [node.js], [jquery], [json] и т. д.