admin管理员组文章数量:1357577
I am trying to make a drive client for my college project and I am not very experienced in it. That's why I am building the application using electron so that I can develop the application as if it was a website, but it will work as a Desktop Client. I want it to be completely client-side which means no interaction with any web server other that google for authentication purposes.
Right now, I am facing an issue with the PKCE flow in Google OAuth2.0 I am getting the following error:
GaxiosError: invalid_grant
at Gaxios._request (MY_PROJECT_PATH\drive-encrypt_(Pre-production)\node_modules\gaxios\build\src\gaxios.js:142:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async OAuth2Client.getTokenAsync (MY_PROJECT_PATH\drive-encrypt_(Pre-production)\node_modules\google-auth-library\build\src\auth\oauth2client.js:158:21)
at async file:///MY_PROJECT_PATH/drive-encrypt_(Pre-production)/index.js:154:35 {
config: {
retry: true,
retryConfig: {
httpMethodsToRetry: [Array],
currentRetryAttempt: 0,
retry: 3,
noResponseRetries: 2,
retryDelayMultiplier: 2,
timeOfFirstRequest: 1743095188566,
totalTimeout: 9007199254740991,
maxRetryDelay: 9007199254740991,
statusCodesToRetry: [Array]
},
method: 'POST',
url: '',
data: '<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'google-api-nodejs-client/9.14.0',
'x-goog-api-client': 'gl-node/20.10.0'
},
paramsSerializer: [Function: paramsSerializer],
body: '<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.',
validateStatus: [Function: validateStatus],
responseType: 'unknown',
errorRedactor: [Function: defaultErrorRedactor]
},
response: {
config: {
retry: true,
retryConfig: [Object],
method: 'POST',
url: '',
data: '<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.',
headers: [Object],
paramsSerializer: [Function: paramsSerializer],
body: '<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.',
validateStatus: [Function: validateStatus],
responseType: 'unknown',
errorRedactor: [Function: defaultErrorRedactor]
},
data: {
error: 'invalid_grant',
error_description: 'Missing code verifier.'
},
headers: {
'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000',
'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
'content-encoding': 'gzip',
'content-type': 'application/json; charset=utf-8',
date: 'Thu, 27 Mar 2025 17:06:28 GMT',
expires: 'Mon, 01 Jan 1990 00:00:00 GMT',
pragma: 'no-cache',
server: 'scaffolding on HTTPServer2',
'transfer-encoding': 'chunked',
vary: 'Origin, X-Origin, Referer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-xss-protection': '0'
},
status: 400,
statusText: 'Bad Request',
request: { responseURL: '' }
},
error: undefined,
status: 400,
[Symbol(gaxios-gaxios-error)]: '6.7.1'
}
The main part to focus here is:-
data: { error: 'invalid_grant', error_description: 'Missing code verifier.' },
I have tried and checked the following things:
- I am using Google's Desktop Client ID (So, I only have the default redirect URI)
- I have also verified that the generated code verifier matches the code challenge using online PKCE Code Generator
- I have also checked that the authorization code is only used once, and never repeated again.
This the my relevant server code (index.js):
import dotenv from "dotenv"
dotenv.config()
import fs from "fs"
import path from "path"
import { fileURLToPath } from "url"
import { google } from "googleapis"
import express from "express"
import bodyParser from "body-parser"
import mime from "mime-types"
import multer from "multer"
import cors from "cors"
import crypto from "node:crypto"
import { Readable } from "stream"
const winregModule = await import('winreg');
const Registry = winregModule.default;
import { generateAndStoreKEK, retrieveKEK, encryptData, decryptData } from "./frontend/scripts/keyManagement.js"
import { recoverPassword, generateAndStorePasswordRecovery, recoveryDataExists } from './frontend/scripts/passwordRecovery.js';
import dns from "dns"
import session from 'express-session';
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const app = express()
app.use(bodyParser.json())
app.use(
cors({
origin: "http://localhost:8000",
credentials: true, // Allow cookies to be sent with requests
}),
)
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
secure: process.env.NODE_ENV === "production",
httpOnly: true,
sameSite: "lax",
}
}));
const isDev = process.env.NODE_ENV === "development"
const frontendDirectoryPath = isDev ? path.join(__dirname, "../frontend") : path.join(__dirname, "frontend")
// Serve static files from the frontend directory
app.use(express.static(frontendDirectoryPath))
// Serve login.html at the root URL
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "frontend/login.html"))
})
const oauth2Client = new google.auth.OAuth2(
process.env.CLIENT_ID,
process.env.NODE_ENV === "production" ? "" : process.env.CLIENT_SECRET,
"http://localhost:8000/google/redirect"
);
let tokens
try {
const tokenData = fs.readFileSync(path.join(__dirname, "tokens.json"), "utf8")
tokens = JSON.parse(tokenData)
oauth2Client.setCredentials(tokens)
} catch (err) {
console.log("No tokens file found. User will need to authenticate.")
}
const PORT = process.env.PORT || 8000
const USER_PREFERENCES_FILE = path.join(__dirname, "userPreferences.json")
function generateCodeVerifier() {
return crypto.randomBytes(32).toString('base64url');
}
function generateCodeChallenge(codeVerifier) {
const hash = crypto.createHash("sha256").update(codeVerifier).digest("base64");
return hash
.replace(/\+/g, "-") // Replace '+' with '-'
.replace(/\//g, "_") // Replace '/' with '_'
.replace(/=+$/, ""); // Remove '=' padding
}
app.get("/auth/google", (req, res) => {
const pkceVerifier = generateCodeVerifier();
req.session.pkceCodeVerifier = pkceVerifier; // Store it in the session
console.log("The pkce code verifier is: ", req.session.pkceCodeVerifier);
const codeChallenge = generateCodeChallenge(pkceVerifier);
console.log("Code Challenge:", codeChallenge);
console.log("Code Verifier:", pkceVerifier);
const authUrl = oauth2Client.generateAuthUrl({
access_type: "offline",
scope: [
".profile",
".email",
".readonly",
";
],
code_challenge: codeChallenge,
code_challenge_method: "S256",
});
console.log("Generated Auth URL: ", authUrl);
res.redirect(authUrl);
});
app.get("/google/redirect", async (req, res) => {
try {
const { code } = req.query;
const pkceVerifier = req.session.pkceCodeVerifier;
console.log("PKCE Code Verifier in redirect: ", pkceVerifier);
if (!pkceVerifier) {
throw new Error("PKCE code verifier not found in session");
}
console.log("Payload to getToken:", {
code,
code_verifier: pkceVerifier,
});
const { tokens: newTokens } = await oauth2Client.getToken({
code,
code_verifier: pkceVerifier,
});
oauth2Client.setCredentials(newTokens)
tokens = newTokens
// Save tokens to file
fs.writeFileSync(path.join(__dirname, "tokens.json"), JSON.stringify(newTokens))
// Set a cookie to indicate the user is authenticated
res.cookie("authenticated", "true", { httpOnly: true, secure: true, sameSite: "Lax" })
req.session.pkceCodeVerifier = null;
// Check if a password has been set in the registry
const passwordSet = await isPasswordSet()
console.log("Is password set?", passwordSet)
if (!passwordSet) {
console.log("Redirecting to password creation page")
res.redirect("/password-creation.html")
} else {
console.log("Redirecting to password verification page")
res.redirect("/password-verification.html")
}
} catch (err) {
console.error("Error in /google/redirect:", err)
res.redirect("/?error=" + encodeURIComponent("Authentication failed. Please try again."))
}
})
Sorry in advance for the poor code formatting and if I am doing something extremely stupid here.
Let me know if you need anything else for context. And Thanks in advance
本文标签: javascriptIssue in Google OAuth flow when using PKCEStack Overflow
版权声明:本文标题:javascript - Issue in Google OAuth flow when using PKCE - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744076522a2586849.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论