using HuntingAppApi.Models; using Newtonsoft.Json.Linq; namespace HuntingAppApi.Services; public sealed class ExternalTokenValidator(HttpClient httpClient) { public async Task ValidateAsync(string provider, string idToken, CancellationToken ct) { provider = provider.ToLowerInvariant(); // Production note: This is intentionally simple. For stricter production validation, // validate issuer, audience/client_id and signature using OIDC metadata/JWKS. var url = provider switch { "google" => $"https://oauth2.googleapis.com/tokeninfo?id_token={Uri.EscapeDataString(idToken)}", "microsoft" => $"https://login.microsoftonline.com/common/oauth2/v2.0/tokeninfo?id_token={Uri.EscapeDataString(idToken)}", _ => throw new ArgumentException("Unsupported provider") }; var json = await httpClient.GetStringAsync(url, ct); var obj = JObject.Parse(json); var sub = obj.Value("sub") ?? throw new InvalidOperationException("Token has no sub claim"); var name = obj.Value("name") ?? obj.Value("preferred_username") ?? obj.Value("email") ?? "User"; return new ExternalUserInfo { Provider = provider, Subject = sub, Email = obj.Value("email") ?? obj.Value("preferred_username"), EmailVerified = obj.Value("email_verified") == "true" || obj.Value("email_verified") == true, DisplayName = name, AvatarUrl = obj.Value("picture") }; } }