How To Validate Google Identity Service (GIS) Access Token On Server Side In C# / .NET?

2.8k views Asked by At

I'm migrating from the old Google Sign In library to the new Google Identity Services (GIS) library. This is mandatory, since the old one will no longer be in use from March 2023.

Previously, I did (simplified for clarity):

<script src="https://apis.google.com/js/api:client.js"></script>
gapi.load();
var auth2 = gapi.auth2.init();
auth2.attachClickHandler();
onGoogleSignIn(googleUser); // attachClickHandler's callback
var profile      = googleUser.getBasicProfile(); // profile info accessible
var authResponse = googleUser.getAuthResponse(); // auth response accessible
var accessToken  = authResponse.id_token; // get actual access token

Now, I'm trying (simplified for clarity):

<script src="https://accounts.google.com/gsi/client"></script>
var gisClient = google.accounts.oauth2.initTokenClient();
gisClient.requestAccessToken();
callback(); // initTokenClient's callback
var accessToken = response.access_token; // get access token in callback

With the old google sign in library, I validated the access token server side as such:

Payload payload = await GoogleJsonWebSignature.ValidateAsync(accessToken);

This also returned the user's email and name in the payload.

The access token I am getting back from GIS, is much shorter than the old one from GAPI.

An online token debugger tells me it's not a valid JWT token.

The ValidateAsync method throws an exception:

JWT must consist of Header, Payload, and Signature

No surprise, considering it's not a valid JWT token.

I also tried the following call:

Payload payload = await JsonWebSignature.VerifySignedTokenAsync(AccessToken, options);

Same result.

The official documentation doesn't say how to validate this token server side for C# / .NET.

I can't find help on this anywhere in the documentation.

What can I do to get server side access token validation (and retrieval of email + profile) working with Google Identity Services?

1

There are 1 answers

6
user160357 On BEST ANSWER

Explanation

The new Google Sign in returns "CredentialResponse" which contains a property called credential, which is the JSON Web Token (JWT) in base64 that you need.
This JWT can be sent to client or server for validation.
After validation you will receive user profile data.

Client

<div id="g_id_onload"
     data-client_id="YOUR_GOOGLE_CLIENT_ID"
     data-callback="handleCredentialResponse">
</div>
<script>

    function handleCredentialResponse(response) {

        //get JSON Web Token (JWT) out of the response object
        var jwt = response.credential;

        //send JWT to backend server for validation
        var result = ValidateAtServer(jwt);

        //do something with result
        KillUserInstantly(result);
    }
</script>

Server (.NET)

public static void ValidateAtServer(httpRequest)
{
  //get jwt string from request
  ...

  //validate it using Google.Apis.Auth (null if invalid)
  var validPayload = await GoogleJsonWebSignature.ValidateAsync(jwtToken);

  //get user data & use it
  var userId = validPayload.Subject; //The unique ID of the user's Google Account
  var email = validPayload.Email;

  //do something with data
  ...
}

Beginner Notes

  1. "unique user ID" in located under subject or sub
  2. "ID token" is the base64-encoded JSON Web Token (JWT) string.
  3. Validating the JWT also decrypts the data inside which includes user profile.

Example JWT/ID Token

eyJhbGciOiJSUzI1NiIsImtpZCI6IjE1NDllMGFlZjU3NGQxYzdiZGQxMzZjMjAyYjhkMjkwNTgwYjE2NWMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2NTk3NTk0MzEsImF1ZCI6IjEwODgwNjIxNjM1NzMtaXMwdWQ1aDRza2JscmR1Njp0cnVlLCJheuZXMiLCJpYXQiOjE2NTk3NTk3MzEsImV4cCI6MTY1OTc2MzMzMSwianRpIjoiMDhlNjRhM2I1YzdmNzcxYmRjNTg5M2YwNmMyZjM1ZWZlMzIyNjYyMCJ9.UT07_-_4o_1D5NmVAI0QtXLupVZtXys3Kg0c--Cv-xrMpUZXInfqj142eojvTEf6QBmBPY3k-Mtu7djJAenB8Ed8-dWtvFdGdv5FdSJCSyLN70ObzCsdo_IgjG5r3HTw1C9pIFKggOklJrVN-zL0_Kh3TZdxfMdyEbAUuhIRCreUVgZ74XEWhR6x4l0EY9o2331HcrzAaie_LN4C8NVHhkQ0DLg5dO2v8T1uKG-eTyv-uvjMuhkSVBJR3MnvkGepj7o0h_ELGO9x74P9nNjIKTyZboEr4_YO0BP5aLPwt67LJHactAJ8DJTzugXwaJBVhusK1KPYYGRhy7nTfbfTSg