Apigee - JS policy variable set doesnt persist through flow runs.

I have an encrypted KVM set for my environment and in a JavaScript policy I created I can do context.setVariable('private.token', accessToken); and get as well the problem is that the values are not persisted through flow runs.

ps. the code is a bit abstracted for simplification of the post.

 

// Get current time in seconds
var currentTime = Date.now();
var normalizedCurrentTime = Math.floor(currentTime / 1000);

// Get token expiry from context
var tokenExpiryString = context.getVariable('private.token-expiry');
var tokenExpiry = parseFloat(tokenExpiryString);

// Get existing token from context
var existingToken = context.getVariable('private.token');

// Set variables for debugging/logging
context.setVariable('token', existingToken);

// Check if token is expired
if (normalizedCurrentTime > tokenExpiry) {
    // Call token retrieval function
    retrieveToken();
}

// Function to retrieve and update token
function retrieveToken() {
    // Get client credentials from context

    // Construct token request

    // Send token request asynchronously
    httpClient.send(reqToken, onCompleteToken);
}

// Callback function for token request
function onCompleteToken(response, error) {
    if (response) {
        var responseContent = response.content;
        if (responseContent.trim() !== '') {
            // Parse token response
            var tokenResponse = JSON.parse(responseContent);
            var accessToken = tokenResponse.access_token;
            var expiresIn = tokenResponse.expires_in;

            // Update token and expiry in context
            context.setVariable('private.token', accessToken);
            context.setVariable('token', accessToken);
            context.setVariable('private.token-expiry', normalizedCurrentTime + expiresIn);
            context.setVariable('tokenExpiry', normalizedCurrentTime + expiresIn);
            context.setVariable("debug_token_response", responseContent);
        } else {
            // Handle empty response content
            context.setVariable("debug_http_error_token", "Empty response content received");
        }
    } else {
        // Handle HTTP error
        context.setVariable("debug_http_error_token", "HTTP Status: " + error.statusCode + ", Content: " + error.message);
    }
}

 

 

 

 

Solved Solved
0 5 115
1 ACCEPTED SOLUTION

If I understand correctly, what you are describing is something we call "security mediation" - basically a call into Apigee presents something to authenticate (an API Key or a token). Apigee validates that something, then calls a service on the upstream/backend that uses a different authentication mechanism. Like a second OAuth token, different realm. 

ServiceCallout + ExtractVariables is a good approach for interacting with an external HTTP endpoint, for example something that dispenses tokens. But some people prefer scripting it all with JavaScript. Whatever is more maintainable for you, is probably better.

But that addresses the problem of getting the token.  The next issue is how to manage the lifecycle of that token; how to "save it" and "get it" when a new request comes in. 

There are multiple ways to "attach" or reference the 2nd token when a request arrives at Apigee. Here's a good approach, a well-worn pattern that many systems use:

  • on inbound call, validate inbound credential (the api key or apigee-issued token)
  • check cache (LookupCache) for the 2nd token; if it exists, it is now available in a context variable.
  • if no token, do the servicecallout, extract the token via ExtractVariables, and insert the token into cache via PopulateCache, specifying the lifetime of the cache entry to coincide with the lifetime of the token (with a few seconds for margin of safety). If you want to set the expiry of the cache entry to some value that is sent in the token response, you will need to do some arithmetic here in JavaScript. Alternatively, if you know the upstream token always expires in 30 minutes, then you can hard-code the expiry to be "slightly less than 30 minutes" and no arithmetic is necessary.
  • Use the token in the upstream call.

This approach is nice because the cache key can be anything you like. You can cache a different token for each distinct client, using the client_id as part of the cache key.  Or, if you prefer you can use a single token across all clients, if you use a cache key that is "fixed" or otherwise not dependent on client id. 

Storing a token in the KVM is ok, but maybe unnecessary, and not quite harmonious. What I mean by the latter; Tokens expire, but KVM entries don't. So if you stuff a token into KVM, then later retrieve it, your proxy will have to have some way of determining validity, before using it.  Or maybe try to use it and then... if it's expired, explicitly take a step to remove the thing from the KVM.  Anyway it feels like a bunch of extra work on your part to maintain the token. Whereas if you don't use a KVM and instead use the Apigee cache, the cached token disappears just before it expires, and your proxy just needs to get a new one, via the logic I described above. 

That's the way I think about it.

View solution in original post

5 REPLIES 5

What do you mean by "through flow runs" ?

When you use context.setVariable() in a JS step, it sets a variable in the message context. The message context is state that Apigee maintains for the duration of the current HTTP Request. If you have a second HTTP request, it gets a unique message context. A variable that you set in another message context, is available only in that other message context.

I have an encrypted KVM set for my environment and

I'm not sure what the KVM has to do with the question you're asking.

Basically I went through Admin->Environments->KVM and created a KVM there (encrypted) with some starting values. And in my proxy a JS policy in which im using this values from my KVM (set for the environment) I can get the inicial values I set beforehand but I cant seem to update them with the context.setVariable(), so my question is how can I update my KVM values throught the JS policy, in this case Im storing an auth token which I dont want to update everytime my request goes through a proxy flow, but instead only when it expires. Thanks for your time.

You can neither retrieve nor update KVM values directly using a JS policy.

The JS policy is able to retrieve (context.getVariable()) or update (context.setVariable()) variables in the message context. But that does not affect the KVM. The message context is ephemeral state that lasts "as long as the request", which can be... yknow... 4ms-150ms or so, depending on the circumstances. The message context is kept in dynamic memory only and it evaporates when the response is returned to the client.

There is a step type that allows you to retrieve or update values in the KVM - KeyValueMapOperations. The Get operation in that step type allows you to read from persistent store, into a context variable. If you had a sequence of KeyValueMapOperations/Get, then a JS policy, your JS policy could use context.getVariable() to read the thing that had been retrieved from the KVM.

There is no way for a JS policy to write to a KVM, directly. You could have a sequence of JS policy with context.setVariable, then KeyValueMapOperations/Put, referring to the same variable, and that would allow you to write to the KVM, a value that had been previously determined or calculated by your JavaScript logic.

in this case Im storing an auth token which I dont want to update everytime my request goes through a proxy flow, but instead only when it expires.

If you explained a little more about the auth token, where it comes from, its lifecycle, which actor holds it, how it gets used..., I might be able to advise a little better. 

Great! Thanks for the reply somehow I found it hard to perceive that from the documentation. Yes, we can delve a bit more into its lifecycle, my objective is that every time my proxy gets called I have this javascript policy to call an external api, this api has client credentials endpoint to call for getting this oauth token which then is saved and used to call the external api endpoint. I kinda managed to do it before with service call outs and extract variables but to connect the steps and to pass the payloads there wasnt many examples or documentation to support this, hence the JS policy. And if this proves to make it too slow I'll do it on the target endpoint itself.

If I understand correctly, what you are describing is something we call "security mediation" - basically a call into Apigee presents something to authenticate (an API Key or a token). Apigee validates that something, then calls a service on the upstream/backend that uses a different authentication mechanism. Like a second OAuth token, different realm. 

ServiceCallout + ExtractVariables is a good approach for interacting with an external HTTP endpoint, for example something that dispenses tokens. But some people prefer scripting it all with JavaScript. Whatever is more maintainable for you, is probably better.

But that addresses the problem of getting the token.  The next issue is how to manage the lifecycle of that token; how to "save it" and "get it" when a new request comes in. 

There are multiple ways to "attach" or reference the 2nd token when a request arrives at Apigee. Here's a good approach, a well-worn pattern that many systems use:

  • on inbound call, validate inbound credential (the api key or apigee-issued token)
  • check cache (LookupCache) for the 2nd token; if it exists, it is now available in a context variable.
  • if no token, do the servicecallout, extract the token via ExtractVariables, and insert the token into cache via PopulateCache, specifying the lifetime of the cache entry to coincide with the lifetime of the token (with a few seconds for margin of safety). If you want to set the expiry of the cache entry to some value that is sent in the token response, you will need to do some arithmetic here in JavaScript. Alternatively, if you know the upstream token always expires in 30 minutes, then you can hard-code the expiry to be "slightly less than 30 minutes" and no arithmetic is necessary.
  • Use the token in the upstream call.

This approach is nice because the cache key can be anything you like. You can cache a different token for each distinct client, using the client_id as part of the cache key.  Or, if you prefer you can use a single token across all clients, if you use a cache key that is "fixed" or otherwise not dependent on client id. 

Storing a token in the KVM is ok, but maybe unnecessary, and not quite harmonious. What I mean by the latter; Tokens expire, but KVM entries don't. So if you stuff a token into KVM, then later retrieve it, your proxy will have to have some way of determining validity, before using it.  Or maybe try to use it and then... if it's expired, explicitly take a step to remove the thing from the KVM.  Anyway it feels like a bunch of extra work on your part to maintain the token. Whereas if you don't use a KVM and instead use the Apigee cache, the cached token disappears just before it expires, and your proxy just needs to get a new one, via the logic I described above. 

That's the way I think about it.