Reputation: 5886
I'm currently creating a web application on which the user can login via his Google account. This works client side but I would also like to secure REST API calls. To do so, I send the "Google id token" with each request via the "Authorization" header. Now, I would like to verify in C# that the token passed is valid. I found that there is a .NET library to do so but I didn't find anywhere any clear documentation on how to simply validate the token.
Does anyone have some pointer for this?
Upvotes: 13
Views: 12207
Reputation: 821
Yet another simplified answer (for .net 6):
Add this nuget package to your project: https://www.nuget.org/packages/Google.Apis.Auth
Add using statement:
using Google.Apis.Auth;
Create this method in your controller:
[AllowAnonymous]
[HttpPost("verify")]
public async Task<ActionResult> Verify(){
string token = Request.Headers["Authorization"].ToString().Remove(0,7); //remove Bearer
var payload = await VerifyGoogleTokenId(token);
if (payload==null)
{
return BadRequest("Invalid token");
}
return Ok(payload); }
Create the VerifyGoogleTokenId function:
public async Task<GoogleJsonWebSignature.Payload> VerifyGoogleTokenId(string token){
try
{
// uncomment these lines if you want to add settings:
// var validationSettings = new GoogleJsonWebSignature.ValidationSettings
// {
// Audience = new string[] { "yourServerClientIdFromGoogleConsole.apps.googleusercontent.com" }
// };
// Add your settings and then get the payload
// GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(token, validationSettings);
// Or Get the payload without settings.
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(token);
return payload;
}
catch (System.Exception)
{
Console.WriteLine("invalid google token");
}
return null;
}
Test the implementation by sending a post request to yourapi.com/verify. Dont forget the authorization header.
Upvotes: 7
Reputation: 506
For future reference the following verifications are checked internally by the Google.Apis.Auth library and no extra validations are required (both passing settings or checking the payload):
The following however require input by the developer in order to be validated. They can be passed with GoogleJsonWebSignature.ValidationSettings:
Source: Google.Apis.Auth.Tests/GoogleJsonWebSignatureTests.cs
According to the docs, the token must be validated by verifying the signature with Google's public key. Also check the aus, iss and exp claims, and the hd claim if applies. Therefore only the aus (and hd) have to be tested explicitly by the developer.
try
{
//...
var validationSettings = new GoogleJsonWebSignature.ValidationSettings
{
Audience = new string[] { "[google-signin-client_id].apps.googleusercontent.com" }
};
var payload = await GoogleJsonWebSignature.ValidateAsync(idToken, validationSettings);
//...
}
catch (InvalidJwtException ex)
{
//...
}
Upvotes: 5
Reputation: 967
According to the "Verify the integrity of the ID token" documentation multiple things must be checked, for the id token to be valid, not just the signature.
One of those is whether "the ID token is equal to [...] your app's client IDs". Since we never give the client ID to GoogleJsonWebSignature.ValidateAsync(token) it seems we need to check it manually. I'm assuming it's really just checking the signature and we need to do all of the other checks manually.
My first shot at this:
bool valid = true;
try
{
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(Token);
if (!payload.Audience.Equals("YOUR_CLIENT_ID_1234567890.apps.googleusercontent.com"))
valid = false;
if (!payload.Issuer.Equals("accounts.google.com") && !payload.Issuer.Equals("https://accounts.google.com"))
valid = false;
if (payload.ExpirationTimeSeconds == null)
valid = false;
else
{
DateTime now = DateTime.Now.ToUniversalTime();
DateTime expiration = DateTimeOffset.FromUnixTimeSeconds((long)payload.ExpirationTimeSeconds).DateTime;
if (now > expiration)
{
valid = false;
}
}
}
catch (InvalidJwtException e)
{
valid = false;
}
Upvotes: 4
Reputation: 2647
My answer is the same as the answer above with a little bit more details.
using Google.Apis.Auth;
using Google.Apis.Auth.OAuth2;
GoogleJsonWebSignature.Payload payload = await GoogleJsonWebSignature.ValidateAsync(Token);
...
The payload object contains all the information that you need.
Upvotes: 22