How to decode base64 encoded string in APIGEE (atob function is not working in javascript)

Hi All,

I am trying to decode base64 encoded string in apigee through below javascript in apigee, but it is giving me an error "Error: "atob" is not defined."

var jsonstr = context.getVariable("JWTToken");
var str = decoder(jsonstr);
context.setVariable("context.targetResponse",str);


function decoder(base64url) {
  try {
    //Convert base 64 url to base 64
    var base64 = base64url.replace('-', '+').replace('_', '/'); //atob() is a built in JS function that decodes a base-64 encoded string
    var utf8 = atob(base64); //Then parse that into JSON
    var json = JSON.parse(utf8); //Then make that JSON look pretty
    var json_string = JSON.stringify(json, null, 4);
  }
  catch (err) {
    json_string = "Bad Section.\nError: " + err.message;
  }
  print(json_string);
  return json_string;
}
Solved Solved
0 7 7,238
1 ACCEPTED SOLUTION

do you have Apigee Edge SaaS? You can do what you want with the DecodeJWT policy. It's built-in.

If you do not have Apigee Edge SaaS, then you need something like this:

// jwtDecode.js

function base64Decode(input) {
  // Takes a base 64 encoded string "input", strips any "=" or
  // "==" padding off it and converts its base 64 numerals into
  // regular integers (using a string as a lookup table). These
  // are then written out as 6-bit binary numbers and concatenated
  // together. The result is split into 8-bit sequences and these
  // are converted to string characters, which are concatenated
  // and output.


  // The index/character relationship in the following string acts
  // as a lookup table to convert from base 64 numerals to
  // Javascript integers
  var swaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
      ob = "",
      output = "",
      tb = "",
      i, L;


  input = input.replace("=",""); // strip padding


  for (i=0, L = input.length; i < L; i++) {
    tb = swaps.indexOf(input.charAt(i)).toString(2);
    while (tb.length < 6) {
      // Add significant zeroes
      tb = "0"+tb;
    }
    while (tb.length > 6) {
      // Remove significant bits
      tb = tb.substring(1);
    }
    ob += tb;
    while (ob.length >= 8) {
      output += String.fromCharCode(parseInt(ob.substring(0,8),2));
      ob = ob.substring(8);
    }
  }
  return output;
}


function decodeJwt(input){
  var parts = input.split('.'),
      header, payload;
  if (parts.length !== 3) {
    return null; // not a valid JWT
  }
  header = base64Decode(parts[0]);
  header = header.replace(/\0/g, '');
  //header = JSON.parse(header);

  payload = base64Decode(parts[1]);
  payload = payload.replace(/\0/g, '');
  //payload = JSON.parse(payload);

  return {
    header: header,
    payload : payload,
    sig : parts[2]
  };
}

var jsonstr = context.getVariable("JWTToken");
var result = decodeJwt(jsonstr);
context.setVariable("context.targetResponse",result.payload);

As for why the atob() function is not available - the JavaScript callout does not have the full node runtime, nor does it have the browser runtime. It's a stripped down Rhino runtime. atob() is not included in that.

View solution in original post

7 REPLIES 7

do you have Apigee Edge SaaS? You can do what you want with the DecodeJWT policy. It's built-in.

If you do not have Apigee Edge SaaS, then you need something like this:

// jwtDecode.js

function base64Decode(input) {
  // Takes a base 64 encoded string "input", strips any "=" or
  // "==" padding off it and converts its base 64 numerals into
  // regular integers (using a string as a lookup table). These
  // are then written out as 6-bit binary numbers and concatenated
  // together. The result is split into 8-bit sequences and these
  // are converted to string characters, which are concatenated
  // and output.


  // The index/character relationship in the following string acts
  // as a lookup table to convert from base 64 numerals to
  // Javascript integers
  var swaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
      ob = "",
      output = "",
      tb = "",
      i, L;


  input = input.replace("=",""); // strip padding


  for (i=0, L = input.length; i < L; i++) {
    tb = swaps.indexOf(input.charAt(i)).toString(2);
    while (tb.length < 6) {
      // Add significant zeroes
      tb = "0"+tb;
    }
    while (tb.length > 6) {
      // Remove significant bits
      tb = tb.substring(1);
    }
    ob += tb;
    while (ob.length >= 8) {
      output += String.fromCharCode(parseInt(ob.substring(0,8),2));
      ob = ob.substring(8);
    }
  }
  return output;
}


function decodeJwt(input){
  var parts = input.split('.'),
      header, payload;
  if (parts.length !== 3) {
    return null; // not a valid JWT
  }
  header = base64Decode(parts[0]);
  header = header.replace(/\0/g, '');
  //header = JSON.parse(header);

  payload = base64Decode(parts[1]);
  payload = payload.replace(/\0/g, '');
  //payload = JSON.parse(payload);

  return {
    header: header,
    payload : payload,
    sig : parts[2]
  };
}

var jsonstr = context.getVariable("JWTToken");
var result = decodeJwt(jsonstr);
context.setVariable("context.targetResponse",result.payload);

As for why the atob() function is not available - the JavaScript callout does not have the full node runtime, nor does it have the browser runtime. It's a stripped down Rhino runtime. atob() is not included in that.

Hi @Dino,

While running above javascript code, it is giving following error:

{"fault":{"faultstring":"Execution of JavaScript-1 failed with error: Javascript runtime error: \"TypeError: Cannot call method \"split\" of null. (JavaScript-1.js:45)\"","detail":{"errorcode":"steps.javascript.ScriptExecutionFailed"}}}

Yes - this error is telling you that you haven't set the context variable called JWTToken.

That code is a sample. You may need to modify it slightly for your environment.

Do you have the token in a context variable? What is the name of the variable?

Use that name in place of JWTToken in the line that reads:

var jsonstr = context.getVariable("JWTToken");

@Dino

After setting the context variable, code worked.

Thanks for all your prompt responses 🙂

Regards

Saransh Tiwari

Thumbsup. Glad to help.

If anyone is looking for an answer in 2021, you can also use python. Just use the Python extension and here it goes:

import base64

def decode (encoded):
  decoded = base64.b64decode(encoded)
  return decoded

encoded = flow.getVariable("your-reference-variable-here")

decoded = decode(encoded)

You can use the "static function" in Apigee Message Templates to base64-decode anything that is a string:

<AssignMessage name="AM-DecodeBase64">
  <AssignVariable> 
    <Name>decoded</Name>
    <Template>{decodeBase64(encoded_value)}</Template>
  </AssignVariable>
</AssignMessage> 

This won't work if the encoded thing represents a byte stream that cannot be decoded into a UTF-8 string. For example, if it's a base64-encoded image or PDF. For that you need a different approach - a Java callout or....something else.