CA API Gateway

This chapter describes how to integrate the CA API Gateway with Curity. The CA API Gateway is a security gateway that proxies HTTP traffic and performs security checks on both the data layer and the identity layer.

The Gateway will be configured to use OAuth 2.0 access tokens from Curity to protect the API access. This description assumes that Curity as well is protected behind the Gateway, but that is not a requirement and it does not affect the implementation of the API protection.

Phantom Tokens

This integration uses the concept of Phantom Tokens. This means that the introspect endpoint will return a JWT version of the incoming access token, that we refer to as a Phantom Token. To configure the introspect endpoint to return Phantom tokens refer to Phantom Token Behaviour.

Protecting APIs

The API is protected by having the gateway check for an access token in the incoming request’s Authorization header. In order for the gateway to validate the incoming access token it needs to do two things.

  1. If the token is never seen before it needs to ask the OAuth server
  2. Cache the response from the OAuth server in the Gateway cache (but not in the Gateway DB which is considerably more resource consuming)
../../_images/ca-gateway-integration-flow.png

Fig. 181 Token Verification using the CA API Gateway

  1. The Web-Backend makes a request towards the OAuth protected API
  2. The Gateway first tries to find the Access Token in the cache, but if miss, calls the OAuth server’s Introspect endpoint
  3. The OAuth server responds with token information, and an Access Token in Json Web Token format (JWT).
  4. The Gateway caches the response, for the lifetime of the token
  5. The Gateway forwards the request to the OAuth Protected service, passing the JWT token instead of the original access token, in the Authorization header back to the protected service.

Setting up the gateway to validate access token

To validate an access token a new policy fragment needs to be created. It’s task is to perform the tasks described in the previous section. The benefit of the policy fragment is that it can be included in any service that is published on the gateway.

Note

The steps described to create this policy are created using CA API Gateway version 9.1, but the same tasks are possible to do in any version 8.x of the gateway as well, with minor differences on where to find the assertions, and the look and feel.

1. Create a new policy

Use the ”Create Policy” option in the policies window and select ”Included Fragment” as policy type. Name it ”Require OAuth Token”

../../_images/ca-policy-manager-00.png

Fig. 182 Create new Policy

2. Define cluster properties

To make the policy portable between environments it recommended to use cluster properties when applicable. There are 3 variables that need to be defined in this policy and one Credential.

Variables:

Open via Tasks menu the ”Global Settings -> Manage Cluster-Wide Properties” and add:

  • oauth_introspect_client_id = the_name_of_the_gateway_client
  • oauth_server_host = the hostname of Curity (eg: https://as.company.net:8443)
  • oauth_server_introspection_path = the path of the introspection endpoint as configured in Curity (eg: /oauth/v2/introspection)
../../_images/ca-policy-manager-01.png

Fig. 183 Define Cluster Wide Properties

Credentials

Open via Tasks menu the ”Certificates, Keys and Secrets -> Manage Stored Passwords” and add:

  • oauth_introspect_clientsecret = the client secret
../../_images/ca-policy-manager-02.png

Fig. 184 Define Cluster Wide Credentials

3. Add the policy content

To add the policy content, open the newly created ”Require OAuth Token” policy and copy the XML from Appendix A: – Require OAuth Token fragment in this document and paste it into the window. It should create a structure that looks as the following:

../../_images/ca-policy-manager-03.png

Fig. 185 Add the policy content

After saving and activating the policy fragment is ready to use.

Important

If self-signed certificates are used or an internal CA is used for issuing the certificates for the Curity SSL server, it must be imported into the Gateway’s trust store before the policy can be used.

4. Create Encapsulated Assertion

This step is optional but makes the usage of the enforcement easier to work with. To create an encapsulated assertion, right click on the ”Require OAuth Token” policy in the service window and select ”Create Encapsulated Assertion”. Enter the oauth.* variables as output variables and place the assertion in the ”Access Control” section. (oauth.client_id, oauth.subject, oauth.expiration, oauth.jwt, oauth.scope)

../../_images/ca-policy-manager-04.png

Fig. 186 Create Encapsulated Assertion

Enforcing Access Token in API policies

Now that the ”Require OAuth Token” policy is setup. It can be included in all services that it should protect.

The included fragment (or the encapsulated assertion) should be placed in the beginning of the service policy, after the ”Require SSL or TLS transport” assertion.

Exported variables

The ”Require OAuth Token” fragment will export the following variables:

  • oauth.client_id
  • oauth.subject
  • oauth.expiration
  • oauth.jwt
  • oauth.scope

Note

This assumes that Curity is setup so that it responds with a JWT from the introspection endpoint. See Phantom Token Behaviour for more details about the introspection endpoint and how to include a JWT.

Passing the JWT to Backend APIs

After a successful lookup of the Access Token, the service will eventually route to a backend API. At this point it should pass on the internal JWT that it received from the introspect endpoint. This is done by adding the oauth.jwt to the Authorization header of the routed request.

../../_images/ca-policy-manager-05.png

Fig. 187 Passing JWT to backend

Introspect with application/jwt as accept header

Curity can also respond to requests in the introspection endpoint with the Accept: application/jwt header. When introspecting a valid access token, Curity responds with 200 OK and the JWT in the body of the response. An expired or invalid access token, causes Curity to respond with 204 No Content. This means that the gateway doesn’t need to parse the JSON (as when using normal introspection), making the proxying even faster. Appendix B: – Require OAuth Token, introspect with application/jwt contains a policy fragment that uses application/jwt, also, Curity’s status codes on the introspection request are taken in consideration and the gateway responds accordingly. This fragment only exports the variable oauth.jwt which can be used to replace the access token in the Authorization header forwarded to the API.

Note

In order for CA API Gateway to be able to parse the response from Curity (which in this case includes the header Content-type: application/jwt), you will need to add another cluster wide variable with key contentType.otherTextualTypes and value application/jwt.

Appendix A: – Require OAuth Token fragment

Listing 319 CA API Gateway Require OAuth Token Fragment
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
<?xml version="1.0" encoding="UTF-8"?>
<wsp:Policy xmlns:L7p="http://www.layer7tech.com/ws/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy">
    <wsp:All wsp:Usage="Required">
        <L7p:CommentAssertion>
            <L7p:Comment stringValue="=== Require OAuth Token ==="/>
        </L7p:CommentAssertion>
        <L7p:AuditDetailAssertion>
            <L7p:Detail stringValue="Policy Fragment: Require OAuth Token"/>
        </L7p:AuditDetailAssertion>
        <L7p:CommentAssertion>
            <L7p:Comment stringValue="1. ------ Validate Header -----"/>
        </L7p:CommentAssertion>
        <L7p:CustomizeErrorResponse>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="LEFT.COMMENT"/>
                        <L7p:value stringValue="400"/>
                    </L7p:entry>
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="No token in Authorization header"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "bad_request",
"error_description" : "No access token found in authorization header"
}]]></L7p:Content>
            <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
            <L7p:ExtraHeaders nameValuePairArray="included"/>
            <L7p:HttpStatus stringValue="400"/>
        </L7p:CustomizeErrorResponse>
        <L7p:SetVariable>
            <L7p:Base64Expression stringValue="JHtyZXF1ZXN0Lmh0dHAuaGVhZGVyLmF1dGhvcml6YXRpb259"/>
            <L7p:VariableToSet stringValue="oauth_header"/>
        </L7p:SetVariable>
        <L7p:ComparisonAssertion>
            <L7p:CaseSensitive booleanValue="false"/>
            <L7p:Expression1 stringValue="${oauth_header}"/>
            <L7p:MultivaluedComparison multivaluedComparison="FAIL"/>
            <L7p:Operator operatorNull="null"/>
            <L7p:Predicates predicates="included">
                <L7p:item dataType="included">
                    <L7p:Type variableDataType="string"/>
                </L7p:item>
                <L7p:item binary="included">
                    <L7p:CaseSensitive booleanValue="false"/>
                    <L7p:Negated booleanValue="true"/>
                    <L7p:Operator operator="EMPTY"/>
                    <L7p:RightValue stringValue=""/>
                </L7p:item>
            </L7p:Predicates>
        </L7p:ComparisonAssertion>
        <L7p:Regex>
            <L7p:AutoTarget booleanValue="false"/>
            <L7p:CaptureVar stringValue="introspect_token"/>
            <L7p:OtherTargetMessageVariable stringValue="oauth_header"/>
            <L7p:Regex stringValue="Bearer\s+(.*)"/>
            <L7p:RegexName stringValue="Retrieve token"/>
            <L7p:Replacement stringValue=""/>
            <L7p:Target target="OTHER"/>
        </L7p:Regex>
        <L7p:SetVariable>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="Take first result from regex-match"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X3Rva2VuWzFdfQ=="/>
            <L7p:VariableToSet stringValue="oauth_token"/>
        </L7p:SetVariable>
        <L7p:CommentAssertion>
            <L7p:Comment stringValue="2. ------ Validate Token -----"/>
        </L7p:CommentAssertion>
        <L7p:CustomizeErrorResponse>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="LEFT.COMMENT"/>
                        <L7p:value stringValue="500"/>
                    </L7p:entry>
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="Invalid response from server"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "internal_error",
"error_description" : "Invalid response from OAuth server"
}]]></L7p:Content>
            <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
            <L7p:ExtraHeaders nameValuePairArray="included"/>
        </L7p:CustomizeErrorResponse>
        <wsp:OneOrMore wsp:Usage="Required">
            <wsp:All wsp:Usage="Required">
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue=""/>
                    <L7p:ContentType stringValue="application/json; charset=utf-8"/>
                    <L7p:DataType variableDataType="message"/>
                    <L7p:VariableToSet stringValue="introspect_response"/>
                </L7p:SetVariable>
                <L7p:CacheLookup>
                    <L7p:CacheEntryKey stringValue="${oauth_token}"/>
                    <L7p:CacheId stringValue="oauth_token_cache"/>
                    <L7p:ContentTypeOverride stringValue=""/>
                    <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
                    <L7p:Target target="OTHER"/>
                </L7p:CacheLookup>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Check Cache"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
            <wsp:All wsp:Usage="Required">
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="dG9rZW49JHtvYXV0aF90b2tlbn0mY2xpZW50X2lkPSR7Z2F0ZXdheS5vYXV0aF9pbnRyb3NwZWN0X2NsaWVudF9pZH0mY2xpZW50X3NlY3JldD0ke3NlY3Bhc3Mub2F1dGhfaW50cm9zcGVjdF9jbGllbnRzZWNyZXQucGxhaW50ZXh0fQ=="/>
                    <L7p:ContentType stringValue="application/x-www-form-urlencoded; charset=utf-8"/>
                    <L7p:DataType variableDataType="message"/>
                    <L7p:VariableToSet stringValue="introspect_request"/>
                </L7p:SetVariable>
                <L7p:HttpRoutingAssertion>
                    <L7p:FailOnErrorStatus booleanValue="false"/>
                    <L7p:HttpMethod httpMethod="POST"/>
                    <L7p:ProtectedServiceUrl stringValue="${gateway.oauth_server_host}${gateway.oauth_server_introspection_path}"/>
                    <L7p:ProxyPassword stringValueNull="null"/>
                    <L7p:ProxyUsername stringValueNull="null"/>
                    <L7p:RequestHeaderRules httpPassthroughRuleSet="included">
                        <L7p:ForwardAll booleanValue="true"/>
                        <L7p:Rules httpPassthroughRules="included">
                            <L7p:item httpPassthroughRule="included">
                                <L7p:Name stringValue="Cookie"/>
                            </L7p:item>
                            <L7p:item httpPassthroughRule="included">
                                <L7p:Name stringValue="SOAPAction"/>
                            </L7p:item>
                        </L7p:Rules>
                    </L7p:RequestHeaderRules>
                    <L7p:RequestMsgSrc stringValue="introspect_request"/>
                    <L7p:RequestParamRules httpPassthroughRuleSet="included">
                        <L7p:ForwardAll booleanValue="true"/>
                        <L7p:Rules httpPassthroughRules="included"/>
                    </L7p:RequestParamRules>
                    <L7p:ResponseHeaderRules httpPassthroughRuleSet="included">
                        <L7p:ForwardAll booleanValue="true"/>
                        <L7p:Rules httpPassthroughRules="included">
                            <L7p:item httpPassthroughRule="included">
                                <L7p:Name stringValue="Set-Cookie"/>
                            </L7p:item>
                        </L7p:Rules>
                    </L7p:ResponseHeaderRules>
                    <L7p:ResponseMsgDest stringValue="introspect_response"/>
                    <L7p:SamlAssertionVersion intValue="2"/>
                </L7p:HttpRoutingAssertion>
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X3Jlc3BvbnNlLmh0dHAuaGVhZGVyLmNhY2hlLWNvbnRyb2x9"/>
                    <L7p:VariableToSet stringValue="cache_control"/>
                </L7p:SetVariable>
                <L7p:Regex>
                    <L7p:AutoTarget booleanValue="false"/>
                    <L7p:CaptureVar stringValue="cache_max_age"/>
                    <L7p:CaseInsensitive booleanValue="true"/>
                    <L7p:OtherTargetMessageVariable stringValue="cache_control"/>
                    <L7p:Regex stringValue="max-age=([0-9]+)"/>
                    <L7p:RegexName stringValue="Get Max-age"/>
                    <L7p:Replacement stringValue=""/>
                    <L7p:Target target="OTHER"/>
                </L7p:Regex>
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="JHtjYWNoZV9tYXhfYWdlWzFdfQ=="/>
                    <L7p:VariableToSet stringValue="cache_max_age"/>
                </L7p:SetVariable>
                <L7p:CacheStorage>
                    <L7p:CacheEntryKey stringValue="${oauth_token}"/>
                    <L7p:CacheId stringValue="oauth_token_cache"/>
                    <L7p:MaxEntries stringValue="1000"/>
                    <L7p:MaxEntryAgeSeconds stringValue="${cache_max_age}"/>
                    <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
                    <L7p:Target target="OTHER"/>
                </L7p:CacheStorage>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Call Curity"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
        </wsp:OneOrMore>
        <L7p:EvaluateJsonPathExpression>
            <L7p:Expression stringValue="$.active"/>
            <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
            <L7p:Target target="OTHER"/>
            <L7p:VariablePrefix stringValue="introspect_active"/>
        </L7p:EvaluateJsonPathExpression>
        <L7p:CustomizeErrorResponse>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="LEFT.COMMENT"/>
                        <L7p:value stringValue="401"/>
                    </L7p:entry>
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="Expired or Invalid"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "invalid_request",
"error_description" : "Token invalid or expired"
}]]></L7p:Content>
            <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
            <L7p:ExtraHeaders nameValuePairArray="included"/>
            <L7p:HttpStatus stringValue="401"/>
        </L7p:CustomizeErrorResponse>
        <L7p:ComparisonAssertion>
            <L7p:CaseSensitive booleanValue="false"/>
            <L7p:Expression1 stringValue="${introspect_active.result}"/>
            <L7p:MultivaluedComparison multivaluedComparison="FAIL"/>
            <L7p:Operator operatorNull="null"/>
            <L7p:Predicates predicates="included">
                <L7p:item dataType="included">
                    <L7p:Type variableDataType="boolean"/>
                </L7p:item>
                <L7p:item binary="included">
                    <L7p:CaseSensitive booleanValue="false"/>
                    <L7p:RightValue stringValue="true"/>
                </L7p:item>
            </L7p:Predicates>
        </L7p:ComparisonAssertion>
        <L7p:CustomizeErrorResponse>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="LEFT.COMMENT"/>
                        <L7p:value stringValue="500"/>
                    </L7p:entry>
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="Invalid response from server"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "internal_error",
"error_description" : "Invalid response from OAuth server"
}]]></L7p:Content>
            <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
            <L7p:ExtraHeaders nameValuePairArray="included"/>
        </L7p:CustomizeErrorResponse>
        <L7p:EvaluateJsonPathExpression>
            <L7p:Expression stringValue="$.exp"/>
            <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
            <L7p:Target target="OTHER"/>
            <L7p:VariablePrefix stringValue="introspect_expire"/>
        </L7p:EvaluateJsonPathExpression>
        <L7p:EvaluateJsonPathExpression>
            <L7p:Expression stringValue="$.sub"/>
            <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
            <L7p:Target target="OTHER"/>
            <L7p:VariablePrefix stringValue="introspect_subject"/>
        </L7p:EvaluateJsonPathExpression>
        <L7p:EvaluateJsonPathExpression>
            <L7p:Expression stringValue="$.scope"/>
            <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
            <L7p:Target target="OTHER"/>
            <L7p:VariablePrefix stringValue="introspect_scope"/>
        </L7p:EvaluateJsonPathExpression>
        <L7p:EvaluateJsonPathExpression>
            <L7p:Expression stringValue="$.client_id"/>
            <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
            <L7p:Target target="OTHER"/>
            <L7p:VariablePrefix stringValue="introspect_clientid"/>
        </L7p:EvaluateJsonPathExpression>
        <L7p:EvaluateJsonPathExpression>
            <L7p:Expression stringValue="$.jwt"/>
            <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
            <L7p:Target target="OTHER"/>
            <L7p:VariablePrefix stringValue="introspect_jwt"/>
        </L7p:EvaluateJsonPathExpression>
        <L7p:CustomizeErrorResponse>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="LEFT.COMMENT"/>
                        <L7p:value stringValue="401"/>
                    </L7p:entry>
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="Expired or Invalid"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "invalid_request",
"error_description" : "Token invalid or expired"
}]]></L7p:Content>
            <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
            <L7p:ExtraHeaders nameValuePairArray="included"/>
            <L7p:HttpStatus stringValue="401"/>
        </L7p:CustomizeErrorResponse>
        <L7p:SetVariable>
            <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X2V4cGlyZS5yZXN1bHR9"/>
            <L7p:VariableToSet stringValue="oauth.expiration"/>
        </L7p:SetVariable>
        <L7p:ComparisonAssertion>
            <L7p:CaseSensitive booleanValue="false"/>
            <L7p:Expression1 stringValue="${oauth.expiration}"/>
            <L7p:MultivaluedComparison multivaluedComparison="FAIL"/>
            <L7p:Operator operatorNull="null"/>
            <L7p:Predicates predicates="included">
                <L7p:item dataType="included">
                    <L7p:Type variableDataType="int"/>
                </L7p:item>
                <L7p:item binary="included">
                    <L7p:Operator operator="GT"/>
                    <L7p:RightValue stringValue="${gateway.time.seconds}"/>
                </L7p:item>
            </L7p:Predicates>
        </L7p:ComparisonAssertion>
        <L7p:SetVariable>
            <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X3N1YmplY3QucmVzdWx0fQ=="/>
            <L7p:VariableToSet stringValue="oauth.subject"/>
        </L7p:SetVariable>
        <L7p:SetVariable>
            <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X3Njb3BlLnJlc3VsdH0="/>
            <L7p:VariableToSet stringValue="oauth.scope"/>
        </L7p:SetVariable>
        <L7p:SetVariable>
            <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X2NsaWVudGlkLnJlc3VsdH0="/>
            <L7p:VariableToSet stringValue="oauth.client_id"/>
        </L7p:SetVariable>
        <L7p:SetVariable>
            <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X2p3dC5yZXN1bHR9"/>
            <L7p:VariableToSet stringValue="oauth.jwt"/>
        </L7p:SetVariable>
        <L7p:ExportVariables>
            <L7p:ExportedVars stringArrayValue="included">
                <L7p:item stringValue="oauth.client_id"/>
                <L7p:item stringValue="oauth.expiration"/>
                <L7p:item stringValue="oauth.jwt"/>
                <L7p:item stringValue="oauth.scope"/>
                <L7p:item stringValue="oauth.subject"/>
            </L7p:ExportedVars>
        </L7p:ExportVariables>
    </wsp:All>
</wsp:Policy>

Appendix B: – Require OAuth Token, introspect with application/jwt

Listing 320 CA API Gateway Require OAuth Token Fragment which introspects using Accept: application/jwt
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
<?xml version="1.0" encoding="UTF-8"?>
<wsp:Policy xmlns:L7p="http://www.layer7tech.com/ws/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy">
    <wsp:All wsp:Usage="Required">
        <L7p:CommentAssertion>
            <L7p:Comment stringValue="=== Require OAuth Token ==="/>
        </L7p:CommentAssertion>
        <L7p:AuditDetailAssertion>
            <L7p:Detail stringValue="Policy Fragment: Require OAuth Token"/>
        </L7p:AuditDetailAssertion>
        <L7p:CommentAssertion>
            <L7p:Comment stringValue="1. ------ Validate Header -----"/>
        </L7p:CommentAssertion>
        <L7p:AuditDetailAssertion>
            <L7p:Detail stringValue="Policy Fragment: Require OAuth Token"/>
        </L7p:AuditDetailAssertion>
        <L7p:CustomizeErrorResponse>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="LEFT.COMMENT"/>
                        <L7p:value stringValue="401"/>
                    </L7p:entry>
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="No token in Authorization header"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "unauthorized",
"error_description" : "No access token found in authorization header"
}]]></L7p:Content>
            <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
            <L7p:ExtraHeaders nameValuePairArray="included"/>
            <L7p:HttpStatus stringValue="401"/>
        </L7p:CustomizeErrorResponse>
        <L7p:SetVariable>
            <L7p:Base64Expression stringValue="JHtyZXF1ZXN0Lmh0dHAuaGVhZGVyLmF1dGhvcml6YXRpb259"/>
            <L7p:VariableToSet stringValue="oauth_header"/>
        </L7p:SetVariable>
        <L7p:ComparisonAssertion>
            <L7p:CaseSensitive booleanValue="false"/>
            <L7p:Expression1 stringValue="${oauth_header}"/>
            <L7p:MultivaluedComparison multivaluedComparison="FAIL"/>
            <L7p:Operator operatorNull="null"/>
            <L7p:Predicates predicates="included">
                <L7p:item dataType="included">
                    <L7p:Type variableDataType="string"/>
                </L7p:item>
                <L7p:item binary="included">
                    <L7p:CaseSensitive booleanValue="false"/>
                    <L7p:Negated booleanValue="true"/>
                    <L7p:Operator operator="EMPTY"/>
                    <L7p:RightValue stringValue=""/>
                </L7p:item>
            </L7p:Predicates>
        </L7p:ComparisonAssertion>
        <L7p:Regex>
            <L7p:AutoTarget booleanValue="false"/>
            <L7p:CaptureVar stringValue="introspect_token"/>
            <L7p:OtherTargetMessageVariable stringValue="oauth_header"/>
            <L7p:Regex stringValue="Bearer\s+(.*)"/>
            <L7p:RegexName stringValue="Retrieve token"/>
            <L7p:Replacement stringValue=""/>
            <L7p:Target target="OTHER"/>
        </L7p:Regex>
        <L7p:SetVariable>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="Take first result from regex-match"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X3Rva2VuWzFdfQ=="/>
            <L7p:VariableToSet stringValue="oauth_token"/>
        </L7p:SetVariable>
        <L7p:CommentAssertion>
            <L7p:Comment stringValue="2. ------ Validate Token -----"/>
        </L7p:CommentAssertion>
        <L7p:CustomizeErrorResponse>
            <L7p:AssertionComment assertionComment="included">
                <L7p:Properties mapValue="included">
                    <L7p:entry>
                        <L7p:key stringValue="LEFT.COMMENT"/>
                        <L7p:value stringValue="502"/>
                    </L7p:entry>
                    <L7p:entry>
                        <L7p:key stringValue="RIGHT.COMMENT"/>
                        <L7p:value stringValue="Generic error"/>
                    </L7p:entry>
                </L7p:Properties>
            </L7p:AssertionComment>
            <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "bad_gateway",
"error_description" : "Bad Gateway"
}]]></L7p:Content>
            <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
            <L7p:ExtraHeaders nameValuePairArray="included"/>
            <L7p:HttpStatus stringValue="502"/>
        </L7p:CustomizeErrorResponse>
        <wsp:OneOrMore wsp:Usage="Required">
            <wsp:All wsp:Usage="Required">
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue=""/>
                    <L7p:ContentType stringValue="application/json; charset=utf-8"/>
                    <L7p:DataType variableDataType="message"/>
                    <L7p:VariableToSet stringValue="introspect_response"/>
                </L7p:SetVariable>
                <L7p:CacheLookup>
                    <L7p:CacheEntryKey stringValue="${oauth_token}"/>
                    <L7p:CacheId stringValue="oauth_token_cache"/>
                    <L7p:ContentTypeOverride stringValue=""/>
                    <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
                    <L7p:Target target="OTHER"/>
                </L7p:CacheLookup>
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="MjAw"/>
                    <L7p:DataType variableDataType="int"/>
                    <L7p:VariableToSet stringValue="introspect_response_code"/>
                </L7p:SetVariable>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Check Cache"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
            <wsp:All wsp:Usage="Required">
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="dG9rZW49JHtvYXV0aF90b2tlbn0mY2xpZW50X2lkPSR7Z2F0ZXdheS5vYXV0aF9pbnRyb3NwZWN0X2NsaWVudF9pZH0mY2xpZW50X3NlY3JldD0ke3NlY3Bhc3Mub2F1dGhfaW50cm9zcGVjdF9jbGllbnRzZWNyZXQucGxhaW50ZXh0fQ=="/>
                    <L7p:ContentType stringValue="application/x-www-form-urlencoded; charset=utf-8"/>
                    <L7p:DataType variableDataType="message"/>
                    <L7p:VariableToSet stringValue="introspect_request"/>
                </L7p:SetVariable>
                <L7p:HttpRoutingAssertion>
                    <L7p:FailOnErrorStatus booleanValue="false"/>
                    <L7p:HttpMethod httpMethod="POST"/>
                    <L7p:ProtectedServiceUrl stringValue="${gateway.oauth_server_host}${gateway.oauth_server_introspection_path}"/>
                    <L7p:ProxyPassword stringValueNull="null"/>
                    <L7p:ProxyUsername stringValueNull="null"/>
                    <L7p:RequestHeaderRules httpPassthroughRuleSet="included">
                        <L7p:Rules httpPassthroughRules="included">
                            <L7p:item httpPassthroughRule="included">
                                <L7p:CustomizeValue stringValue="application/jwt"/>
                                <L7p:Name stringValue="accept"/>
                                <L7p:UsesCustomizedValue booleanValue="true"/>
                            </L7p:item>
                        </L7p:Rules>
                    </L7p:RequestHeaderRules>
                    <L7p:RequestMsgSrc stringValue="introspect_request"/>
                    <L7p:RequestParamRules httpPassthroughRuleSet="included">
                        <L7p:ForwardAll booleanValue="true"/>
                        <L7p:Rules httpPassthroughRules="included"/>
                    </L7p:RequestParamRules>
                    <L7p:ResponseHeaderRules httpPassthroughRuleSet="included">
                        <L7p:ForwardAll booleanValue="true"/>
                        <L7p:Rules httpPassthroughRules="included"/>
                    </L7p:ResponseHeaderRules>
                    <L7p:ResponseMsgDest stringValue="introspect_response"/>
                    <L7p:SamlAssertionVersion intValue="2"/>
                </L7p:HttpRoutingAssertion>
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="JHtodHRwUm91dGluZy5yZWFzb25Db2RlfQ=="/>
                    <L7p:DataType variableDataType="int"/>
                    <L7p:VariableToSet stringValue="introspect_response_code"/>
                </L7p:SetVariable>
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X3Jlc3BvbnNlLmh0dHAuaGVhZGVyLmNhY2hlLWNvbnRyb2x9"/>
                    <L7p:VariableToSet stringValue="cache_control"/>
                </L7p:SetVariable>
                <wsp:OneOrMore wsp:Usage="Required">
                    <wsp:All wsp:Usage="Required">
                        <L7p:Regex>
                            <L7p:AutoTarget booleanValue="false"/>
                            <L7p:CaptureVar stringValue="cache_max_age"/>
                            <L7p:CaseInsensitive booleanValue="true"/>
                            <L7p:OtherTargetMessageVariable stringValue="cache_control"/>
                            <L7p:Regex stringValue="max-age=([0-9]+)"/>
                            <L7p:RegexName stringValue="Get Max-age"/>
                            <L7p:Replacement stringValue=""/>
                            <L7p:Target target="OTHER"/>
                        </L7p:Regex>
                        <L7p:SetVariable>
                            <L7p:Base64Expression stringValue="JHtjYWNoZV9tYXhfYWdlWzFdfQ=="/>
                            <L7p:VariableToSet stringValue="cache_max_age"/>
                        </L7p:SetVariable>
                        <L7p:CacheStorage>
                            <L7p:CacheEntryKey stringValue="${oauth_token}"/>
                            <L7p:CacheId stringValue="oauth_token_cache"/>
                            <L7p:MaxEntries stringValue="1000"/>
                            <L7p:MaxEntryAgeSeconds stringValue="${cache_max_age}"/>
                            <L7p:OtherTargetMessageVariable stringValue="introspect_response"/>
                            <L7p:Target target="OTHER"/>
                        </L7p:CacheStorage>
                    </wsp:All>
                    <wsp:OneOrMore wsp:Usage="Required">
                        <L7p:ComparisonAssertion>
                            <L7p:CaseSensitive booleanValue="false"/>
                            <L7p:Expression1 stringValue="${cache_control}"/>
                            <L7p:Operator operatorNull="null"/>
                            <L7p:Predicates predicates="included">
                                <L7p:item dataType="included">
                                <L7p:Type variableDataType="string"/>
                                </L7p:item>
                                <L7p:item binary="included">
                                <L7p:Operator operator="EMPTY"/>
                                <L7p:RightValue stringValue=""/>
                                </L7p:item>
                            </L7p:Predicates>
                        </L7p:ComparisonAssertion>
                    </wsp:OneOrMore>
                    <L7p:assertionComment>
                        <L7p:Properties mapValue="included">
                            <L7p:entry>
                                <L7p:key stringValue="RIGHT.COMMENT"/>
                                <L7p:value stringValue="Only cache the response when cache_control header has max-age"/>
                            </L7p:entry>
                        </L7p:Properties>
                    </L7p:assertionComment>
                </wsp:OneOrMore>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Call Curity"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
        </wsp:OneOrMore>
        <L7p:CommentAssertion>
            <L7p:Comment stringValue="3. ------ Check Curity's response status code ------"/>
        </L7p:CommentAssertion>
        <wsp:OneOrMore wsp:Usage="Required">
            <wsp:All wsp:Usage="Required">
                <L7p:ComparisonAssertion>
                    <L7p:CaseSensitive booleanValue="false"/>
                    <L7p:Expression1 stringValue="${introspect_response_code}"/>
                    <L7p:MultivaluedComparison multivaluedComparison="FIRST"/>
                    <L7p:Operator operatorNull="null"/>
                    <L7p:Predicates predicates="included">
                        <L7p:item dataType="included">
                            <L7p:Type variableDataType="int"/>
                        </L7p:item>
                        <L7p:item binary="included">
                            <L7p:RightValue stringValue="200"/>
                        </L7p:item>
                    </L7p:Predicates>
                </L7p:ComparisonAssertion>
                <L7p:SetVariable>
                    <L7p:Base64Expression stringValue="JHtpbnRyb3NwZWN0X3Jlc3BvbnNlLm1haW5wYXJ0fQ=="/>
                    <L7p:VariableToSet stringValue="oauth.jwt"/>
                </L7p:SetVariable>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Curity responds 200"/>
                        </L7p:entry>
                        <L7p:entry>
                            <L7p:key stringValue="RIGHT.COMMENT"/>
                            <L7p:value stringValue="jwt in introspect_response.mainpart"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
            <wsp:All wsp:Usage="Required">
                <L7p:ComparisonAssertion>
                    <L7p:CaseSensitive booleanValue="false"/>
                    <L7p:Expression1 stringValue="${introspect_response_code}"/>
                    <L7p:MultivaluedComparison multivaluedComparison="FIRST"/>
                    <L7p:Operator operatorNull="null"/>
                    <L7p:Predicates predicates="included">
                        <L7p:item dataType="included">
                            <L7p:Type variableDataType="int"/>
                        </L7p:item>
                        <L7p:item binary="included">
                            <L7p:RightValue stringValue="204"/>
                        </L7p:item>
                    </L7p:Predicates>
                </L7p:ComparisonAssertion>
                <L7p:CustomizeErrorResponse>
                    <L7p:AssertionComment assertionComment="included">
                        <L7p:Properties mapValue="included">
                            <L7p:entry>
                                <L7p:key stringValue="LEFT.COMMENT"/>
                                <L7p:value stringValue="400"/>
                            </L7p:entry>
                            <L7p:entry>
                                <L7p:key stringValue="RIGHT.COMMENT"/>
                                <L7p:value stringValue="No token in Authorization header"/>
                            </L7p:entry>
                        </L7p:Properties>
                    </L7p:AssertionComment>
                    <L7p:Content stringValueReference="inline"><![CDATA[{
"error" : "invalid_request",
"error_description" : "Token invalid or expired"
}]]></L7p:Content>
                    <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
                    <L7p:ExtraHeaders nameValuePairArray="included">
                        <L7p:item nameValuePair="included">
                            <L7p:Key stringValue="WWW-Authenticate"/>
                            <L7p:Value stringValue="Bearer error=&quot;invalid_token&quot;"/>
                        </L7p:item>
                    </L7p:ExtraHeaders>
                    <L7p:HttpStatus stringValue="401"/>
                </L7p:CustomizeErrorResponse>
                <L7p:RaiseError/>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Curity responds 204"/>
                        </L7p:entry>
                        <L7p:entry>
                            <L7p:key stringValue="RIGHT.COMMENT"/>
                            <L7p:value stringValue="introspected invalid or expired access_token"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
            <wsp:All wsp:Usage="Required">
                <L7p:ComparisonAssertion>
                    <L7p:CaseSensitive booleanValue="false"/>
                    <L7p:Expression1 stringValue="${introspect_response_code}"/>
                    <L7p:MultivaluedComparison multivaluedComparison="FIRST"/>
                    <L7p:Operator operatorNull="null"/>
                    <L7p:Predicates predicates="included">
                        <L7p:item dataType="included">
                            <L7p:Type variableDataType="int"/>
                        </L7p:item>
                        <L7p:item binary="included">
                            <L7p:RightValue stringValue="503"/>
                        </L7p:item>
                    </L7p:Predicates>
                </L7p:ComparisonAssertion>
                <L7p:CustomizeErrorResponse>
                    <L7p:AssertionComment assertionComment="included">
                        <L7p:Properties mapValue="included">
                            <L7p:entry>
                                <L7p:key stringValue="LEFT.COMMENT"/>
                                <L7p:value stringValue="400"/>
                            </L7p:entry>
                            <L7p:entry>
                                <L7p:key stringValue="RIGHT.COMMENT"/>
                                <L7p:value stringValue="No token in Authorization header"/>
                            </L7p:entry>
                        </L7p:Properties>
                    </L7p:AssertionComment>
                    <L7p:Content stringValueReference="inline"><![CDATA[{
"error": "service_unavailable",
"error_description": "Service Unavailable"
}]]></L7p:Content>
                    <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
                    <L7p:ExtraHeaders nameValuePairArray="included"/>
                    <L7p:HttpStatus stringValue="503"/>
                </L7p:CustomizeErrorResponse>
                <L7p:RaiseError/>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Curity responds 503"/>
                        </L7p:entry>
                        <L7p:entry>
                            <L7p:key stringValue="RIGHT.COMMENT"/>
                            <L7p:value stringValue="Service Unavailable"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
            <wsp:All wsp:Usage="Required">
                <L7p:ComparisonAssertion>
                    <L7p:CaseSensitive booleanValue="false"/>
                    <L7p:Expression1 stringValue="${introspect_response_code}"/>
                    <L7p:MultivaluedComparison multivaluedComparison="ANY"/>
                    <L7p:Operator operatorNull="null"/>
                    <L7p:Predicates predicates="included">
                        <L7p:item dataType="included">
                            <L7p:Type variableDataType="int"/>
                        </L7p:item>
                        <L7p:item regex="included">
                            <L7p:Pattern stringValue="^401|403|404|5[0][0-2]|50[4-9]|5[1-9][0-9]$"/>
                        </L7p:item>
                    </L7p:Predicates>
                </L7p:ComparisonAssertion>
                <L7p:CustomizeErrorResponse>
                    <L7p:AssertionComment assertionComment="included">
                        <L7p:Properties mapValue="included">
                            <L7p:entry>
                                <L7p:key stringValue="LEFT.COMMENT"/>
                                <L7p:value stringValue="400"/>
                            </L7p:entry>
                            <L7p:entry>
                                <L7p:key stringValue="RIGHT.COMMENT"/>
                                <L7p:value stringValue="No token in Authorization header"/>
                            </L7p:entry>
                        </L7p:Properties>
                    </L7p:AssertionComment>
                    <L7p:Content stringValueReference="inline"><![CDATA[{
"error": "bad_gateway",
"error_description": "Bad Gateway"
}]]></L7p:Content>
                    <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
                    <L7p:ExtraHeaders nameValuePairArray="included"/>
                    <L7p:HttpStatus stringValue="502"/>
                </L7p:CustomizeErrorResponse>
                <L7p:RaiseError/>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Curity responds 401,403,404,500-502,504-599"/>
                        </L7p:entry>
                        <L7p:entry>
                            <L7p:key stringValue="RIGHT.COMMENT"/>
                            <L7p:value stringValue="Gateway responds with 502"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
            <wsp:All wsp:Usage="Required">
                <L7p:CustomizeErrorResponse>
                    <L7p:AssertionComment assertionComment="included">
                        <L7p:Properties mapValue="included">
                            <L7p:entry>
                                <L7p:key stringValue="LEFT.COMMENT"/>
                                <L7p:value stringValue="400"/>
                            </L7p:entry>
                            <L7p:entry>
                                <L7p:key stringValue="RIGHT.COMMENT"/>
                                <L7p:value stringValue="No token in Authorization header"/>
                            </L7p:entry>
                        </L7p:Properties>
                    </L7p:AssertionComment>
                    <L7p:Content stringValueReference="inline"><![CDATA[{
"error": "internal_server_error",
"error_description": "Internal Server Error"
}]]></L7p:Content>
                    <L7p:ContentType stringValue="application/json; charset=UTF-8"/>
                    <L7p:ExtraHeaders nameValuePairArray="included"/>
                </L7p:CustomizeErrorResponse>
                <L7p:RaiseError/>
                <L7p:assertionComment>
                    <L7p:Properties mapValue="included">
                        <L7p:entry>
                            <L7p:key stringValue="LEFT.COMMENT"/>
                            <L7p:value stringValue="Any other status code"/>
                        </L7p:entry>
                        <L7p:entry>
                            <L7p:key stringValue="RIGHT.COMMENT"/>
                            <L7p:value stringValue="Gateway responds with 500"/>
                        </L7p:entry>
                    </L7p:Properties>
                </L7p:assertionComment>
            </wsp:All>
        </wsp:OneOrMore>
        <L7p:ExportVariables>
            <L7p:ExportedVars stringArrayValue="included">
                <L7p:item stringValue="oauth.jwt"/>
            </L7p:ExportedVars>
        </L7p:ExportVariables>
    </wsp:All>
</wsp:Policy>