Firebase Custom Token - verify JWT fails in apigee with the error Key Id Missing for JWT Token: policy

Former Community Member
Not applicable

i'm generating the JWT custom token using Firebase Admin SDK.. the token gets generated and i am trying to add the verify the same token in apigee api tool.

when i am trying to verify the token - i have been receiving message "Key Id Missing for JWT Token"

when i parse the taken itself in jwt.io i don't see KID attribute in the token .. is that that reason why parsing is failing. ?

i have written a custom service and using the public key listed by google , i can verify the token.

so i need some guidance here what is missing piece here..

https://firebase.google.com/docs/auth/admin/verify-id-tokens

i have tried to very with google listed public key/ jwk and no luck

0 5 2,083
5 REPLIES 5

Please show

  • The VerifyJWT policy configuration you are using
  • The decoded header of a JWT that fails

The VerifyJWT policy will use the Key ID (kid in the header) to de-reference a key in a JWKS. Typically the JWKS is specified at a URI, which allows any party to retrieve the public keys used by an issuer. For Google signin , the JWKS is available at https://www.googleapis.com/oauth2/v3/certs .

The keys used by an issuer may change over time; this is why issuers publish a JWKS endpoint. It allows verifying parties (like Apigee Edge) to lookup the keys at the time of verification. Verifiers perform the lookup using the kid. If you examine the payload at that /oauth2/v3/certs endpoint you will see a JSON blob containing one or more JWK's, each one carrying a kid. Like this:

{
  "keys": [
    {
      "kid": "f6f80f37f21c23e61f2be4231e27d269d6695329",
      "e": "AQAB",
      "kty": "RSA",
      "alg": "RS256",
      "n": "8Q-bsTm7Mf...HMXczw",
      "use": "sig"
    },
    {
      "alg": "RS256",
      "n": "0QW_fsq8WFt....ZFxw",
      "use": "sig",
      "kid": "05a02649a5b45c90fdfe4da1ebefa9c079ab593e",
      "e": "AQAB",
      "kty": "RSA"
    }
  ]
}

The verifier uses the kid in the JWT to find the JWK in the JWKS endpoint.

If you specify a JWKS endpoint but your JWT has no kid, then VerifyJWT cannot find the corresponding key in the JWKS. As a result of this situation, at runtime, you will see the error "Key Id Missing for JWT Token".

There may be a separate problem. I looked at the documentation for verifying a Firebase ID token. It give this URL as the endpoint at which keys are available: https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com and found something that is not a JWKS. The data looks like this:

{
  "5486da3e1bf209c6f75629d1d834f710ca309d50": "-----BEGIN CERTIFICATE-----\nMI...oP+y3Ps=\n-----END CERTIFICATE-----\n",
  "c8425f94bee1e47f14d79fabd812254b1bf918c0": "-----BEGIN CERTIFICATE-----\nMIIDHDCCA...78hE+rJs=\n-----END CERTIFICATE-----\n"
}

This is not a JWKS, and the VerifyJWT policy cannot automatically read that. The good news is that this is a problem that has been seen by others. The actual JWKS endpoint, which I did not see in the documentation, is https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com.

This endpoint renders a JWKS like so:

{
  "keys": [
    {
      "kid": "c8425f94bee1e47f14d79fabd812254b1bf918c0",
      "e": "AQAB",
      "kty": "RSA",
      "alg": "RS256",
      "n": "pRNXPvCIT...PyAQ",
      "use": "sig"
    },
    {
      "kid": "5486da3e1bf209c6f75629d1d834f710ca309d50",
      "e": "AQAB",
      "kty": "RSA",
      "alg": "RS256",
      "n": "k842NIJke...pwpFtQ",
      "use": "sig"
    }
  ]
} 

The latter URL is the one you should use in a VerifyJWT policy for the JWKS uri.

<VerifyJWT name='VerifyJWT-1'>
  <Source>variable-name-here</Source>
  <Algorithm>RS256</Algorithm>
  <PublicKey>
    <JWKS uri='https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com'/>
  </PublicKey>
  ...
</VerifyJWT>

The former won't work.

Former Community Member
Not applicable

thanks Dino for your response.

so i tried with url where i could see "kid" is being populated on GCP for the project account that i am using JWK link

<PublicKey>
	<JWKS uri="https://www.googleapis.com/service_accounts/v1/jwk/hhp-diginn-automation-dev@appspot.gserviceaccount.com"/>
 </PublicKey>

hhp-diginn-automation-dev is the account that is used for signing this JWT.

i still get the same error message from apigee verify JWT step.

{"fault":{"faultstring":"Key Id Missing for JWT Token: policy(Verify-JWT-again-4)","detail":{"errorcode":"steps.jwt.KeyIdMissing"}}}

i'm also sharing here the JWT token that i am trying to verify as well

eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImNsYWltcyI6e30sImV4cCI6MTU2NzUzNzc4NiwiaWF0IjoxNTY3NTM0MTg2LCJpc3MiOiJoaHAtZGlnaW5uLWF1dG9tYXRpb24tZGV2QGFwcHNwb3QuZ3NlcnZpY2VhY2NvdW50LmNvbSIsInN1YiI6ImhocC1kaWdpbm4tYXV0b21hdGlvbi1kZXZAYXBwc3BvdC5nc2VydmljZWFjY291bnQuY29tIiwidWlkIjoiODAzNDQ3NiJ9.or8AMHx6FyzdXXgdiTX9mwJLWzALwVQWmdvBtMim68fTm2QrPdWZoDnQhz88T5j2CmVzGXbJgZ4REXpL1nImiNTEojwUvIYTnaS-yz5800tKofGspj2NHxijOkpUPjzPlMbVoBQfssZKG5LANy45VfrWlprXjdanqJtEAtqtcsBC2IST9ee0tOoiMTmWPSPa_fm2FBFZceX-q4BY8RuDLoD7kteSgraP7iCizKcpBVEL2EdgFHxK3VEUuKERhJPloga3sXFBdK3gBr4eAvEM8-73pK5Tnrsj37QHg4clu4yw4M6wH4I7QEINZR80tCafikNakHZgd6aCcOdoI_KPGg

is there another way that i can use apigee tool to validate the JWT token that i'm generating from firebase/GCP ?

Mohan, you have not showed your JWT header. I asked for that and you did not provide it.

Also, Maybe you missed what I wrote. I will repeat it here:

The VerifyJWT policy will use the Key ID (kid in the header) to de-reference a key in a JWKS.

If you specify a JWKS endpoint but your JWT has no kid, then VerifyJWT cannot find the corresponding key in the JWKS. As a result of this situation, at runtime, you will see the error "Key Id Missing for JWT Token".

The error is telling you there is no kid. That JWT cannot be verified with a JWKS if there is no kid.

Former Community Member
Not applicable

Dino, the custom JWT token that is generated via below Firebase Admin API call, every thing else is abstract for caller.

FirebaseAuth.getInstance().createCustomToken(uid)

The API call does not seem to have "kid" attribute in it, and i also dont see any API's that can be used to set this attribute while token generation. I have opened a support ticket with Firebase team on this to validate and confirm.

second part - GCP also exposes the pubilc key that corresponding to the JWT token that is being used generated at

https://www.googleapis.com/robot/v1/metadata/x509/hhp-diginn-automation-dev@appspot.gserviceaccount....

this is also "rotatable" public key. In Apigee is there a way to reference public key url to validate the JWT token instead of going through jwks--> kid-- > public key path presuming "kid" will not be part JWT header.

Using this public key / 3rd part JWT library i was definetely able to verify the JWT token that is being generated.

The API call does not seem to have "kid" attribute in it, and i also dont see any API's that can be used to set this attribute while token generation. I have opened a support ticket with Firebase team on this to validate and confirm.

I feel your pain. If the JWT does not include a kid in the header, then perhaps the intent by the Firebase system is that verification will not use JWKS. I haven't read the docs thoroughly and I don't know exactly how you're generating the JWT. But it's possible there is a different suggested path for verifying. Maybe there is a well-known public key that is not identified by kid, somewhere. In that case you can just refer to the public key directly, without using the kid as a lookup parameter.

this is also "rotatable" public key.

Yes, I can infer that from the format of the payload. It's a series of key-value pairs where the key is the unique kid, and the value is the certificate in PEM form. Obviously the goal is to support multiple distinct and possibly rotating keys.

In Apigee is there a way to reference public key url to validate the JWT token instead of going through jwks--> kid-- > public key path presuming "kid" will not be part JWT header.

I don't know how this would work. That URL you gave offers 3 distinct certificates. How would the VerifyJWT policy in Apigee Edge choose one of those certificates to use for verification? The way it works with JWKS is: the kid is the discriminator. If the JWT stipulates kid = X, then VerifyJWT finds the JWK with the kid = X, and uses THAT public key to verify the JWT. And by the way this is not particular to Apigee Edge; this is how JWKS and JWT verificaiton is intended to work. That's the whole point of the kid in the JWT standard: to identify the public key to be used to verify the signature.

You're asking "Can Apigee Edge verify a JWT, in the case in which there's no way to determine which public key to use?" and the answer to that should be obvious. No.

I can imagine you building some logic to iterate through the available public keys, but that seems like a really really bad idea. There needs to be a documented way for verifiers to verify the JWT. Either the JWT has a kid in the header, or there is some alternative, like a documented "Well known" public key that does not rotate.


EDIT. I just read the Firebase documentation regarding custom tokens and here's what I found. Regarding "custom tokens":

Custom tokens are signed JWTs where the private key used for signing belongs to a Google service account.

...

This method of initialization is suitable for a wide range of Admin SDK deployments. Also it enables the Admin SDK to create and sign custom tokens locally, without making any remote API calls. The main drawback of this approach is that it requires you to package a service account JSON file [ and hence the private key] along with your code.

Also I found doc on verifying tokens, which says:

The ID token verification methods included in the Firebase Admin SDKs are meant to verify ID tokens that come from the client SDKs, not the custom tokens that you create with the Admin SDKs.

This tells me that when you try to use https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com , you are not using a documented, supported mechanism for verifying custom firebase tokens.

Also, this tells me: The custom token is signed by the private key which is stored in the JSON file. Always. To verify such a token You need to extract the public key from that private key and embed that public key into VerifyJWT, or otherwise make the public key available to the VerifyJWT policy.

There is no kid because the private key is not rotatable. There is just one key - THE private key in the JSON file. It would be good for Firebase to generate a kid anyway, to clarify matters.

But in any case now you know where to find the public key.