Home > Enterprise >  Golang Oauth2 Service account returns empty refresh token string
Golang Oauth2 Service account returns empty refresh token string

Time:01-03

I'm having a problem with the google/Oauth2 package when attempting to authenticate through the service account using a server to server authentication. Google responds with a token struct with an empty refresh token string, and the token expires in 1h, which I can't refresh as I don't have a refresh token. Here is the code snippet I'm using:

/*
import(
    "github.com/google/go-containerregistry/pkg/authn"
    gcr "github.com/google/go-containerregistry/pkg/name"
    "github.com/google/go-containerregistry/pkg/v1/remote"
)

*/
data, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", path, serviceAccountFilePath))
if err != nil {
   log.Fatalf("Failed to read GCP service account key file: %s", err)
}
ctx := context.Background()
fmt.Println(scopes)
creds, err := google.CredentialsFromJSON(ctx, data, scopes...)

if err != nil {
   log.Fatalf("Failed to load GCP service account credentials: %s", err)

}
t, _ := creds.TokenSource.Token()
fmt.Println(t.Expiry.Sub(time.Now()).String(), t.RefreshToken, ">>>")

r, err := gcr.NewRegistry("https://gcr.io")
if err != nil {
 log.Fatalf("failed to ping registry: %s", err)
}
authToken := authn.FromConfig(authn.AuthConfig{
    RegistryToken: t.AccessToken,
})

repo, err := gcr.NewRepository(fmt.Sprintf("%s/%s", urlPrefix, imageName))
repo.Registry = r
list, err := remote.List(repo, remote.WithAuth(authToken))

output

I tried different ways while using the service account for authentication, such as the config and JWT but I still got the same result.

CodePudding user response:

Service accounts don't need / use refresh tokens.

Refresh tokens are used for offline access by standard Oauth2 authorization. If the user is offline then the application can use the refresh tokens to get an new access token and make requests on behalf of the user.

With service accounts they are already preauthorized and have access to the data they have. A request should return an access token once that access token expires after an hour you just make a new authorization request to get a new access token.

Refresh tokens are unnecessary in the case of service accounts. When the access token expires just run your auth code again to get a new one. Its saving you a step.

CodePudding user response:

Thanks to @DalmTo's hints, I solved the problem.

So the fix for the such problem was by not using the credentials out of google.CredentialsFromJSON() func will return the token source without refreshing the token in case of passing the service account to that function, which means that you can't refresh your token when it expires again later. Also, anticipating and re-authenticating to generate a new token didn't work for me (no clue why).

So I had to convert the JSON of the service account into JWT through this func instead

scopes := []string{"https://www.googleapis.com/auth/cloud-platform"}
tokenSource, err := google.JWTAccessTokenSourceWithScope(serviceAccountFileBytes, scopes...)

The reason that this one works, is because it creates the JWT token internally through the service_account's properties such as client email and client_id and private_key as GCP allows us to create our local JWT tokens and encode them.

  • Related