diff --git a/library/src/social/google.rs b/library/src/social/google.rs index b03bacf..7758490 100644 --- a/library/src/social/google.rs +++ b/library/src/social/google.rs @@ -45,6 +45,10 @@ pub struct GoogleJwtProfile { pub given_name: String, pub family_name: String, pub locale: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub google_play_games_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub google_play_games_profile: Option, } impl GoogleJwtProfile { @@ -159,4 +163,76 @@ impl GoogleSocial { Ok(google_jwt_profile) } + + /// 验证 Google Play Games 的身份信息 + /// 参考: https://developers.google.com/games/services/android/signin + pub async fn verify_play_games_token(&self, play_games_token: &str) -> SocialResult { + // Google Play Games 的验证端点 + const GOOGLE_PLAY_GAMES_TOKEN_INFO_URL: &str = + "https://oauth2.googleapis.com/tokeninfo"; + + let client = Client::new(); + let response = client + .get(GOOGLE_PLAY_GAMES_TOKEN_INFO_URL) + .query(&[("access_token", play_games_token)]) + .send() + .await?; + + if !response.status().is_success() { + return Err(Box::new(ResErr::social("验证 Google Play Games Token 失败"))); + } + + let token_info: Value = response.json().await?; + + // 验证 token 信息 + if let Some(error) = token_info.get("error") { + return Err(Box::new(ResErr::social(format!( + "Google Play Games Token 无效: {}", + error + )))); + } + + // 验证 audience 是否匹配你的应用 + if let Some(aud) = token_info.get("aud") { + // TODO: 验证 aud 是否匹配你的 Google Play Games 应用 ID + tracing::debug!("Google Play Games aud: {}", aud); + } + + // 构建用户资料 + let mut profile = GoogleJwtProfile::default(); + if let Some(sub) = token_info.get("sub").and_then(|v| v.as_str()) { + profile.sub = sub.to_string(); + } + if let Some(email) = token_info.get("email").and_then(|v| v.as_str()) { + profile.email = email.to_string(); + } + + // 获取 Google Play Games 特定的信息 + let play_games_info = self.fetch_play_games_profile(play_games_token).await?; + profile.google_play_games_id = play_games_info.get("playerId") + .and_then(|v| v.as_str()) + .map(String::from); + + Ok(profile) + } + + /// 获取 Google Play Games 用户资料 + async fn fetch_play_games_profile(&self, access_token: &str) -> SocialResult { + const PLAY_GAMES_PROFILE_URL: &str = + "https://games.googleapis.com/games/v1/players/me"; + + let client = Client::new(); + let response = client + .get(PLAY_GAMES_PROFILE_URL) + .header("Authorization", format!("Bearer {}", access_token)) + .send() + .await?; + + if !response.status().is_success() { + return Err(Box::new(ResErr::social("获取 Google Play Games 用户资料失败"))); + } + + let profile: Value = response.json().await?; + Ok(profile) + } }