Hi I want to have users authenticate using Google and I want my API and also use their Google token to communicate on their behalf with google. Here is a diagram so far. The ???? is where I'm wondering what should I return the the client.
a) Should I return my own JWT and use it to authenticate all other client request? But then to communicate with google on their behalf I have to store their token which I dont want to
b) Should I return the google token to the client for them to authenticate their requests with it? Do I have a out-of-the-box middleware for authenticating their tokens again with google? Or should I write one myself?
c) Some other option?
Basically I need their google token so I can talk with google API but I dont want to store it on my end and also I dont want the client to need to send my JWT and their google token with each request.
EDIT This is my custom google token validator but this is just the validation of the google token when the client sends it with a request.
public class CustomGoogleTokenValidator : ISecurityTokenValidator
{
private readonly JwtSecurityTokenHandler tokenHandler;
private readonly ILogger logger;
public bool CanValidateToken => true;
public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;
public CustomGoogleTokenValidator(ILogger logger)
{
tokenHandler = new JwtSecurityTokenHandler();
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public bool CanReadToken(string securityToken)
{
return tokenHandler.CanReadToken(securityToken);
}
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
validatedToken = null;
var payload = GoogleJsonWebSignature.ValidateAsync(securityToken, new GoogleJsonWebSignature.ValidationSettings()).Result;
// TODO VALIDATE
//payload.Audience == "myclientid";
//payload.Issuer == "accounts.google.com" or "https://accounts.google.com"
//payload.ExpirationTimeSeconds > 0;
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, payload.Name),
new Claim(ClaimTypes.Name, payload.Name),
new Claim(JwtRegisteredClaimNames.FamilyName, payload.FamilyName),
new Claim(JwtRegisteredClaimNames.GivenName, payload.GivenName),
new Claim(JwtRegisteredClaimNames.Email, payload.Email),
new Claim(JwtRegisteredClaimNames.Sub, payload.Subject),
new Claim(JwtRegisteredClaimNames.Iss, payload.Issuer),
};
try
{
var principle = new ClaimsPrincipal();
principle.AddIdentity(new ClaimsIdentity(claims));
return principle;
}
catch (Exception e)
{
this.logger.Error(e, "Error while creating claims priciple.");
throw;
}
}
}
I still don't know if it's appropriate and enought to just send google token to them after I validate it on login. Like below or should I create a new jwt with claims or somethig else?
[AllowAnonymous]
[HttpPost("google")]
public async Task<IActionResult> Google([FromBody]GoogleLoginDto loginDto)
{
try
{
var payload = await GoogleJsonWebSignature.ValidateAsync(loginDto.TokenId, new GoogleJsonWebSignature.ValidationSettings());
// TODO Check if user exists if not create new one...
var user = this.GetUsers().FirstOrDefault(u => u.Email == payload.Email);
return Ok(new
{
token = loginDto.TokenId
});
}
catch (Exception ex)
{
BadRequest(ex.Message);
}
return BadRequest();
}