'How to validate Hangouts Chat webhook token in C#

I am working on a pretty simple chat bot with an ASP.NET webhook to process responses. I am not having any issues sending or receiving messages, but I am a little bit stuck on validating the Bearer token in the Authorization header in order to validate that the incoming request has come from Google.

I have included the Google.Apis.Auth API version 1.55. It has the functionality that should do this validation. Of course the documentation does not give a .NET example, but from what I can gather, it should look like this:

try
{
    string token = "token here";
    SignedTokenVerificationOptions stvo = new SignedTokenVerificationOptions()
    { 
        TrustedAudiences = { "my project id" },
        TrustedIssuers = { "[email protected]" },
        CertificatesUrl = "https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]"
    };
    JsonWebSignature.Payload r = await JsonWebSignature.VerifySignedTokenAsync(token, stvo);
    return true;
}
catch (InvalidJwtException)
{
    return false;
}

My issue is that I am getting an exception from deep inside the API where it looks like it is processing the certificates from Google. I don't think I have much influence over this bit!

System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector)
   at Google.Apis.Auth.SignedTokenVerification.CertificateCacheBase.<GetCertificatesAsync>d__5.MoveNext() in C:\Apiary\2021-09-08.15-52-39\Src\Support\Google.Apis.Auth\SignedTokenVerification.cs:line 246
   at Google.Apis.Auth.SignedTokenVerification.<GetCertificatesAsync>d__6.MoveNext() in C:\Apiary\2021-09-08.15-52-39\Src\Support\Google.Apis.Auth\SignedTokenVerification.cs:line 203
   at Google.Apis.Auth.SignedTokenVerification.<VerifyRS256TokenAsync>d__4`2.MoveNext() in C:\Apiary\2021-09-08.15-52-39\Src\Support\Google.Apis.Auth\SignedTokenVerification.cs:line 110
   at Google.Apis.Auth.SignedTokenVerification.<VerifySignedTokenAsync>d__3`2.MoveNext() in C:\Apiary\2021-09-08.15-52-39\Src\Support\Google.Apis.Auth\SignedTokenVerification.cs:line 102
   at Google.Apis.Auth.JsonWebSignature.<VerifySignedTokenAsync>d__1`1.MoveNext() in C:\Apiary\2021-09-08.15-52-39\Src\Support\Google.Apis.Auth\JsonWebSignature.cs:line 61
   at GroupHandler.<>c__DisplayClass0_0.<<ProcessRequest>b__0>d.MoveNext() in D:\IIS\Sites\Test\Google\BotVerify.ashx:line 31

Is this the right approach? Or am I missing something totally obvious? :) Joel



Solution 1:[1]

It should be the standard service account authorization.

You need Google.Apis.Auth and Google.Apis.HangoutsChat.v1 packages.

public class HangoutsAuth
    {
        public static string[] scopes =  { "https://www.googleapis.com/auth/chat.bot" };

            private static GoogleCredential GetCredential(string pathToServiceAccountKeyFile, string[] scopes)
            {
                // Load the Service account credentials and define the scope of its access.
                return GoogleCredential.FromFile(pathToServiceAccountKeyFile)
                    .CreateScoped(scopes);
            }    
          
        }

Just call it with

var service = HangoutsAuth.GetService(PathToKeyFile,  { "https://www.googleapis.com/auth/chat.bot" });

var response = await service.Spaces.Messages.Create(body, "spaces/AAAA2CiqVDM").ExecuteAsync();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1