aeat.adapters.outbound.google._impersonation module¶
Service-account impersonation credential source for Google API access.
A gestor operating for several represented entities may want one shared
Google identity backing the Sheets/Drive export mirror instead of every team
member running the interactive per-profile OAuth Desktop consent flow
(run_login_flow()). Google’s supported mechanism for
this is service-account (SA) impersonation: a locally-discoverable identity —
Application Default Credentials (ADC) — is granted IAM
roles/iam.serviceAccountTokenCreator on a target SA, and every API call mints
a short-lived, scoped access token for that SA rather than presenting the ADC
identity’s own token directly.
GoogleImpersonationConfig is the typed,
frozen configuration record; resolve_impersonated_credentials()
performs the two-step resolution (ADC discovery, then impersonation wrapping)
and eagerly validates the grant with one real token refresh so a misconfigured
SA fails loudly at resolution time rather than deep inside a later Sheets write.
That eager refresh also covers ADC freshness: Google’s own impersonated-
credentials refresh implementation refreshes a stale or invalid SOURCE
credential internally before minting the impersonated token
(google.auth.impersonated_credentials.Credentials._perform_refresh_token
calls source_credentials.refresh(request) whenever
source_credentials.token_state is STALE or INVALID), so a
merely-stale (but still refreshable) ADC user credential is transparently
renewed with no operator action.
resolve_impersonated_credentials() additionally
distinguishes the two ways that refresh can still fail: a
genuinely revoked/expired ADC SOURCE credential (the operator’s local
gcloud auth application-default login grant itself is dead) raises
GoogleAuthAdcStaleError naming the
gcloud re-login remediation, while every other refresh failure (a real IAM
Token Creator grant problem on target_principal) raises
GoogleAuthImpersonationRefusedError naming
the IAM role-grant remediation instead. Per
no-silent-under-declaration, a stale token is never silently reused or
misreported as an unrelated IAM refusal.
Unlike build_google_credentials() (the
existing OAuth-Desktop path), the resolved credential itself persists
NOTHING: ADC is discovered fresh from the host environment on every call
(GOOGLE_APPLICATION_CREDENTIALS, gcloud user credentials, or an
attached workload identity), and the impersonated access token is held only
in memory for the process lifetime, never written to secure storage or
workflow state (sensitive-financial-data-secure-storage-only: there is
no long-lived secret here to protect because none is stored).
GoogleCredentialSourceSelection persists only
the non-secret CONFIGURATION (which kind a profile has chosen, and the target SA
email / scopes) — never a credential.
The CLI verb and locale strings for configuring this source are still
deferred (see .vault/adr/2026-07-04-google-sa-impersonation-adr.md,
google-sa-impersonation ADR, and GitHub issue #591 remainder); the
per-profile persistence and
build_google_credentials() dispatch wiring
described there are implemented by
GoogleCredentialSourceSelection and its
session-store persistence functions, consumed by the factory.
See also
GoogleCredentialSourceKindThe closed taxonomy this module implements one member of.
build_google_credentials()The existing default (interactive OAuth Desktop) credential source this module is an alternative to, never a replacement for; also the dispatch point that reads
GoogleCredentialSourceSelection.REQUIRED_SCOPESThe OAuth-Desktop scope bundle; this module’s default
target_scopesexcludes the identity scopes (openid,email) that only apply to a human OAuth consent grant.
Bases:
GoogleAuthErrorRaised when Application Default Credentials cannot be discovered on this host.
Emitted when
google.auth.default()fails to locate any of the ADC discovery sources (GOOGLE_APPLICATION_CREDENTIALSenv var, thegcloud auth application-default loginuser credential file, or an attached GCE/GKE/Cloud Run workload identity).- Parameters:
- Return type:
None
- exception GoogleAuthAdcStaleError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
GoogleAuthErrorRaised when a discovered ADC source credential can no longer be refreshed.
ADC was discovered (unlike
GoogleAuthAdcUnavailableError, where discovery itself fails), but the source credential’s own refresh failed — the common case is agcloud auth application-default logingrant that was revoked or expired since it was issued. This is distinct fromGoogleAuthImpersonationRefusedError: here the ADC identity itself is the problem (re-authenticate it), not the IAM grant ontarget_principal(grant Token Creator).- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception GoogleAuthImpersonationRefusedError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
GoogleAuthErrorRaised when IAM refuses to mint an impersonated token for the target principal.
The most common cause is that the ADC identity lacks
roles/iam.serviceAccountTokenCreatorontarget_principal(or, for a chaineddelegatessequence, on the first delegate). The exception context carriestarget_principalso a caller can render the exact IAM grant the operator needs to add.- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- class GoogleImpersonationConfig(**data)[source]¶
Bases:
BaseModelTyped, frozen configuration for one service-account impersonation grant.
- Variables:
target_principal – The service-account email being impersonated (e.g.
"aeat-export@my-project.iam.gserviceaccount.com").target_scopes – OAuth scopes requested for the minted token. Defaults to the Sheets/Drive data-access scopes (
DRIVE_FILE_SCOPE,SHEETS_SCOPE); the identity scopes (openid,email) do not apply to a service account and are intentionally excluded from the default.delegates – Optional chained impersonation sequence. When set, each entry must hold Token Creator on the next, and the ADC identity must hold Token Creator on
delegates[0].subject – Optional user email to impersonate via Workspace domain-wide delegation. Only meaningful when the target SA has been granted domain-wide delegation by a Workspace administrator; this module cannot verify that grant and surfaces whatever refusal Google’s token endpoint returns.
lifetime_s – Requested token lifetime in seconds, bounded to Google’s 3600s ceiling.
- Parameters:
- target_principal: str¶
- target_scopes: tuple[str, ...]¶
- delegates: tuple[str, ...]¶
- subject: str | None¶
- lifetime_s: int¶
- class GoogleCredentialSourceSelection(**data)[source]¶
Bases:
BaseModelPer-profile persisted choice of
GoogleCredentialSourceKind.Persisted via
save_credential_source_selection()/load_credential_source_selection()and read bybuild_google_credentials()to decide whether to hydrate the default per-profile OAuth-Desktop credential or dispatch toresolve_impersonated_credentials().Carries no long-lived secret:
kind = OAUTH_DESKTOPneeds no additional field (the existingOAuthClient/OAuthTokenrecords already hold that path’s credential);kind = SERVICE_ACCOUNT_IMPERSONATIONrequiresimpersonationto be populated with the target SA email and scopes, which are configuration, not a secret — the actual access token is re-derived from Application Default Credentials on every use and is never written to secure storage.- Parameters:
kind (GoogleCredentialSourceKind)
impersonation (GoogleImpersonationConfig | None)
- kind: GoogleCredentialSourceKind¶
- impersonation: GoogleImpersonationConfig | None¶
- describe_impersonation_target(config)[source]¶
Return the exact service-account email
configwould impersonate.Returns
config.target_principalverbatim. Exists as a named accessor (rather than reading the field directly at every call site) so a future CLIshow/statusverb can surface “you are about to grant IAM roles to exactly this identity” without requiring a live token exchange first — satisfying the operator’s need to confirm the SA identity before approving an IAM role grant.- Return type:
- Parameters:
config (GoogleImpersonationConfig)
- resolve_impersonated_credentials(config)[source]¶
Resolve
configinto a validated, impersonatedCredentialsobject.Three-step resolution:
Discover Application Default Credentials via
google.auth.default(), scoped toconfig.target_scopes.Eagerly refresh the discovered SOURCE credential when it is stale or invalid (
_ensure_source_credential_is_fresh()). A merely-stale ADC user credential (past its access-token lifetime but still holding a live refresh token) is silently renewed here — the normal case for a long-running process reusing agcloud auth application-default logingrant. A genuinely dead grant (revoked, expired refresh token) raisesGoogleAuthAdcStaleErrornaming the exactgcloudre-authentication remediation, distinct from an IAM grant problem on the impersonation target.Wrap the (now-fresh) source credentials in
google.auth.impersonated_credentials.Credentialstargetingconfig.target_principal, then eagerly call.refresh()once so a misconfigured grant (missing Token Creator role, wrong scopes, revoked domain-wide delegation) is caught here rather than silently later inside a Sheets/Drive call.
- Parameters:
config (
GoogleImpersonationConfig) – The impersonation target and scope configuration.- Return type:
Credentials- Returns:
A
google.auth.impersonated_credentials.Credentialsinstance carrying a valid, short-lived access token fortarget_principal. Nothing from this resolution is persisted by this application.- Raises:
GoogleAuthAdcUnavailableError – When ADC discovery finds no usable credential source on this host.
GoogleAuthAdcStaleError – When ADC was discovered but its own refresh (renewing an expired access token, or exchanging a long-lived refresh token) fails — the ADC identity itself must be re-authenticated.
GoogleAuthImpersonationRefusedError – When IAM refuses to mint a token for
target_principal(commonly a missing Token Creator grant, or asubjectrequiring domain-wide delegation the target SA was never granted).