using System.Text.Json; using Application.Abstractions.Authentication; using Microsoft.Extensions.Logging; namespace Infrastructure.Authentication; internal sealed class GoogleTokenValidator( HttpClient httpClient, ILogger logger ) : IGoogleTokenValidator { public async Task ValidateAsync(string credential, string clientId, CancellationToken ct) { try { var response = await httpClient.GetAsync( $"https://oauth2.googleapis.com/tokeninfo?id_token={credential}", ct ); if (!response.IsSuccessStatusCode) { logger.LogWarning("Google token validation failed: {StatusCode}", response.StatusCode); return null; } var json = await response.Content.ReadAsStringAsync(ct); var payload = JsonSerializer.Deserialize(json); // aud (audience) 검증 var aud = payload.GetProperty("aud").GetString(); if (aud != clientId) { logger.LogWarning("Google token aud mismatch: expected {Expected}, got {Actual}", clientId, aud); return null; } // 이메일 인증 여부 확인 var emailVerified = payload.GetProperty("email_verified").GetString(); if (emailVerified != "true") { logger.LogWarning("Google token email not verified"); return null; } var email = payload.GetProperty("email").GetString()!; var sub = payload.GetProperty("sub").GetString()!; var name = payload.TryGetProperty("name", out var nameProp) ? nameProp.GetString() : null; var picture = payload.TryGetProperty("picture", out var picProp) ? picProp.GetString() : null; return new GoogleUserInfo(sub, email, name, picture); } catch (Exception ex) { logger.LogError(ex, "Google token validation error"); return null; } } }