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

Google Sign-In登录失败(error_description:Invalid Value)求助

解决Google Sign-In ID Token过期后无法手动刷新的问题

嘿,我看你遇到了一个挺棘手的Google登录问题:ID Token过期1小时后,当日登出就没法再登录,改到次日就正常。这大概率是因为你的应用没正确处理Token刷新流程,而且可能本地缓存了失效的Token信息,导致请求被Google验证服务拒绝。下面我给你详细讲讲怎么手动实现ID Token的刷新:

先搞懂问题根源

你的场景里,改系统时间到次日能登录,其实是绕开了应用本地的Token过期判断,重新触发了完整的授权流程,拿到了新的有效Token。但正常来说,我们应该用Refresh Token来主动刷新ID Token,而不是靠改时间这种临时办法。

手动刷新ID Token的具体步骤

1. 授权时务必请求offline_access权限

Google Sign-In默认不会返回Refresh Token,所以你得在初始化登录请求时,显式添加离线访问权限,这样才能拿到后续刷新用的Refresh Token:

GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(getString(R.string.default_web_client_id))
        .requestEmail()
        .requestScopes(new Scope("https://www.googleapis.com/auth/offline_access")) // 这步是关键!
        .build();

2. 安全存储Refresh Token

首次登录成功后,从GoogleSignInAccount里把Refresh Token取出来,存到本地(比如加密的SharedPreferences,别明文存):

GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(context);
if (account != null) {
    String refreshToken = account.getRefreshToken();
    // 把refreshToken存到本地安全的地方,比如用EncryptedSharedPreferences
}

3. 用Refresh Token请求新的ID Token

当ID Token过期时,给Google的Token端点发个POST请求,就能拿到新的ID Token了。这里用OkHttp举个例子:

OkHttpClient client = new OkHttpClient();

RequestBody formBody = new FormBody.Builder()
        .add("grant_type", "refresh_token")
        .add("client_id", getString(R.string.default_web_client_id)) // 注意:用Web客户端ID,不是Android客户端ID
        .add("refresh_token", yourStoredRefreshToken) // 从本地取之前存的Refresh Token
        .build();

Request request = new Request.Builder()
        .url("https://www.googleapis.com/oauth2/v4/token")
        .post(formBody)
        .build();

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        // 请求失败的处理,比如网络问题
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
            String responseJson = response.body().string();
            JSONObject json = new JSONObject(responseJson);
            String newIdToken = json.getString("id_token");
            // 更新本地存储的ID Token,后续接口就用这个新的
            
            // 额外提醒:如果返回了新的refresh_token,一定要更新本地存储,Google偶尔会刷新这个Token
            if (json.has("refresh_token")) {
                String newRefreshToken = json.getString("refresh_token");
                // 更新本地的refresh_token
            }
        } else {
            // 处理错误,比如Refresh Token失效了,这时候就得引导用户重新登录
        }
    }
});

4. 提前判断Token是否过期

每次用ID Token之前,先检查它的有效期,别等过期了才去刷新:

// 解析ID Token的payload,获取过期时间(单位毫秒)
private long getIdTokenExpiry(String idToken) {
    String[] tokenParts = idToken.split("\\.");
    if (tokenParts.length < 2) return 0;
    try {
        String payload = new String(Base64.decode(tokenParts[1], Base64.URL_SAFE));
        JSONObject payloadJson = new JSONObject(payload);
        return payloadJson.getLong("exp") * 1000; // 把秒转成毫秒
    } catch (Exception e) {
        e.printStackTrace();
        return 0;
    }
}

// 使用前检查
if (System.currentTimeMillis() >= getIdTokenExpiry(currentIdToken)) {
    // 触发刷新流程,调用上面步骤3的代码
}

几个重要的注意点

  • 一定要用Web客户端ID,Android客户端ID没有client_secret,没法用Refresh Token请求新的ID Token。你可以在Google Cloud Console里创建一个Web类型的客户端。
  • 如果Refresh Token失效(比如用户在Google账号里 revoke了你的应用权限),那只能引导用户重新登录,获取新的Refresh Token和ID Token。
  • 存储Token的时候要加密,别明文存在SharedPreferences里,避免安全问题。

内容的提问来源于stack exchange,提问作者Jinmiao Ouyang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:45:01