External api is being called from the javascript and the response is passed to the custom attribute in oauth policy.

Not applicable

oauth-rev2-2017-12-13.zipHi @Dino,

I'm trying to do a POC of the 1st part of the flow (discussed here https://community.apigee.com/questions/49592/oauth-custom-flow.html by @manojramakrishnan), where an external api is being called from the javascript code and the response is passed in the custom attribute in oauth policy.

Few things I'm unsure of is -

1)What should be the ref here for the attribute section?

<Attributes>

<Attribute name="tenant_list" ref="" display="true"/>

</Attributes>

2)In the Proxy flows I added a step before GenerateAccessTokenClient-CC and RefreshAccessToken to run the JS policy 1st -

<Step>

<Name>getTenantsSubs</Name>

</Step>

Is this right approach?

3)The actual JS code that does an external api call. Currently I'm seeing the custom attribute in the response but it's empty.

I'm attaching the proxy here. Can you please suggest fixes to this?

Solved Solved
0 6 469
1 ACCEPTED SOLUTION

I'm seeing the custom attribute in the response but it's empty. I'm attaching the proxy here. Can you please suggest fixes to this?

Hmm yes.

Your GenerateAccessToken policy looks like this:

 <Attributes>
    <Attribute name="tenant_list" ref="context.proxyResponse.content" display="true"/>
 </Attributes>

In English, that means you are inserting an attribute called "tenant_list" to the OAuth token, and it will take as its value, whatever value is stored in the context variable "context.proxyResponse.content".

I am sure that the context variable called "context.proxyResponse.content" is empty. This is why your Attribute appears as empty.

Next question: why is that variable empty?

Easy answer: it hasn't been set to anything.

From examining your JS code, it appears that you are trying to set it there. The JS code is like this:

try {
  var testApiCall = httpClient.get(
        'https://maps.googleapis.com/maps/api/geocode/json?.....');
  testApiCall.waitForComplete();
  if (!testApiCall.isSuccess()) {
    throw 'Error test Api Call';
  }  
  testResponse = testApiCall.getResponse().content.asJSON;
  if (testResponse.status != 'OK') {
    throw 'Error returned from geocoding web service: ' + geocodeResponse.status;
  }
  context.proxyResponse.content = JSON.stringify(testResponse);
}
catch (err) {
}

ok, there are couple problems I see here.

Issue A: the line like this:

context.proxyResponse.content = JSON.stringify(testResponse);

... seems wrong. First, it seems to be aiming to set a variable within the scope of the JavaScript execution. The name of the variable is "context.proxyResponse.content". Key point #1: variables that are valid within the scope of JavaScript execution are independent of "context variables" known within the scope of API Proxy flow. There are two distinct "worlds".

an example. Suppose I have a simple JS like this:

var index = 3;

Within the scope of the JavaScript, after the line executes, the variable "index" will hold the value 3.

Now suppose you embed that JS into a JavaScript policy that runs within an api proxy flow in Apigee Edge. After that JS step executes, there is no "context variable" in the Apigee Edge apiproxy that has the name "index". Do you see? They are separate scopes. Both the API proxy flow and the JS execution have things called "variables" but they are distinct scopes.

Clear?

ok, let me confuse you a little. Key point #2: There are SOME exceptions to key point #1. You might think of them as crossover variables. For example:

JavaScript scope Proxy flow scope
context.proxyResponse.content="42"; response.content
context.proxyRequest.content request.content

And I guess there are other examples. The point here is that if you assign to "context.proxyResponse.content" within JS, then in the API Proxy flow (and in particular within the OAuthV2 policy ) you would reference a context variable called "response.content" to read that.

But you should NOT assign to "context.proxyResponse.content", from within a JS. That variable holds the target response content and unless you are trying to purposefully overwrite that, you shouldn't use that variable name.

If you want to export a JS variable to a NEW context variable that will be known in the API proxy flow, you should do so explicitly, like this:

context.setVariable('api-proxy-flow-variable', variable_known_in_js_scope); 

Issue B: the line like this:

 testResponse = testApiCall.getResponse().content.asJSON;

...is wrong. You need to declare that testResponse variable. You need a 'var'. This is just basic JavaScript syntax. Also, since in the next line, you try to read testResponse.status, the correct line should be:

 var testResponse = testApiCall.getResponse();

And then... the next section should be:

  if (testResponse.status != 'OK') {
    throw 'Error returned from geocoding web service: ' + testResponse.status;
  }

Issue C: back to the assignment line:

context.proxyResponse.content = JSON.stringify(testResponse);

Now that testResponse is different, we need to change that line. Also from Issue A we know you need to use context.setVariable() to export a variable to the api proxy flow. You want something like this:

context.setVariable('geocode_response', JSON.stringify(testResponse.content));

OK, once you have resolved all of those issues with JS, you can update your OAuth policy to look like this:

 <Attributes>
    <Attribute name="my_attribute" ref="geocode_response" display="true"/>
 </Attributes>

Do you see?

View solution in original post

6 REPLIES 6

This is a follow up question to the thread here : https://community.apigee.com/questions/49592/oauth-custom-flow.html

I'm seeing the custom attribute in the response but it's empty. I'm attaching the proxy here. Can you please suggest fixes to this?

Hmm yes.

Your GenerateAccessToken policy looks like this:

 <Attributes>
    <Attribute name="tenant_list" ref="context.proxyResponse.content" display="true"/>
 </Attributes>

In English, that means you are inserting an attribute called "tenant_list" to the OAuth token, and it will take as its value, whatever value is stored in the context variable "context.proxyResponse.content".

I am sure that the context variable called "context.proxyResponse.content" is empty. This is why your Attribute appears as empty.

Next question: why is that variable empty?

Easy answer: it hasn't been set to anything.

From examining your JS code, it appears that you are trying to set it there. The JS code is like this:

try {
  var testApiCall = httpClient.get(
        'https://maps.googleapis.com/maps/api/geocode/json?.....');
  testApiCall.waitForComplete();
  if (!testApiCall.isSuccess()) {
    throw 'Error test Api Call';
  }  
  testResponse = testApiCall.getResponse().content.asJSON;
  if (testResponse.status != 'OK') {
    throw 'Error returned from geocoding web service: ' + geocodeResponse.status;
  }
  context.proxyResponse.content = JSON.stringify(testResponse);
}
catch (err) {
}

ok, there are couple problems I see here.

Issue A: the line like this:

context.proxyResponse.content = JSON.stringify(testResponse);

... seems wrong. First, it seems to be aiming to set a variable within the scope of the JavaScript execution. The name of the variable is "context.proxyResponse.content". Key point #1: variables that are valid within the scope of JavaScript execution are independent of "context variables" known within the scope of API Proxy flow. There are two distinct "worlds".

an example. Suppose I have a simple JS like this:

var index = 3;

Within the scope of the JavaScript, after the line executes, the variable "index" will hold the value 3.

Now suppose you embed that JS into a JavaScript policy that runs within an api proxy flow in Apigee Edge. After that JS step executes, there is no "context variable" in the Apigee Edge apiproxy that has the name "index". Do you see? They are separate scopes. Both the API proxy flow and the JS execution have things called "variables" but they are distinct scopes.

Clear?

ok, let me confuse you a little. Key point #2: There are SOME exceptions to key point #1. You might think of them as crossover variables. For example:

JavaScript scope Proxy flow scope
context.proxyResponse.content="42"; response.content
context.proxyRequest.content request.content

And I guess there are other examples. The point here is that if you assign to "context.proxyResponse.content" within JS, then in the API Proxy flow (and in particular within the OAuthV2 policy ) you would reference a context variable called "response.content" to read that.

But you should NOT assign to "context.proxyResponse.content", from within a JS. That variable holds the target response content and unless you are trying to purposefully overwrite that, you shouldn't use that variable name.

If you want to export a JS variable to a NEW context variable that will be known in the API proxy flow, you should do so explicitly, like this:

context.setVariable('api-proxy-flow-variable', variable_known_in_js_scope); 

Issue B: the line like this:

 testResponse = testApiCall.getResponse().content.asJSON;

...is wrong. You need to declare that testResponse variable. You need a 'var'. This is just basic JavaScript syntax. Also, since in the next line, you try to read testResponse.status, the correct line should be:

 var testResponse = testApiCall.getResponse();

And then... the next section should be:

  if (testResponse.status != 'OK') {
    throw 'Error returned from geocoding web service: ' + testResponse.status;
  }

Issue C: back to the assignment line:

context.proxyResponse.content = JSON.stringify(testResponse);

Now that testResponse is different, we need to change that line. Also from Issue A we know you need to use context.setVariable() to export a variable to the api proxy flow. You want something like this:

context.setVariable('geocode_response', JSON.stringify(testResponse.content));

OK, once you have resolved all of those issues with JS, you can update your OAuth policy to look like this:

 <Attributes>
    <Attribute name="my_attribute" ref="geocode_response" display="true"/>
 </Attributes>

Do you see?

Thanks a lot for your detailed answer with explanation. That's very helpful.

I followed your suggestions and still getting the custom attribute empty after calling /oauth/client_credential/accesstoken?grant_type=client_credentials

I'm attaching the updated proxy here.

oauth-rev2-2017-12-14.zip

can you try this JS?

try {
  var testApiCall = httpClient.get(
        'https://maps.googleapis.com/maps/api/geocode/json?address=...&key=AI...A');
  testApiCall.waitForComplete();
  if (!testApiCall.isSuccess()) {
    throw 'Error test Api Call';
  }
  // We got a response. Parse the JSON into a JavaScript object.
  var testResponse = testApiCall.getResponse();
  if (testResponse.status != 200) {
    throw 'Error returned from geocoding web service: ' + testResponse.status;
  }

  context.setVariable('testApi_response', testResponse.content);

}
catch (e) {
  context.setVariable('testApi_error', JSON.stringify(e));
}

This works for me.

Thanks very much! It worked 🙂

Glad to hear it!