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
相关产品推荐
相关产品推荐

