Decode JWT Policy concerns and question

We have a client sending JWT token in Authorization header of REST call using bearer. Since the JWT is part of bearer, I am not using a source variable as per the documentation, neither I need verify keys if I am using this policy.

https://docs.apigee.com/api-platform/reference/policies/decode-jwt-policy

Here is my default JWT policy

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <DecodeJWT async="false" continueOnError="false" enabled="true" name="Decode-JWT-2"> <DisplayName>Decode JWT-2</DisplayName> </DecodeJWT>

1. Whenever I add this, I get the following error. I get a proper response when I remove this policy.

{
    "fault": {
        "faultstring": "Unable to identify proxy for host: partner and url: /xyz-app/ticker",
        "detail": {
            "errorcode": "messaging.adaptors.http.flow.ApplicationNotFound"
        }
    }
}

2. I understand we can do simple JWT decoding and validation, for instance verifying the audience as below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DecodeJWT async="false" continueOnError="false" enabled="true" name="Decode-JWT-1">
    <DisplayName>Decode JWT-1</DisplayName>
    <Audience>https://abc.com/ApigeeWeb</Audience>
</DecodeJWT>

but how do I validate a JWT which has array element? For example, if my JWT has

"group": [
    "group-1",
    "group-2",
    "group-3"
  ]

How can I validate if the incoming JWT request contains group-2?

Thank you,

Himalay

Solved Solved
0 5 994
1 ACCEPTED SOLUTION

The first problem: "Unable to identify proxy for host: partner and url: /xyz-app/ticker"

...means there is no API proxy listening at the given hostname and basepath. Check that your proxy is deployed to an environment. Also check that the host+path you are using when invoking the API proxy is the same as the vhost for the environment in which it is deployed, and the basepath configured in your proxy. The error has nothing directly to do with the DecodeJWT policy you are using. The failed request isn't getting that far.


Regarding the second question.

DecodeJWT does not verify a JWT. It simply decodes it. As a side effect, the policy sets variables in the context that hold the content of the decoded (but not verified) JWT header and payload. One of the variables will hold the value of the audience claim , if there is an audience claim. Check the documentation on the DecodeJWT policy for details.

In short, including the <Audience> element into the DecodeJWT policy does nothing. It's also misleading. Don't do that.

You cannot, within the policy itself, check that a custom array claim (like your group example) contains a specific value.

To check that a JWT array claim (like your group example) holds a particular value, you can use a regex match, in a condition that is evaluated AFTER the DecodeJWT policy.

    <Step>
        <Name>Decode-JWT-1</Name>
    </Step>
    <Step>
        <Condition>NOT (jwt.Decode-JWT-1.claim.group ~~ "^\[.*group-2.*\]$")</Condition>
        <Name>RF-MissingGroup</Name>
    </Step>

BUT THIS IS A NO GOOD, TERRIBLE, VERY BAD IDEA.

Depending on the outputs of DecodeJWT for authorization decisions is a bad idea.

The DecodeJWT policy does not verify a JWT.
The DecodeJWT policy does not verify a JWT.
The DecodeJWT policy does not verify a JWT.

All the decode policy does is *decode* the token. It does not verify the signature. The JWT could have any content in it at all. The JWT could have been changed or completely contrived. It could be completely unsigned. By simply trusting the data that is passed in the JWT, without verifying the signature, you are trusting what the unauthenticated client is sending to you. Obviously unacceptable.

You need to use the VerifyJWT policy to verify the signature on the JWT. The VerifyJWT policy also sets variables in the same way I described above for DecodeJWT. After VerifyJWT you can use the same regular expression match (with ~~) to check the value of the VERIFIED group claim.

The correct flow will be something like this:

    <Step>
        <Name>Verify-JWT-1</Name>
    </Step>
    <Step>
        <Condition>NOT (jwt.Verify-JWT-1.claim.group ~~ "^\[.*group-2.*\]$")</Condition>
        <Name>RF-MissingGroup</Name>
    </Step>

And of course the Verify-JWT-1 policy is a VerifyJWT, something like this:

<VerifyJWT name="Verify-JWT-1">
    <Algorithm>RS256</Algorithm>
    <PublicKey>
        <Value ref='publickey'/>
    </PublicKey>
    <Audience>https://abc.com/ApigeeWeb</Audience>
    <Issuer>urn://Issuer</Issuer>
</VerifyJWT>

View solution in original post

5 REPLIES 5

The first problem: "Unable to identify proxy for host: partner and url: /xyz-app/ticker"

...means there is no API proxy listening at the given hostname and basepath. Check that your proxy is deployed to an environment. Also check that the host+path you are using when invoking the API proxy is the same as the vhost for the environment in which it is deployed, and the basepath configured in your proxy. The error has nothing directly to do with the DecodeJWT policy you are using. The failed request isn't getting that far.


Regarding the second question.

DecodeJWT does not verify a JWT. It simply decodes it. As a side effect, the policy sets variables in the context that hold the content of the decoded (but not verified) JWT header and payload. One of the variables will hold the value of the audience claim , if there is an audience claim. Check the documentation on the DecodeJWT policy for details.

In short, including the <Audience> element into the DecodeJWT policy does nothing. It's also misleading. Don't do that.

You cannot, within the policy itself, check that a custom array claim (like your group example) contains a specific value.

To check that a JWT array claim (like your group example) holds a particular value, you can use a regex match, in a condition that is evaluated AFTER the DecodeJWT policy.

    <Step>
        <Name>Decode-JWT-1</Name>
    </Step>
    <Step>
        <Condition>NOT (jwt.Decode-JWT-1.claim.group ~~ "^\[.*group-2.*\]$")</Condition>
        <Name>RF-MissingGroup</Name>
    </Step>

BUT THIS IS A NO GOOD, TERRIBLE, VERY BAD IDEA.

Depending on the outputs of DecodeJWT for authorization decisions is a bad idea.

The DecodeJWT policy does not verify a JWT.
The DecodeJWT policy does not verify a JWT.
The DecodeJWT policy does not verify a JWT.

All the decode policy does is *decode* the token. It does not verify the signature. The JWT could have any content in it at all. The JWT could have been changed or completely contrived. It could be completely unsigned. By simply trusting the data that is passed in the JWT, without verifying the signature, you are trusting what the unauthenticated client is sending to you. Obviously unacceptable.

You need to use the VerifyJWT policy to verify the signature on the JWT. The VerifyJWT policy also sets variables in the same way I described above for DecodeJWT. After VerifyJWT you can use the same regular expression match (with ~~) to check the value of the VERIFIED group claim.

The correct flow will be something like this:

    <Step>
        <Name>Verify-JWT-1</Name>
    </Step>
    <Step>
        <Condition>NOT (jwt.Verify-JWT-1.claim.group ~~ "^\[.*group-2.*\]$")</Condition>
        <Name>RF-MissingGroup</Name>
    </Step>

And of course the Verify-JWT-1 policy is a VerifyJWT, something like this:

<VerifyJWT name="Verify-JWT-1">
    <Algorithm>RS256</Algorithm>
    <PublicKey>
        <Value ref='publickey'/>
    </PublicKey>
    <Audience>https://abc.com/ApigeeWeb</Audience>
    <Issuer>urn://Issuer</Issuer>
</VerifyJWT>

Thanks for the detailed explanation in such a short time. Couple of things I wan't to add.

1. Being new to Apigee I had a hard time figuring out what policy should be used with a condition variable, in my case I used RaiseFault

2. In our case, we are not simply passing the token, but already verifying the token thru an OAuth policy, so decode JWT should be enough. (Please let me know otherwise)

If your proxy receives a JWT, the way to verify it is with VerifyJWT.

The OAuthV2/VerifyAccessToken policy does not verify JWT.

You should not rely on claims extracted from a JWT via DecodeJWT. You should verify the JWT before relying on any claims contained within.

When would you use DecodeJWT , then? One scenario: suppose your client generates a JWT and signs it with a private key. The JWT contains an audience or client_id claim indicates a Client ID. In this case you might:

  • use DecodeJWT to extract the client id
  • call VerifyApiKey to verify that client id
  • extract (as custom attribute) the public key associated to that client id
  • Call VerifyJWT with the public key extracted from the client id
  • if everything verifies, then you can rely on the claims.

Hello dchiesa,

I am using decode jwt policy by giving bearer authorisation header in postman,

how can i decode the jwt from JSONresponse without using the authorisation header

ask a new question, please. There is a blue button in the upper-right corner, labeled "Ask a Question". 

ask-a-new-question.png