1

In JavaScript, how can i decode a string that was encoded in C# using HttpServerUtility.UrlTokenEncode?

There are some equivalents in other languages but i couldn't rewrite them in JS. Any help would be appreciated.

This is the Java version.

This is the Objective-C version.

Update:

C#'s URLTokenEncode is not the same as base64 encoding. For example the last character is always the number of padding characters. So some characters need to be replaced properly. Java and Objective-C version of the code show which characters need to be replaced with what.

I tried decodeURIComponent but it was not decoded successfully. That makes sense because the string is encoded by a particular method. It's not base64.

For example, This is just a part of a C#'s UrlTokenEncode String:

vj4_fv7__7-_Pr6-ff3_Pr6_vz8_________________f____79_vP1_vb3_vz8____________________AAA1

And this is the correct decoded version using Objective-C/Java method:

vj4/fv7//7+/Pr6+ff3/Pr6/vz8/////////////////f////79/vP1/vb3/vz8////////////////////AAA=

Community
  • 1
  • 1
Sobhan
  • 1,051
  • 2
  • 13
  • 29

4 Answers4

2

I finally managed to convert the Objective-c version of URLTokenDecode by Jeffrey Thomas to JavaScript and it worked.

Here is the function:

function URLTokenDecode(token) {

    if (token.length == 0) return null;

    // The last character in the token is the number of padding characters.
    var numberOfPaddingCharacters = token.slice(-1);

    // The Base64 string is the token without the last character.
    token = token.slice(0, -1);

    // '-'s are '+'s and '_'s are '/'s.
    token = token.replace(/-/g, '+');
    token = token.replace(/_/g, '/');

    // Pad the Base64 string out with '='s
    for (var i = 0; i < numberOfPaddingCharacters; i++)
        token += "=";

    return token;
}

Here is the $filter if you are using AngularJS:

app.filter('URLTokenDecode', function () {
        return function (token) {

            if (token.length == 0) return null;

            // The last character in the token is the number of padding characters.
            var numberOfPaddingCharacters = token.slice(-1);

            // The Base64 string is the token without the last character.
            token = token.slice(0, -1);

            // '-'s are '+'s and '_'s are '/'s.
            token = token.replace(/-/g, '+');
            token = token.replace(/_/g, '/');

            // Pad the Base64 string out with '='s
            for (var i = 0; i < numberOfPaddingCharacters; i++)
                token += "=";

            return token;
        }

});
Community
  • 1
  • 1
Sobhan
  • 1,051
  • 2
  • 13
  • 29
  • Doesn't seem to be correct. You're missing out on the "int equalsCount = (int)(((char)bytes[inputLength]) - 0x30);" – Stefan Steiger Aug 18 '15 at 13:37
  • @Stefan, because of how str.replace() works in Javascript, i don't need to know the length of my input. That goes the same when i'm padding with '=', no need to know the length. So this should work. – Sobhan Aug 19 '15 at 03:42
  • Can't really say I understand why anybody does it this way instead of just url-encoding a base-64 encoding, or what's the logic behind this method, but anyway, it would surprise me if this decoding method would work correctly IN ALL CASES. Note that while it may work correctly in a few cases, this doesn't necessarely mean it works correctly for all cases. – Stefan Steiger Aug 20 '15 at 11:56
  • 1
    @StefanSteiger, I'm sending a binary image from my webservice and if i send it with Base64 encoding then in some cases it sends empty. But UrlTokenEncode works with the same case. So that's why i'm using this. About this method , I'd say it should work because it does what the algorithm says. I'd be surprised if it doesn't work in all cases. – Sobhan Aug 21 '15 at 16:12
  • Thanks a lot @Sobhan - I wasted a lot of time converting to hex then modify (with no exact solution) - until I found your solution – RaSor Jul 31 '20 at 09:06
0

I guess that would be decodeuricomponent:
http://www.w3schools.com/jsref/jsref_decodeuricomponent.asp

To decode a UTF8-base64-encoded string in JavaScript:

var str2 = decodeURIComponent(escape(window.atob(b64)));
console.log(str2);

To encode a UTF8-JavaScript-string as such in JavaScript:

var str = "äöüÄÖÜçéèñ";
var b64 = window.btoa(unescape(encodeURIComponent(str)))
console.log(b64);

Seems UrlTokenDecode is a little bit more complicated.
The best method to be 100% sure is to call UrlTokenDecode on the server-side with AJAX, then return a simple base64-encoded string.

public static byte[] UrlTokenDecode (string input)
{
    if (input == null)
        throw new ArgumentNullException ("input");
    if (input.Length < 1)
        return new byte[0];
    byte[] bytes = Encoding.ASCII.GetBytes (input);
    int inputLength = input.Length - 1;
    int equalsCount = (int)(((char)bytes[inputLength]) - 0x30);
    char[] ret = new char[inputLength + equalsCount];
    int i = 0;
    for (; i < inputLength; i++) {
        switch ((char)bytes[i]) {
            case '-':
                ret[i] = '+';
                break;

            case '_':
                ret[i] = '/';
                break;

            default:
                ret[i] = (char)bytes[i];
                break;
        }
    }
    while (equalsCount > 0) {
        ret[i++] = '=';
        equalsCount--;
    }

    return Convert.FromBase64CharArray (ret, 0, ret.Length);
}

Source

Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • this would not work because my string is not a base64 encoded string. It's encoded in C# using `URLTokenEncode` which is a bit different than base 64. Please see my update. Thanks. – Sobhan Aug 18 '15 at 09:31
  • Thanks @Stefan, but i have the limitation that the string i'm sending from server-side should always be UrlTokenEncoded because if it's encoded by Base64 then in some cases it doesn't work. So i have to decode it client-side. – Sobhan Aug 19 '15 at 03:46
0

This is possibly against the posting rules as its not an answer to the question but, I found this page whilst looking for the UrlTokenEncode method (not decode) so using the info here I made the following method which I hope helps someone else out:

function urlTokenEncode(str) {
    var b64 = btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
            function(match, t) {
                return String.fromCharCode('0x' + t);
            })),
        padChars = b64.match(/=/g);
    return b64.replace(/=/g, "") + (padChars == null ? 0 : padChars.length);
}

Tested and working with C# HttpServerUtility.UrlTokenEncode and HttpServerUtility.UrlTokenEncode

Jimbo
  • 22,379
  • 42
  • 117
  • 159
-1

read this artical in MDN which describes the problem & solution:

https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding

function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    }));
}

b64EncodeUnicode('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
MoLow
  • 3,056
  • 2
  • 21
  • 41
  • this would not work because my string is not a base64 encoded string. It's encoded in C# using `URLTokenEncode` which is a bit different than base64. Please see my update. Thanks. – Sobhan Aug 18 '15 at 09:37