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

[FR] Is it possible to use impersonated service account? #1861

Closed
blue-hope opened this issue Aug 10, 2022 · 19 comments
Closed

[FR] Is it possible to use impersonated service account? #1861

blue-hope opened this issue Aug 10, 2022 · 19 comments

Comments

@blue-hope
Copy link
Contributor

blue-hope commented Aug 10, 2022

Is your feature request related to a problem? Please describe.
maybe related to #1703
I think It is not natural for the client to always have the service account json file, and I want to access the firebase admin through impersonate service account generated by gcloud auth application-default login --impersonate-service-account= which the service account has permission for generating custom token.
However, when I use the service account which now stored in ADC, firebase admin throws an error message: 'Refresh token must contain a "client_id" property.'.
In fact, the impersonate service account does not have a client_id itself, but it is contained in source_credentials like:

{
  "delegates": [],
  "service_account_impersonation_url": "",
  "source_credentials": {
    "client_id": "",
    "client_secret": "",
    "refresh_token": "",
    "type": "authorized_user"
  },
  "type": "impersonated_service_account"
}

Describe the solution you'd like
When checking ADC's service account, if type is impersonated_service_account, then check source_credentials.

Describe alternatives you've considered
I can manually get my service account from ADC and use 'source_credentials'. But is it really the only solution for checking impersonated service account?

Additional context
No additional context

@google-oss-bot
Copy link

I found a few problems with this issue:

  • I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
  • This issue does not seem to follow the issue template. Make sure you provide all the required information.

@lahirumaramba
Copy link
Member

Hey @blue-hope Thank you for your feature request and the PR! I am looking into this now. Thank you for your patience!

@blue-hope
Copy link
Contributor Author

blue-hope commented Nov 1, 2022

@lahirumaramba Are there any updates?

@Klaitos
Copy link

Klaitos commented Jan 12, 2023

Hello, any chance to get that reviewed ? It will helps a lot to reduce the risk of generating a json key.
Thanks !

@lahirumaramba
Copy link
Member

This is fixed in #1862 and now included in the v11.5.0 release.

Thanks folks for your patience on this! Try out the new feature if you get a chance and let us know what you think.
If you encounter any issues, please open a new issue on Github. Thank you!

@frederikvanhevel
Copy link

Hi @blue-hope ! Thanks for working on this! Could you let me know what your initializeApp function looks like?

I'm initializing firebase like this:

initializeApp({
        credential: applicationDefault()
    })

But I get the following error:

Error: //cloud.google.com/docs/authentication/. If you are getting this error with curl or similar tools, you may need to specify 'X-Goog-User-Project' HTTP header for quota and billing purposes. For more information regarding 'X-Goog-User-Project' header, please check https://cloud.google.com/apis/docs/system-parameters. Raw server response: "{"error":{"code":403,"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/. If you are getting this error with curl or similar tools, you may need to specify 'X-Goog-User-Project' HTTP header for quota and billing purposes. For more information regarding 'X-Goog-User-Project' header, please check https://cloud.google.com/apis/docs/system-parameters.","errors":[{"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/. If you are getting this error with curl or similar tools, you may need to specify 'X-Goog-User-Project' HTTP header for quota and billing purposes. For more information regarding 'X-Goog-User-Project' header, please check https://cloud.google.com/apis/docs/system-parameters.","domain":"usageLimits","reason":"accessNotConfigured","extendedHelp":"https://console.developers.google.com"}],"status":"PERMISSION_DENIED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"SERVICE_DISABLED","domain":"googleapis.com","metadata":{"consumer":"projects/764086051850","service":"identitytoolkit.googleapis.com"}}]}}"

@rafael-fecha
Copy link

Hi @blue-hope ! Thanks for working on this! Could you let me know what your initializeApp function looks like?

I'm initializing firebase like this:

initializeApp({
        credential: applicationDefault()
    })

But I get the following error:

Error: //cloud.google.com/docs/authentication/. If you are getting this error with curl or similar tools, you may need to specify 'X-Goog-User-Project' HTTP header for quota and billing purposes. For more information regarding 'X-Goog-User-Project' header, please check https://cloud.google.com/apis/docs/system-parameters. Raw server response: "{"error":{"code":403,"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/. If you are getting this error with curl or similar tools, you may need to specify 'X-Goog-User-Project' HTTP header for quota and billing purposes. For more information regarding 'X-Goog-User-Project' header, please check https://cloud.google.com/apis/docs/system-parameters.","errors":[{"message":"Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the identitytoolkit.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/. If you are getting this error with curl or similar tools, you may need to specify 'X-Goog-User-Project' HTTP header for quota and billing purposes. For more information regarding 'X-Goog-User-Project' header, please check https://cloud.google.com/apis/docs/system-parameters.","domain":"usageLimits","reason":"accessNotConfigured","extendedHelp":"https://console.developers.google.com"}],"status":"PERMISSION_DENIED","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"SERVICE_DISABLED","domain":"googleapis.com","metadata":{"consumer":"projects/764086051850","service":"identitytoolkit.googleapis.com"}}]}}"

+1

@Klaitos
Copy link

Klaitos commented Jan 24, 2023

Hello, you need to authenticate as a service account, not an end-user.
gcloud auth application-default login --impersonate-service-account my-service-account@email.com

Of course your end-user will need specific permission like Service Account User on the impersonated service account

@frederikvanhevel
Copy link

Hey @Klaitos , that's what i have been doing but without luck. I'm impersonating the firebase service account that has the following permissions:
Screenshot 2023-01-25 at 13 03 45
And as for the end user, my account has Owner permissions so i should be good.
Is there anything else I could be doing wrong?

@Klaitos
Copy link

Klaitos commented Jan 25, 2023

Hum yes you're right, it does not simply work on my local computer either.
I figured it out how to make it works but it requires a lot of changes in the class ImpersonatedServiceAccountCredential, i can try to make a pull request but i don't know many things on oauth2 scopes.

@Klaitos
Copy link

Klaitos commented Jan 25, 2023

@blue-hope i think we might need your help on this one. I tried some code on my laptop, it works when we tweak the function getAccessToken of class ImpersonatedServiceAccountCredential with something like

  const auth = new GoogleAuth({
    scopes: 'https://www.googleapis.com/auth/cloud-platform',
  });
  const client = await auth.getClient();
  const json = await client.getAccessToken()

  if (!json.token || !json.res?.data.expireTime) {
    throw new FirebaseAppError(
      AppErrorCodes.INVALID_CREDENTIAL,
      `Unexpected response while fetching impersonated access token: ${JSON.stringify(json)}`,
    );
  }

  return {
    access_token: json.token,
    expires_in: new Date(json.res.data.expireTime).valueOf() - new Date().valueOf()
  }

but i'm not aware of unexpected consequences

@lahirumaramba
Copy link
Member

It looks like we are not handling the token creation for impersonated accounts correctly here:

public getAccessToken(): Promise<GoogleOAuthAccessToken> {

Looking at the implementation in google-auth-library-nodejs this needs a bit more work. We need to send the token request to iamcredentials.googleapis.com instead of using the REFRESH_TOKEN_HOST. We also need to include the required scopes for admin sdk and add support for delegates in impersonated credentials. See: https://github.com/googleapis/google-auth-library-nodejs/blob/bdc6339014ae13945bbf82576f7ff71534851ab1/src/auth/impersonated.ts#L132

The proper fix for this is to migrate Admin SDK credentials handling logic to use google-auth-library-nodejs. This migration prevents us from having to maintain a separate codebase to perform the same actions that google-auth-library-nodejs handles better.

This is part of the reason for @Klaitos's workaround above to work, as it creates a new GoogleAuth client using ADC (and the client is smart enough to detect impersonated service account credentials from your environment).

We are currently doing the initial planning for credentials migration to google-auth-library-nodejs and part of that work is also related to #1377

@blue-hope
Copy link
Contributor Author

Sorry for my naive approach for getting access token.
Don't we have to hot-fix REFRESH_TOKEN_HOST to iamcredentials.googleapis.com before implementing google-auth-library-nodejs? @lahirumaramba
And if I can get a chance, I would like to participate the migration task.

@Klaitos
Copy link

Klaitos commented Jan 26, 2023

It's not that simple because you need to call the route iamcredentials.googleapis.com with an access token from the source user in order to get the access token for the service account and we do not have a simple way to get the first one i think

@lox
Copy link

lox commented Aug 29, 2023

Folks, is there an open issue tracking this? It seems like a huge problem that firebase doesn't work out of the box with Google's Recommended Security Practices for Service Accounts.

@lox
Copy link

lox commented Nov 9, 2023

Can we re-open this please?

@lox
Copy link

lox commented Nov 9, 2023

I implemented a workaround to do service account impersonation in https://gist.github.com/lox/8bff5607c3e713c92a03a631796ab3f3.

@velocd
Copy link

velocd commented Mar 7, 2024

Any updates on this? Is @lox workaround still the only way? gcloud's documentation goes into incredible depth of why service accounts are risky but there's no official implementation for service account impersonation in firebase admin.

@lahirumaramba
Copy link
Member

Hey @velocd we have made some changes internally to migrate credentials handling to use google-auth-library. Check out #1377 (comment) for a custom build if you have some time to test it out and share any early feedback. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants