来自 https://www.googleapis.com/oauth2/v4/token 的意外响应。不给"access_token",只给"id_token"



我试图获得一个访问令牌,所以我可以使用谷歌云TTS。

这看起来是我唯一能做的。我正在使用Cloudflare worker,因为它需要在浏览器上运行,所以我认为一些库可以促进我不能使用的工作。

我在实习,给我这个任务的人给了我这个文件。

{
"type": "service_account",
"project_id": "...",
"private_key_id": "...",
"private_key": "-----BEGIN PRIVATE KEY-----nMII...NF0=n-----END PRIVATE KEY-----n",
"client_email": "...",
"client_id": "...",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "..."
}

我已经可以通过将文件的路径作为特定的环境变量来使用该文件创建音频,然后使用gcloudCLI工具来打印访问令牌,然后我使用它。但我不知道符号变了。

现在我正在尝试使用JWT令牌。

这是我创建用来测试JWT的文件。

它创建了JWT,但是当我在响应中执行POST请求以获取access_token时,我只得到id_token

const jwt = require("jsonwebtoken");
const credentials = require("./credentials.json");
const corsHeaders = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS",
"Access-Control-Allow-Headers": "Authorization",
"Access-Control-Allow-Credentials" : true,
}
async function main() {
const JWTToken = jwt.sign({
"iss": credentials.client_email,
"sub": credentials.client_email,
"scope": "https://texttospeech.googleapis.com/$discovery/rest?version=v1",
//"aud": credentials.token_uri,
"aud": "https://www.googleapis.com/oauth2/v4/token",
"exp": Math.floor(+new Date() / 1000) + 60 * 45,
"iat": Math.floor(+new Date() / 1000),
}, credentials.private_key, {
"algorithm": "RS256",
"header": {
"kid": credentials.private_key_id,
"typ": "JWT",
"alg": "RS256",
}
});
const JWTBody = new URLSearchParams();
JWTBody.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
JWTBody.append("assertion", JWTToken);

let JWTResponse;

try {
//JWTResponse = await fetch("https://oauth2.googleapis.com/token", {
JWTResponse = await fetch("https://www.googleapis.com/oauth2/v4/token", {
method: "POST",
body: JWTBody,
});
} catch (e) {
console.error(e);
return new Response(`${e.name}: ${e.message}`, {
status: 500,
statusText: "Internal Server Error",
headers: new Headers(corsHeaders),
});
}

let JWTResponseBody;
if (JWTResponse.ok) {
JWTResponseBody = await JWTResponse.json();
console.log("JWT", JWTResponseBody);
console.log("JWT ACCESS_TOKEN", JWTResponseBody["access_token"]);
} else {
console.error("HTTP status code: ", JWTResponse.status, JWTResponse.statusText, JWTResponse);

return new Response("HTTP status code: " + JWTResponse.status + JWTResponse.statusText, {
status: 500,
statusText: "Internal Server Error",
headers: new Headers(corsHeaders),
});
}
const TTSGoogleAPIsEndpoint = new URL("https://texttospeech.googleapis.com");
const TTSRESTResources = {
synthesize: new URL("/v1/text:synthesize", TTSGoogleAPIsEndpoint),
list: new URL("/v1/voices", TTSGoogleAPIsEndpoint),
};
let response;
try {
response = await fetch(TTSRESTResources.synthesize, {
method: "POST",
headers: new Headers({
"Authorization": `Bearer ${JWTResponseBody["access_token"]}`,
"Content-Type": "application/json; charset=utf-8",
}),
body: JSON.stringify({
"audioConfig": {
"audioEncoding": "LINEAR16",
"pitch": 0,
"speakingRate": 1
},
"input": {
"ssml": "<speak> <emphasis level="strong">To be</emphasis> <break time="200ms"/> or not to be? </speak>"
},
"voice": {
"languageCode": "en-US",
"name": "en-US-Standard-A"
}
}),
});
} catch (e) {
console.error(e);
return new Response(`${e.name}: ${e.message}`, {
status: 500,
statusText: "Internal Server Error",
headers: new Headers(corsHeaders),
});
}
if (response.ok) {
const audio = await response.json();
console.log(audio);
} else {
console.error("HTTP status code: ", response.status, response.statusText);
console.log(response.headers.get("WWW-Authenticate"));
return new Response("HTTP status code: " + response.status + response.statusText, {
status: 500,
statusText: "Internal Server Error",
headers: new Headers(corsHeaders),
});
}
}
main();

当我运行代码时,它打印如下:

(node:53185) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
JWT {
id_token: 'eyJh ... THE ID_TOKEN ... BSZw'
}
JWT ACCESS_TOKEN undefined
HTTP status code:  401 Unauthorized
Bearer realm="https://accounts.google.com/", error="invalid_token"

我已经控制台记录了JWTToken,并对邮差进行了POST请求。它给了相同的反应,只有id_token

我认为问题可能是JWT有效载荷内容,我不知道它的范围是否正确,如果我需要使用https://www.googleapis.com/oauth2/v4/tokenURL或aud的凭据中的一个。关于我需要为fetch()使用的URL也是如此。但我想我测试了所有的URL可能性,它从来没有给出access_token

谢谢


解决方案JWT有效负载上的作用域错误。必须是"scope": "https://www.googleapis.com/auth/cloud-platform",

John Hanley的回答。

您的代码指定了一个不存在的OAuth作用域(并且格式完全错误)。

async function main() {
const JWTToken = jwt.sign({
"iss": credentials.client_email,
"sub": credentials.client_email,
"scope": "https://www.googleapis.com/auth/cloud-platform",
...

URIhttps://www.googleapis.com/oauth2/v4/token正确。也可以使用auth_uri中的值:https://accounts.google.com/o/oauth2/auth.

应该可以解决JWT访问令牌的问题。如果您对文本到语音API有进一步的问题,请发布一个新的问题。

相关内容