Home› Blog› GCP Architecture Series #42 — Short-Lived Credentials and Token Generation…
GCP Architecture GCP Architecture Series

GCP Architecture Series #42 — Short-Lived Credentials and Token Generation

#40 said the private key behind every service account is held in escrow where you can never reach it. This post is the other half of that sentence: you cannot read the key, but you can ask Google to sign with it — four different ways, for four different audiences. The escrow restricts possession, not capability.

Verified against current vendor documentation on 24 September 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.

Business Challenge

#41 described the exchange. This is what comes back from it, and the four shapes it can take.

1
"We will store the refresh token and renew in the background"

There is no refresh token. When you generate an access token by impersonation, the access token comes without a refresh token, which means that when the token expires, you must repeat the impersonation process to generate a new one.

Correct approach

Renewal is re-impersonation, not a refresh call. That is deliberate — it is what forces the caller to re-present their own identity, which is #41's whole argument.

2
"We set the JWT to expire in twelve hours like the access tokens"

Only if nothing Google validates it. The exp claim must be no more than 12 hours in the future — but if you are calling a Google API, the exp claim must be set no more than 1 hour in the future.

Correct approach

Pick the expiry from the audience, not from a house standard. The same token is legal for twelve hours against your own service and rejected at sixty-one minutes against Google's.

3
"We will chain through a middle account to grant the access"

A chain does not confer anything. The intermediary only passes on the request — it does not give the caller or the final account any additional access. Each account simply grants the Service Account Token Creator role to the previous one.

Correct approach

Use a chain to cross a trust boundary, never to accumulate privilege. And if one call suffices, Google says to create the credential directly.

4
"An ID token and an access token are basically interchangeable"

They answer different questions. An access token carries permissions and is accepted for authentication by most Google APIs; an ID token carries identity for something that checks an audience.

Correct approach

Choose by what sits at the other end. Sending the wrong one produces an authentication failure that looks like a permissions problem.

Architecture

The Service Account Credentials API mints four things, and all four are the same private key being used on your behalf.

Diagram: the four short-lived credential types from the Service Account Credentials API, what validates each one, the two different twelve-hour limits for access tokens and self-signed JWTs, and how a delegation chain passes a request without conferring access
Four outputs, one escrowed key, and two unrelated things both called twelve hours.

The four types, by who checks them

TypeWhat it carriesWho validates it
OAuth 2.0 access token Permissions. Google. Accepted for authentication by most Google APIs.
OIDC ID token Identity, for a stated audience. Whatever the audience names — Cloud Run, IAP, your own service.
Self-signed JWT Whatever claims you write. Your application, an API Gateway, or a Google API.
Self-signed blob Arbitrary bytes. Anything that can verify a signature.
All four are #40's escrowed key, working for you

Both signing methods say it explicitly: they sign using the service account's system-managed private key — the same key #40 described as always held in escrow, where you can never access it directly. That reframes the escrow entirely. It is not a limitation on what you can do with the key; it is a limitation on where the key can be. You keep signing, issuing and authenticating, and you lose only the ability to copy the key onto a laptop. Which is the one capability that has ever caused an incident.

The two twelve-hour limits, which are not the same limit

Access tokenSelf-signed JWT
Default maximum 1 hour (3,600 seconds). You set exp; the ceiling is 12 hours.
Longer is possible by Adding the account to the allowServiceAccountCredentialLifetimeExtension list constraint, to reach 12 hours (43,200 seconds). Nothing — 12 hours is the ceiling.
Calling a Google API Same limits. 1 hour maximum, regardless.
Who enforces it The token issuer, at generation. The recipient, at validation.

The last row is the one to internalise. An over-long access token request fails immediately and visibly, at the moment you ask for it. An over-long JWT is minted happily and rejected later by whatever receives it — so the same mistake is a build-time error in one case and a runtime error in the other, against a service you may not control.

Delegation chains, and what they do not do

A delegated request involves more than two identities: the caller, one or more intermediaries, and finally the account the credential is for. Each account grants the Service Account Token Creator role to the previous one in the chain.

The property worth quoting is what the middle does not do. An intermediary only passes on the request — it does not give the caller or the final account any additional access. A chain is a route, not an accumulation. If you were hoping that passing through a privileged account would lend its privileges onward, it does not, and that is the design working correctly.

Google's own guidance is to avoid the mechanism where possible: if you can generate a token with the required permissions with a single token generation call, you should create short-lived credentials for that service account directly.

Why This Architecture Holds Up

One line in the JWT section deserves more attention than its placement suggests. Among the listed uses is treating a service account as an identity provider by signing a JWT that contains arbitrary claims about a user, account, or device.

That is a larger capability than it appears. A service account can assert facts — about a person, a machine, anything — signed with a key that is verifiable by anyone and held by nobody. It is a small identity provider with the private key problem already solved, because the key never leaves Google and you never had a copy to protect.

The caution follows from the same property. Nothing validates the content of those claims. The signature proves the service account asserted them; it proves nothing about whether they are true. So the trust you are establishing is exactly the trust you place in whatever code decided what to put in the payload — and anybody who can impersonate that service account can assert anything in its name, with a valid signature. #41's point about auditing who holds getAccessToken applies here with more force than it did for access tokens, because a forged claim outlives the token that carried it.

What to do with this

  1. Pick the type from the verifier. Google APIs take access tokens; audience-checking services take ID tokens; your own code takes whatever you decide to validate.
  2. Do not build refresh logic. There is no refresh token; renewal is another impersonation, and a library already does it.
  3. Set JWT expiry from the destination — one hour for Google APIs, up to twelve elsewhere.
  4. Expect JWT expiry errors at runtime, not at generation. The recipient enforces it.
  5. Use a chain only to cross a boundary, and prefer a direct credential when one call is enough.
  6. Treat signing authority as assertion authority. Whoever can impersonate the account can make it say anything.

Key Architecture Decisions

DecisionChoose thisBecause
Calling a Google API Access token Accepted for authentication by most Google APIs.
Calling a service that checks an audience ID token It carries identity rather than permissions.
Asserting claims between your own services Self-signed JWT The account can act as an identity provider.
Signing arbitrary bytes Signed blob It uses the system-managed private key you cannot hold.
Token renewal Re-impersonate There is no refresh token.
A job longer than an hour The lifetime extension list constraint Default maximum is 1 hour; 12 hours per named account.
JWT expiry for a Google API No more than 1 hour The 12-hour ceiling does not apply to Google APIs.
Reaching an account you cannot call directly A delegation chain Each link grants Token Creator to the previous one.
Hoping a chain adds privilege It does not The intermediary only passes on the request.

Closing Thought

Three posts ago the argument against service account keys rested on a comparison: a static file you hold, against a managed key you do not. The obvious objection to that is capability — surely holding the key lets you do things the managed one will not. This API is the answer, and the answer is no. Sign a blob, mint a token, assert a claim, authenticate to anything: all of it is available through a key that never leaves Google, because possession was never what made the key useful.

What possession bought was the ability to use the key without asking, which is the same thing as using it without being recorded — and that is precisely what #40's non-repudiation threat was about. The escrow does not take a capability away; it inserts a step where you have to say who you are. Everything in this post is the shape of that step, repeated four times for four audiences, with two different clocks and one chain that deliberately carries nothing but a request.

Next in this series

#43 takes the mechanism outside Google entirely: Workload Identity Federation for AWS — how a workload holding AWS credentials gets a Google token without anyone issuing it a Google credential at all.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent