.NET Core WebAPI验证Google JWT Token持续返回401问题排查
Hey there! Let's troubleshoot that stubborn 401 error you're facing when validating Google JWT Bearer Tokens in your .NET Core WebAPI. Since you've confirmed the token is valid via jwt.io, the issue is almost certainly in how your code is set up to validate it. Let's break down the key areas to check:
1. Startup.cs Authentication Configuration Gaps
First, double-check that your authentication setup is correctly wired to Google's JWT endpoints. Common missteps here include:
- Forgetting to set the correct Authority and Audience (your Google OAuth Client ID)
- Misordering middleware (authentication must run before authorization)
- Failing to register the custom validator properly
Here's a corrected example of what this should look like:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); // Pull your Google Client ID from config (never hardcode!) var googleClientId = Configuration["Authentication:Google:ClientId"]; services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://accounts.google.com"; options.Audience = googleClientId; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "https://accounts.google.com", ValidateAudience = true, ValidAudience = googleClientId, ValidateLifetime = true, // Hook up your custom validator if you're using one TokenValidator = new GoogleTokenValidator() }; }); services.AddAuthorization(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... other middleware like exception handling, static files ... // Critical order: Authentication MUST come before Authorization app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
2. Custom GoogleTokenValidator Issues
If you're using a custom validator, make sure it's not accidentally rejecting valid tokens. Common pitfalls:
- Not properly validating Google's required claims (like
issandaud) - Swallowing exceptions that would tell you why validation failed
- Not implementing the
ISecurityTokenValidatorinterface correctly
Here's a robust implementation of the validator that adds Google-specific checks:
public class GoogleTokenValidator : ISecurityTokenValidator { private readonly JwtSecurityTokenHandler _tokenHandler = new(); public bool CanValidateToken => true; public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes; public bool CanReadToken(string securityToken) => _tokenHandler.CanReadToken(securityToken); public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) { // Let the base handler do initial validation var principal = _tokenHandler.ValidateToken(securityToken, validationParameters, out validatedToken); // Add Google-specific checks var issuerClaim = principal.FindFirst(ClaimTypes.Issuer); if (issuerClaim == null || !issuerClaim.Value.Equals("https://accounts.google.com", StringComparison.OrdinalIgnoreCase)) { throw new SecurityTokenInvalidIssuerException("Token is not issued by Google"); } var audienceClaim = principal.FindFirst(JwtRegisteredClaimNames.Aud); if (audienceClaim == null || !audienceClaim.Value.Equals(validationParameters.ValidAudience, StringComparison.OrdinalIgnoreCase)) { throw new SecurityTokenInvalidAudienceException("Token audience does not match API client ID"); } return principal; } }
3. Xamarin Forms HttpClient Call Mistakes
Don't overlook the client-side! Even a tiny mistake here can cause a 401:
- Using the wrong token: Make sure you're passing a Google ID Token, not an Access Token. Access Tokens are for calling Google APIs, ID Tokens are for authentication.
- Malformed Authorization header: Ensure you're using the format
Bearer {token}(note the space between "Bearer" and the token). - Missing scopes: When requesting the token from Google, make sure you include the
openidscope (required for ID Tokens).
Here's a corrected client-side call example:
private async Task CallProtectedApi() { var httpClient = new HttpClient(); // Get the ID Token from your Google Auth flow (not Access Token!) var googleIdToken = await _googleAuthService.GetIdTokenAsync(); // Set the Authorization header correctly httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", googleIdToken); try { var response = await httpClient.GetAsync("https://your-api-domain/api/protected-endpoint"); response.EnsureSuccessStatusCode(); // Handle successful response } catch (HttpRequestException ex) { // Log or handle the error } }
Bonus Troubleshooting Tips
- Enable debug logging: Add this to your
appsettings.jsonto see detailed validation errors:{ "Logging": { "LogLevel": { "Microsoft.AspNetCore.Authentication.JwtBearer": "Debug" } } } - Verify token claims: Double-check the
aud(audience) claim in your token via jwt.io matches exactly the Client ID you're using in your WebAPI. - Check Google Cloud Console: Ensure your WebAPI's Client ID is correctly configured, and your Xamarin app's OAuth Client ID is allowed to request tokens for this audience.
内容的提问来源于stack exchange,提问作者Napoleon Ike Jones

