Can't authenticate Google access token on my net.core backend

111 views Asked by At

I'm testing for the first time, how to use net.core API + Angular with Google Authentication, I already achieved to get an access token with my UI application, basically I developed a POC that uses OAuthService and once all the handshake is ok, you are able to call this method:

this.oAuthService.getAccessToken()

Which will result in a valid token (presumably)

So, now is the time to inject that in the backend and authenticate every single request that goes from the UI to the backend. For this, I created a REST API which has Swagger, I added the statements to be able to manually inject the bearer token (for testing purposes) and now i'm trying to test a very simple endpoint to confirm the authentication works.

This is the CURL sent by Swagger to the backend, on which we can see the authorization header is added properly:

curl -X 'GET' \
  'https://localhost:7103/AuthenticationMethod/GetAll' \
  -H 'accept: text/plain' \
  -H 'Authorization: ya29.a0AfB_byBis8NBGFASFeCEeMg4m9CJY0kusHndtCyMqHL8zBtpp8DsOaL26iuFhW2FpxrroNveQqoVCT5Ug2iPCM0uCTT5MYlBdDfGrjPsGyUR6L3mxwu2R9xlAzRJfZTOsp97swdMRJW2419H-kX5_6klnV27fXcvYlQaCgYKASESARMSFQHGX2MiuCAIYY4jhQkgsH2IwFKBFw0170'

For my surprise, I get all the time a 401 error, and it's not able to authenticate in any way.

I created this extension method to configure the Authentication, so this is basically it:

public static void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "https://accounts.google.com", 
            ValidAudience = configuration["Authentication:Google:ClientId"], 
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Authentication:Google:ClientSecret"]))
        };
    });

}

Other points to consider.

  1. swagger runs in "https://localhost:7103", so just in case, I included this url in the Authorized JavaScript Origins and Authorized redirect URIs

  2. Secret and client are ok.

  3. When I try to copy/paste the result of "GetAccessToken" from the UI, and try to see its content in jwt.io, this page throws an error, which is strange, like the token is not valid.

  4. For the UI part, I developed the Oauth handshake using "angular-oauth2-oidc", I tried to use "social-login" but got tired of the amount of errors that I have with that.

What am I doing wrong here? I tried several examples on how to authenticate my backend for google and nothing seems to work.

2

There are 2 answers

1
Yogurtu On BEST ANSWER

I found the solution, thanks to a "related question" here from stack overflow that poped up a week later.

First, will change the "configure" method to this, which I took from another suggestion from another post, and failed at that time:

  public static void ConfigureServices(IServiceCollection services, IConfiguration configuration)
        {
            var googleClientId = configuration["Authentication:Google:ClientId"];
            var googleIssuer = "https://accounts.google.com";

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
                .AddJwtBearer(options =>
            {
                options.Authority = googleIssuer;
                options.Audience = googleClientId;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = false,
                    ValidateIssuerSigningKey = false,
                    ValidIssuer = googleIssuer,
                    ValidAudience = googleClientId,
                };
            });

        }

And second, it is basically a problem in the documentation, it is not quite clear (or at least not for me) that the access_token is NOT meant to authenticate your user in your OWN backend, it is used to authenticate your user in GOOGLE APIS (like get email, or similar)

This explains why when I added my token (from getAccessToken()) and tried to parse it in JWT.IO, it thrown an error.

The solution, instead of using:

this.oAuthService.getAccessToken()

I should use:

this.oAuthService.getIdToken()

Found this solution thanks to: https://developers.google.com/identity/openid-connect/openid-connect#exchangecode

but not because it's properly explained, instead it was because the id_token is highlighted that IT IS A JWT TOKEN, I tried that token instead, and my backend started to work without issues.

enter image description here

0
mnedev On

Please note that the id_token is used for authentication, while the access_token is used for authorization.

If you are trying to call an API to get protected data, you normally need to use the access_token. The system checks whether the request is authorized to call up the resource.

[Authorize]
public void GetPrivateData() { 
  ... 
}

So using the id_token sounds wrong to me.

I'm not sure if you can use the access_token from Google at all. It seems that it can only be used for APIs close to Google. Your own API cannot be protected with it. This would also explain the format of the token.

ya29.a0.....

EDIT:

It seems that you can create your own TokenValidator. Maybe you can use it to validate the Google access_token?

see here: Google JWT Authentication with AspNet Core 2.0