Ign*_*rre 2 google-calendar-api google-api firebase google-drive-api google-oauth
我有一个 Firebase 应用程序,可以对用户进行身份验证并返回一个访问令牌,然后我可以使用该令牌来访问 Google Calendar 和 Sheets API。我还保存了刷新令牌。经过身份验证的令牌的示例代码:
firebase
.signInWithGoogle()
.then(async (socialAuthUser) => {
let accessToken = socialAuthUser.credential.accessToken // token to access Google Sheets API
let refreshToken = socialAuthUser.user.refreshToken
this.setState({accessToken, refreshToken})
})
Run Code Online (Sandbox Code Playgroud)
1 小时后,accessToken 将过期。Firebase 身份验证在登录后在用户对象上提供刷新令牌
我使用该刷新令牌重新进行身份验证并通过发布到以下位置获取新的 access_token:
https://securetoken.googleapis.com/v1/token?key=firebaseAppAPIKey
新的访问令牌不再适用于 Google API,并且不再具有授权范围。我也尝试将其发送到
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token="refreshToken"
它给我错误“无效令牌”。当我使用 firebase 的原始令牌时,它工作得很好。
还有其他人遇到类似的问题吗?我还没有找到一种方法来使用正确的访问范围刷新原始访问令牌,而不需要用户注销并再次登录。
谢谢!
我经过多次尝试终于能够解决这个问题。
在 Medium 上发布了详细的解决方案:https://inaguirre.medium.com/reusing-access-tokens-in-firebase-with-react-and-node-3fde1d48cbd3
在客户端上,我将 React 与 Firebase 库结合使用,在服务器上,我将 Node.js 与链接到同一 Firebase 项目的 google-apis 包和 firebase-admin skd 包一起使用。
脚步:
大多数事情发生在身份验证后处理 Google 重定向时。以下是在后端处理身份验证和令牌的示例:
const router = require("express").Router();
const { google } = require("googleapis");
const { initializeApp, cert } = require("firebase-admin/app");
const { getAuth } = require("firebase-admin/auth");
const { getDatabase } = require("firebase-admin/database");
const serviceAccount = require("../google-credentials.json");
const fetch = require("node-fetch");
initializeApp({
credential: cert(serviceAccount),
databaseURL: "YOUR_DB_URL",
});
const db = getDatabase();
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
"http://localhost:8080/handleGoogleRedirect"
);
//post to google auth api to generate auth link
router.post("/authLink", (req, res) => {
try {
// generate a url that asks permissions for Blogger and Google Calendar scopes
const scopes = [
"profile",
"email",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/calendar",
];
const url = oauth2Client.generateAuthUrl({
access_type: "offline",
scope: scopes,
// force access
prompt: "consent",
});
res.json({ authLink: url });
} catch (error) {
res.json({ error: error.message });
}
});
router.get("/handleGoogleRedirect", async (req, res) => {
console.log("google.js 39 | handling redirect", req.query.code);
// handle user login
try {
const { tokens } = await oauth2Client.getToken(req.query.code);
oauth2Client.setCredentials(tokens);
// get google user profile info
const oauth2 = google.oauth2({
version: "v2",
auth: oauth2Client,
});
const googleUserInfo = await oauth2.userinfo.get();
console.log("google.js 72 | credentials", tokens);
const userRecord = await checkForUserRecord(googleUserInfo.data.email);
if (userRecord === "auth/user-not-found") {
const userRecord = await createNewUser(
googleUserInfo.data,
tokens.refresh_token
);
const customToken = await getAuth().createCustomToken(userRecord.uid);
res.redirect(
`http://localhost:3000/home?id_token=${customToken}&accessToken=${tokens.access_token}&userId=${userRecord.uid}`
);
} else {
const customToken = await getAuth().createCustomToken(userRecord.uid);
await addRefreshTokenToUserInDatabase(userRecord, tokens);
res.redirect(
`http://localhost:3000/home?id_token=${customToken}&accessToken=${tokens.access_token}&userId=${userRecord.uid}`
);
}
} catch (error) {
res.json({ error: error.message });
}
});
const checkForUserRecord = async (email) => {
try {
const userRecord = await getAuth().getUserByEmail(email);
console.log("google.js 35 | userRecord", userRecord.displayName);
return userRecord;
} catch (error) {
return error.code;
}
};
const createNewUser = async (googleUserInfo, refreshToken) => {
console.log(
"google.js 65 | creating new user",
googleUserInfo.email,
refreshToken
);
try {
const userRecord = await getAuth().createUser({
email: googleUserInfo.email,
displayName: googleUserInfo.name,
providerToLink: "google.com",
});
console.log("google.js 72 | user record created", userRecord.uid);
await db.ref(`users/${userRecord.uid}`).set({
email: googleUserInfo.email,
displayName: googleUserInfo.name,
provider: "google",
refresh_token: refreshToken,
});
return userRecord;
} catch (error) {
return error.code;
}
};
const addRefreshTokenToUserInDatabase = async (userRecord, tokens) => {
console.log(
"google.js 144 | adding refresh token to user in database",
userRecord.uid,
tokens
);
try {
const addRefreshTokenToUser = await db
.ref(`users/${userRecord.uid}`)
.update({
refresh_token: tokens.refresh_token,
});
console.log("google.js 55 | addRefreshTokenToUser", tokens);
return addRefreshTokenToUser;
} catch (error) {
console.log("google.js 158 | error", error);
return error.code;
}
};
router.post("/getNewAccessToken", async (req, res) => {
console.log("google.js 153 | refreshtoken", req.body.refresh_token);
// get new access token
try {
const request = await fetch("https://www.googleapis.com/oauth2/v4/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
refresh_token: req.body.refresh_token,
grant_type: "refresh_token",
}),
});
const data = await request.json();
console.log("google.js 160 | data", data);
res.json({
token: data.access_token,
});
} catch (error) {
console.log("google.js 155 | error", error);
res.json({ error: error.message });
}
});
module.exports = router;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1323 次 |
最近记录: |