Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImpersonatedCredentials can't negotiate proper access token with delegated access to user account #759

Open
tzhou2021 opened this issue Oct 8, 2021 · 9 comments
Assignees
Labels
status: investigating The issue is under investigation, which is determined to be non-trivial. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@tzhou2021
Copy link

tzhou2021 commented Oct 8, 2021

Hello,

For my use case, I have to impersonate a service account B (own by our customer) that has domain wide delegation enabled using another source service account A (own by us). The goal is to access all user emails that service account B has access to using service account A.

        String emailAddress = "customer_email@gmail.com"
        ServiceAccountCredentials sourceCredentials = (ServiceAccountCredentials) ServiceAccountCredentials.fromStream(new FileInputStream("service_account_A.json")).createScoped(Arrays.asList("https://www.googleapis.com/auth/iam"));
        GoogleCredentials impersonatedCredentials = ImpersonatedCredentials.create(
                sourceCredentials,
                "service_account_B@projectB.iam.gserviceaccount.com",
                null,
                Arrays.asList("https://mail.google.com/", "https://www.googleapis.com/auth/calendar"),
                300)
               .createDelegated(emailAddress);

        try {
            impersonatedCredentials.refreshAccessToken();
        } catch (IOException e) {
            System.out.println(e);
            return;
        }
        HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(impersonatedCredentials);
        Gmail service = new Gmail.Builder(httpTransport, jacksonFactory, requestInitializer).setApplicationName("Foundation POC").build();
	ListMessagesResponse response = service.users().messages().list(emailAddress).execute();
	for (Message message : response.getMessages()) {
		System.out.println(message.toPrettyString());
	}

However, the access token retrieved doesn't have access to fetching user email, I got the error below:

Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
  "code": 400,
  "errors": [
    {
      "domain": "global",
      "message": "Precondition check failed.",
      "reason": "failedPrecondition"
    }
  ],
  "message": "Precondition check failed.",
  "status": "FAILED_PRECONDITION"
}

By further examining the implementation of createDelegated(String user) of ImpersonatedCredentials, it actually does not respect the input user, which is not what I was expecting:

Screen Shot 2021-10-08 at 3 15 44 AM

I'm using

        <dependency>
            <groupId>com.google.auth</groupId>
            <artifactId>google-auth-library-oauth2-http</artifactId>
            <version>1.2.0</version>
        </dependency>

I don't really know what could solve this issue for my unique use case, thoughts on this?
Thank you!

@tzhou2021 tzhou2021 changed the title ImpersonatedCredentials can't negotiate proper access token with domain wide delegation enabled ImpersonatedCredentials can't negotiate proper access token with delegated access to user account with domain wide delegation enabled in service account Oct 8, 2021
@tzhou2021 tzhou2021 changed the title ImpersonatedCredentials can't negotiate proper access token with delegated access to user account with domain wide delegation enabled in service account ImpersonatedCredentials can't negotiate proper access token with delegated access to user account. Oct 8, 2021
@tzhou2021 tzhou2021 changed the title ImpersonatedCredentials can't negotiate proper access token with delegated access to user account. ImpersonatedCredentials can't negotiate proper access token with delegated access to user account Oct 8, 2021
@chanseokoh chanseokoh added type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. priority: p2 Moderately-important priority. Fix may not be included in next release. labels Oct 8, 2021
@lesv
Copy link
Contributor

lesv commented Nov 1, 2021

@silvolu Could you ask someone to look at this?

@TimurSadykov TimurSadykov assigned TimurSadykov and unassigned silvolu Nov 21, 2021
@yoshi-automation yoshi-automation added 🚨 This issue needs some love. and removed 🚨 This issue needs some love. labels Feb 19, 2022
@yoshi-automation yoshi-automation added the 🚨 This issue needs some love. label Apr 6, 2022
@TimurSadykov
Copy link
Member

Hi @tzhou2021,

The code that you referenced is expected. It is a default functionality, which is - not implemented. Individual credentials should have an override with a specific implementation. As an example you can see ServiceAccountCredential has this method implemented.

The README for this library has an example how to instantiate ImpersonatedCredentials, have you tried that?

@TimurSadykov TimurSadykov added type: question Request for information or clarification. Not an issue. and removed type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. priority: p2 Moderately-important priority. Fix may not be included in next release. labels Jun 25, 2022
@TimurSadykov
Copy link
Member

requilifying as question

@yoshi-automation yoshi-automation removed the 🚨 This issue needs some love. label Jun 25, 2022
@alamothe
Copy link

Hello,
I have the same problem. Does Google Auth not support impersonation + domain-wide delegation? If not, it's certainly a design flaw.

@TimurSadykov the answer is not satisfying as it's saying to either look at domain-wide delegation or impersonation separately, but there is no example on how to do them together. It also fails for me with "bad request" so it is a bug or just not supported at all.

@alamothe
Copy link

@tzhou2021 did you find a solution?

@TimurSadykov
Copy link
Member

@alamothe Please provide details of your particular issue and we try to figure it out.

In the original issue the problem that @tzhou2021 misunderstood that user override is not respected by referencing GoogleCredentials. The GoogleCredentials is a base class, but actual impersonation is credential specific. If ServiceAccount is used, it has an override implementation:

@Override public GoogleCredentials createDelegated(String user) { return this.toBuilder().setServiceAccountUser(user).build(); }

@alamothe
Copy link

@TimurSadykov You're mixing the part where @tzhou2021 tried to debug why it doesn't work and the problem description.

Let me try to explain just the problem description.

Now, we want A to be able to perform an action on W as user U in that workspace. This should work, because permissions have been adequately set up. However it fails at runtime:

		val credentials = (GoogleCredentials.getApplicationDefault() as ServiceAccountCredentials)
				.createScoped("https://www.googleapis.com/auth/iam")
				.let {
					ImpersonatedCredentials.create(
							it,
							null,
							listOf("service-account-b@project.iam.gserviceaccount.com"),
							listOf(DirectoryScopes.ADMIN_DIRECTORY_USER),
							300)
				}
				.createDelegated("user-u@workspace-w.com")

		val service = Directory.Builder(
				httpTransport,
				GsonFactory.getDefaultInstance(),
				HttpCredentialsAdapter(credentials),
		)
				.setApplicationName("test")
				.build()

		service.users()
				.list()
				.setCustomer("my_customer")
				.setMaxResults(10)
				.setOrderBy("email")
				.execute()

@TimurSadykov
Copy link
Member

@alamothe The use case is clear from the original description, but thanks for providing specific code you use.

I'm trying to understand the issue you are seeing. "It fails" does not provide much data to investigate.

Do you see exactly the same error like @tzhou2021? If you just do refresh token for the created credential, does it work or it throws same error?

@TimurSadykov TimurSadykov added status: investigating The issue is under investigation, which is determined to be non-trivial. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. and removed type: question Request for information or clarification. Not an issue. labels Aug 29, 2022
@weiminyu
Copy link

@TimurSadykov

We are also seeing the same problem in our Appengine Java app. The details are below:

  • Service Account S has domain-wide delegation access to our domain as user U.
  • The Appengine Default Service Account (project-id@appspot.gserviceaccount.com) has Service Account Token Creator permission to S. This is the credential returned by GoogleCredentials.getApplicationDefault().
  • The credential is created as follows:
        ImpersonatedCredentials.newBuilder()
            .setSourceCredentials(GoogleCredentials.getApplicationDefault())
            .setTargetPrincipal(service_account_S_email)
            .setScopes(requiredScopesList)
            .build();

The error message we get back is

{
  "code": 403,
  "errors": [
    {
      "domain": "global",
      "message": "Not Authorized to access this resource/api",
      "reason": "forbidden"
    }
  ],
  "message": "Not Authorized to access this resource/api"
}

One thing we find it odd is that since ImpersonatedCredentials does not override parent's createDelegated() method, we don't have a way to set the subject's email address (user-U@our-domain.co).

By the way, the python library's service account implementation is reportedly working for domain-wide delegation. The GCP professional services even has an example here.

The python service_account code is closer to the Java JwtCredentials than to the ImpersonatedCredentials. Unfortunately, the JwTCredentials does not take another Credentials as signer. Is ImpersonatedCredentials meant to support domain-wide delegation, or should we make a feature request for JwTCredentials to accept another credential as signer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: investigating The issue is under investigation, which is determined to be non-trivial. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

No branches or pull requests

8 participants