Validate multiple firebase project issuer in Apigee policy

Hello Community,

I am trying to validate the issuers of multiple Firebase projects on the same Apigee proxy. I will be calling the same Apigee proxy endpoint from different Firebase apps. Each app will have a different issuer in its JWT.

Example:

Firebase project 1: https://securetoken.google.com/{firebaseId-1}

Firebase project 2: https://securetoken.google.com/{firebaseId-2}

 

 

How can I validate multiple issuers in an Apigee policy?

My Verify JWT policy:

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyJWT continueOnError="false" enabled="true" name="JWT-RS256-Firebase">
<Algorithm>RS256</Algorithm>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<PublicKey>
<JWKS uri="https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"/>
</PublicKey>
<Issuer>https://securetoken.google.com/{firebaseId-1}</Issuer>
</VerifyJWT>

 

Thanks.

7 REPLIES 7

You can use a reference to a variable, for the issuer in VerifyJWT, like this:

 

<VerifyJWT name="JWT-RS256-Firebase">
  <Algorithm>RS256</Algorithm>
  <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
  <PublicKey>
    <JWKS uri="https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"/>
  </PublicKey>
  <!-- verify that the iss claim contains the same value as this variable -->
  <Issuer ref='variable-containing-issuer'/>
</VerifyJWT>

 

But to make that work you need the variable-containing-issuer  variable to contain the string you want to verify, as appearing in the iss claim. So, prior to that VerifyJWT policy executing, you need to assign into that variable. Maybe like this:

 

<AssignMessage name='AM-Set-Expected-Issuer'>
  <AssignVariable>
    <Name>variable-containing-issuer</Name>
    <Template>https://securetoken.google.com/{desired-firebaseId}</Template>
  </AssignVariable>
</AssignMessage>

 

@dchiesa1 Thank you for providing the detailed solution.

Also, I tried the below with condition, if the JWT issuer matches with the first issuer, then validate with the first policy. If it does not, go to the second condition. 

 

   <Step>
     <Name>VerifyJWT-1</Name>
     <Condition>jwt.Decode-JWT.claim.issuer = "https://firebase-issuer-url-1"</Condition>
   </Step>
   <Step>
     <Name>VerifyJWT-2</Name>
     <Condition>jwt.Decode-JWT.claim.issuer = "https://firebase-issuer-url-2"</Condition>
   </Step>

 

However, this did not work, even though the issuer was not matching, it always went to the second step condition and returned an invalid claim(iss).

Do you know if this should work or what could be the reason why it enters the second step policy even though it does not match the issuer?

Thanks

Do you know if this should work 

This should work, if you use a prior step called "Decode-JWT" to decode the JWT, before using VerifyJWT on it. The variable you are referencing, jwt.Decode-JWT.claim.issuer, gets set by the DecodeJWT step.

what could be the reason why it enters the second step policy even though it does not match the issuer?

As for why the VerifyJWT-2 always seems to return "an invalid claim(iss)," I guess it's because the Issuer element in the policy configuration does not match the issuer in the JWT.  There is no other reason for that error to occur.

Check that your policy configuration is consistent with what you think it should be. 

@dchiesa1 

The issue is when I pass the JWT token of the first issuer, it still enters the second policy as well even though the condition doesn't match the issuer and returns the invalid claim.

I tried removing the second step condition and kept only the first step condition and it worked. So, not sure why it went to the condition without a match when both step conditions are kept.

you tried removing the condition on the 2nd step, and "it worked" means what? 

I think maybe there is something else going on , something that is wrong, with your API proxy flow configuration.  if you have the following flow configuration: 

   <Step>
     <Name>VerifyJWT-1</Name>
     <Condition>jwt.Decode-JWT.claim.issuer = "https://firebase-issuer-url-1"</Condition>
   </Step>
   <Step>
     <Name>VerifyJWT-2</Name>
     <Condition>jwt.Decode-JWT.claim.issuer = "https://firebase-issuer-url-2"</Condition>
   </Step>

it is not possible for both of those VerifyJWT steps to execute. That won't happen.  The conditions check for equality to different things. That variable won't change values between either of those two steps. Therefore both conditions will not evaluate to true. So it is not possible that both policies will execute. 

More likely, you have other steps, other conditions, other attachments of these policies and they may or may not have conditions associated to them.  Or those condition are not the actual conditions.  Or something similar.  If I were diagnosing this, I would look for that kind of problem.  If you feel comfortable, collect a debugsession (JSON format) and attach it here.  

Another thing that can help you find problems is running apigeelint on your API proxy.

 

@dchiesa1 With the suggested solution, I could only add a single issuer to a policy. I need to add multiple Firebase project issuer URLs to an Apigee proxy policy.

Firebase project 1: https://securetoken.google.com/{firebaseId-1}

Firebase project 2: https://securetoken.google.com/{firebaseId-2}

Thanks

You can configure  a VerifyJWT policy step to check that the issuer is a specific value- either a fixed "hard-coded" value, or a value indirectly specified in a context variable.  That latter option is what I showed above.

It is not possible to configure  a VerifyJWT policy step to check that the issuer is ONE of a set of values.  

If you want to check that, you can omit the Issuer element from the VerifyJWT policy step, so that you do not verify the issuer within that policy.  Then augment it with a Condition element, to perform the check for the issuer AFTER the VerifyJWT policy step completes.  If you follow this path, then the proxy flow configuration  would be something like: 

   <Step>
     <!-- verify the signature and expiry on the JWT, but not the issuer -->
     <Name>VerifyJWT-1</Name>
   </Step>
   <Step>
     <!-- raise a fault if the issuer is not acceptable -->
     <Name>RF-Invalid-Issuer</Name>
     <Condition>jwt.VerifyJWT-1.decoded.claim.iss != "https://firebase-issuer-url-1" AND
        jwt.VerifyJWT-1.decoded.claim.iss != "https://firebase-issuer-url-2"</Condition>
   </Step>

You could also perform the check within a JavaScript step.  The flow would look similar:

   <Step>
     <!-- verify the signature and expiry on the JWT, but not the issuer -->
     <Name>VerifyJWT-1</Name>
   </Step>
   <Step>
     <!-- valdate the issuer within JavaScript. This may raise a fault. -->
     <Name>JS-Validate-Issuer</Name>
   </Step>

And the JavaScript would look like 

var iss = context.getVariable('jwt.VerifyJWT-1.decoded.claim.iss');
var ACCEPTABLE_ISSUERS = ['https://firebase-issuer-1', 'https://firebase-issuer-2']; 

if (ACCEPTABLE_ISSUERS.indexOf(key) == -1) {
  // this will result in a Fault in the proxy flow
  throw new Error('Unacceptable issuer');
}