我有一个应用程序,Google OAuth2 以前运行良好,但现在似乎有些东西发生了变化,它不再工作了。当我尝试使用 Google 登录时,我在响应正文中收到以下消息:
The verifyIdToken method requires an ID Token
起初我认为问题可能出在 Node 后端的
google-auth-library
实现上,但是,据我所知,它似乎没有改变,除非我遗漏了一些东西。
这是我在前端的内容:
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import AuthProvider from "./context/AuthContext";
import { GoogleOAuthProvider } from "@react-oauth/google";
ReactDOM.render(
<GoogleOAuthProvider clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}>
<AuthProvider>
<App />
</AuthProvider>
</GoogleOAuthProvider>,
document.getElementById("root")
);
登录.js
import { useNavigate, useLocation } from "react-router-dom";
import { AuthContext } from "../context/AuthContext";
import { GoogleLogin } from "@react-oauth/google";
import axios from "../config/axiosConfig";
const isProduction = process.env.NODE_ENV === "production";
const authContext = useContext(AuthContext);
const navigate = useNavigate();
const { state } = useLocation();
const [message, setMessage] = useState(null);
function Login() {
function handleSuccess(response) {
axios
.post("/api/auth/google", { token: response.tokenId })
.then((res) => {
const { user, isAuthenticated } = res.data;
authContext.setUser(user);
authContext.setIsAuthenticated(isAuthenticated);
navigate(state?.path || "/diary");
})
.catch((err) => {
console.log(err);
});
}
function handleFailure(response) {
setMessage("Authentication failed");
}
return (
<GoogleLogin
className="google-btn"
redirectUri={
isProduction
? process.env.REACT_APP_CLIENT_URL_PROD
: process.env.REACT_APP_CLIENT_URL_DEV
}
onSuccess={handleSuccess}
onError={handleFailure}
theme="filled_blue"
width="312px"
/>
)
}
export default Login;
并且,在服务器端,我有这个:
auth.controller.js
const { OAuth2Client } = require("google-auth-library");
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
const google = async (req, res) => {
try {
const { token } = req.body;
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.GOOGLE_CLIENT_ID,
});
const { given_name, family_name, email, sub } = ticket.getPayload();
User.findOne({ email: email }, (err, user) => {
if (!user) {
const newUser = new User({
first_name: given_name,
last_name: family_name,
email: email,
googleId: sub,
});
newUser.save();
generateTokens(newUser, res);
res.status(201).json({
isAuthenticated: true,
user: given_name,
});
} else if (user && !user.googleId) {
user.googleId = sub;
user.save();
generateTokens(user, res);
res.status(200).json({
isAuthenticated: true,
user: user.first_name,
});
} else {
console.log(err);
}
});
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
它需要的只是对如何从新的
@react-oauth/google
组件传递数据以及 Node 后端如何处理数据进行一些调整。
首先,确保您的应用程序由
GoogleOAuthProvider
包装,就像之前在 index.js
中所做的那样。这里没有什么新鲜事:
import { GoogleOAuthProvider } from "@react-oauth/google";
ReactDOM.render(
<GoogleOAuthProvider clientId={process.env.REACT_APP_GOOGLE_CLIENT_ID}>
<App />
</GoogleOAuthProvider>,
document.getElementById("root")
);
请注意,
clientId
已被通过。
当您单击 Google 登录按钮时,如果没有出现任何问题,它将将从 Google 返回的凭据响应传递给
handleSuccess
函数:
function Login() {
// HANDLE GOOGLE AUTHENTICATION
function handleSuccess(credentials) {
axios
.post("/api/auth/google", credentials)
.then((res) => {
// do something...
})
.catch((err) => {
console.log("Authentication failed: " + err);
});
}
return (
<GoogleLogin
onSuccess={(credentialResponse) => {
handleSuccess(credentialResponse);
}}
onError={handleFailure}
theme="filled_blue"
width="312px"
/>
)
}
export default Login;
这就是在
POST
请求中实际传递到后端的内容:
凭证
{
"clientId": "<YOUR_CLIENT_ID>",
"credential": "<ID_TOKEN>",
"select_by": "btn"
}
现在,这就是您在处理来自前端的
POST
请求时希望在后端实现的目标。一旦您能够正确验证凭据,您就可以对其进行解码以获取用户信息并根据需要操作数据:
const { OAuth2Client } = require("google-auth-library");
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
const google = async (req, res) => {
const { credential, clientId } = req.body;
try {
const ticket = await client.verifyIdToken({
idToken: credential,
audience: clientId,
});
const { given_name, family_name, email, sub } = ticket.getPayload();
// do something...
} catch (error) {
res.status(500).json({ success: false, message: error.message });
}
};
这是解码后的凭据(更多信息此处):
{
"iss": "https://accounts.google.com", // The JWT's issuer
"nbf": 161803398874,
"aud": "314159265-pi.apps.googleusercontent.com", // Your server's client ID
"sub": "3141592653589793238", // The unique ID of the user's Google Account
"hd": "gmail.com", // If present, the host domain of the user's GSuite email address
"email": "[email protected]", // The user's email address
"email_verified": true, // true, if Google has verified the email address
"azp": "314159265-pi.apps.googleusercontent.com",
"name": "Elisa Beckett",
// If present, a URL to user's profile picture
"picture": "https://lh3.googleusercontent.com/a-/e2718281828459045235360uler",
"given_name": "Elisa",
"family_name": "Beckett",
"iat": 1596474000, // Unix timestamp of the assertion's creation time
"exp": 1596477600, // Unix timestamp of the assertion's expiration time
"jti": "abc161803398874def"
}