You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 04:05:21