Gmail API中Refresh Token获取、使用及AccessToken刷新问题(JS)
问题场景
使用JavaScript开发,通过Gmail API完成登录授权后,控制台可获取access token、过期时间等信息,但未返回refresh token。现咨询以下问题:
- 如何获取refresh token?
- 如何通过refresh token获取新的access token?
- 当前获取access token的方式是否为最优方案?
- 若无refresh token,该如何刷新access token?
实现代码
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>FIFV Dashboard</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css"> <link rel="stylesheet" href="home.css"> <script src="https://apis.google.com/js/api.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css"> </head> <body> <div class="wrapper fadeInDown"> <div id="formContent"> <!-- Tabs Titles --> <!-- Icon --> <div class="fadeIn first"> <img src="Images/company Logo.png" id="icon" alt="User Icon" /> </div> <!-- Login Form --> <form> <h3 class="heading"> Welcome to FIFV authentication </h3> <p class="shortPara">Please authenticate yourself to get access to the dashboard.</p> <!-- <input type="submit" class="fadeIn fourth" value="Log In" href="Home.html"> --> <br> <button onclick="authenticate().then(loadClient)" type="button" class="btn"><a>Authorise</a></button> <p id="errorMsg"></p> </form> </div> </div> <script> /** * Sample JavaScript code for gmail.users.messages.list * See instructions for running APIs Explorer code samples locally: * https://developers.google.com/explorer-help/code-samples#javascript */ // Authentication code function authenticate() { return gapi.auth2.getAuthInstance() .signIn({ scope: "https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/gmail.compose https://www.googleapis.com/auth/gmail.readonly" }) .then(function (response) { console.log("Sign-in successful", response); console.log("Access Token", response.Bc.access_token) localStorage.setItem("accessToken", response.Bc.access_token) localStorage.setItem("expire", response.Bc.expires_in) }, function (err) { console.error("Error signing in", err); }); } function loadClient() { gapi.client.setApiKey("my-api-key"); return gapi.client.load("https://gmail.googleapis.com/$discovery/rest?version=v1") .then(function () { console.log("GAPI client loaded for API"); }, function (err) { console.error("Error loading GAPI client for API", err); document.getElementById('errorMsg').innerHTML = err; }); } gapi.load("client:auth2", function () { gapi.auth2.init({ client_id: "my-client-id", plugin_name: "hello" }); }); function logout() { fetch("https://oauth2.googleapis.com/revoke?token=" + token, { method: 'POST', headers: { "Content-type": "application/x-www-form-urlencoded" } }) .then((data) => { location.href = "http://localhost:5500/index.html" }) } </script> <script src="home.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"></script> <script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> </body> </html>
1. 如何获取refresh token?
要拿到refresh token,需满足两个核心条件,同时修改代码逻辑:
- 授权请求添加
prompt: 'consent'参数,强制用户确认授权,触发refresh token发放; - 确保Google Cloud控制台的OAuth客户端配置正确:如果是Web应用,需开启离线访问权限;如果是桌面/移动应用,默认支持离线访问。
修改后的authenticate函数(避免依赖私有属性response.Bc,改用官方API获取完整授权响应):
function authenticate() { return gapi.auth2.getAuthInstance() .signIn({ scope: "https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/gmail.compose https://www.googleapis.com/auth/gmail.readonly", prompt: 'consent' // 强制弹出授权确认框,确保返回refresh token }) .then(function () { const user = gapi.auth2.getAuthInstance().currentUser.get(); const authResponse = user.getAuthResponse(true); // true参数会返回refresh token console.log("授权成功", authResponse); console.log("Access Token", authResponse.access_token); console.log("Refresh Token", authResponse.refresh_token); localStorage.setItem("accessToken", authResponse.access_token); localStorage.setItem("expire", authResponse.expires_in); localStorage.setItem("refreshToken", authResponse.refresh_token); }, function (err) { console.error("授权失败", err); }); }
2. 如何通过refresh token获取新的access token?
直接调用Google的token刷新接口,用POST请求提交参数即可:
function refreshAccessToken(refreshToken) { const clientId = "你的client-id"; return fetch("https://oauth2.googleapis.com/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ client_id: clientId, refresh_token: refreshToken, grant_type: "refresh_token" }) }) .then(response => response.json()) .then(data => { if (data.access_token) { localStorage.setItem("accessToken", data.access_token); localStorage.setItem("expire", data.expires_in); return data.access_token; } else { throw new Error("刷新token失败:" + data.error); } }); }
调用示例:refreshAccessToken(localStorage.getItem("refreshToken"))
3. 当前获取access token的方式是否为最优方案?
当前方式不是最优,存在两个关键问题:
- 依赖私有属性
response.Bc:这是Google未公开的内部结构,随时可能被修改,导致代码失效; - 无持久化刷新机制:没处理refresh token的存储和自动刷新逻辑,token过期后需用户重新授权。
优化方向:
- 改用官方
getAuthResponse(true)方法获取token信息; - 加入refresh token存储和自动刷新逻辑;
- 统一错误处理,比如token过期时自动触发刷新。
4. 若无refresh token,该如何刷新access token?
没有refresh token时,只有两种可行方式:
- 静默刷新:调用官方API尝试在用户无感知的情况下刷新token(前提是用户已授权且会话未过期):
function silentRefresh() { const user = gapi.auth2.getAuthInstance().currentUser.get(); return user.reloadAuthResponse() .then(authResponse => { localStorage.setItem("accessToken", authResponse.access_token); localStorage.setItem("expire", authResponse.expires_in); return authResponse.access_token; }) .catch(err => { // 静默刷新失败,引导用户重新授权 console.error("静默刷新失败,请重新授权", err); return authenticate(); }); }
- 重新授权:如果静默刷新失败,只能引导用户点击授权按钮,重新发起授权流程获取新的access token。
内容的提问来源于stack exchange,提问作者Muzammil Zafar
相关产品推荐
相关产品推荐

