0
votes

I have a Google Pub/Sub push subscription that sends a JWT token to the endpoint. The endpoint needs to validate this token. From Google documentation, I need to check the issuer, the audience and the signature. This works fine, except for whatever I add to IssuerSigningKey(s), the token is valid. I expected this to break whenever I e.g. remove a part of the key.

I tried all kinds of different values for IssuerSigningKey and IssuerSigningKeys. No matter what, I get a valid response. Changing e.g. the domain or audience parameters does return a 401 Unauthorized.

public void ConfigureServices(IServiceCollection services)
{
    string domain = "https://accounts.google.com";
    string audience = "theaudience";
    // Just to debug/test
    string signingKey = "---- - BEGIN PRIVATE KEY-----\nMIIfujHGitJ\n---- - END PRIVATE KEY-----\n";
    var certificates = 
    this.FetchGoogleCertificates().GetAwaiter().GetResult();

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.Authority = domain;
            options.Audience = audience;

            options.TokenValidationParameters = new TokenValidationParameters
            {
                ClockSkew = TimeSpan.FromHours(48), // This is just for debugging. Maybe we do need a little clock skew if the clock in Google is not aligned with the VD system
                ValidateAudience = true, // Validate the audience, this will change in production to the endpoint URL
                ValidateIssuer = true, // Validate the issuer (Google). If this is wrong, we get a 500 error instead of 40x
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)),
                /* Stuff I also tried:
                IssuerSigningKey = new RsaSecurityKey(new RSACryptoServiceProvider(2048))
                IssuerSigningKeys = certificates.Values.Select(x => new X509SecurityKey(x)),
                IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) =>
                {
                    return certificates
                    .Where(x => x.Key.ToUpper() == kid.ToUpper())
                    .Select(x => new X509SecurityKey(x.Value));
                }
                */
            };
        });

        services.AddAuthorization();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

What is happening here?

1

1 Answers

2
votes

From the ASP.NET blog post on JWT Validation:

First, the Authority property should not be set on the JwtBearerOptions. If it’s set, the middleware assumes that it can go to that URI to get token validation information.

You are leaving the Authority property set, in your code example, which causes the verification library to make a network request to accounts.google.com to get token validation information. If you leave the Authority property unset, it will be forced to use your TokenValidationParameters.