Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

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 9 8,471
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

9 REPLIES 9

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.

@dchiesa1 DecodeJWT fails to decode for unsignedJWT. I get the following error "Invalid JWS header: The algorithm "alg" header parameter must be for signatures" from trace

correct. The JWT policies in Apigee, including DecodeJWT, do not work (should not work) with unsigned JWT, or JWT with alg="none". 

You can "manually" decode such a JWT using JavaScript code if you wish.