How to convert token to the desired format? - javascript

How to convert token to the desired format?
I get token from domain.com on profile.damain.com via urlParams
testConfirm = () => {
if(window.location.href = 'http://localhost:3000') {
const req = localStorage.getItem('accessToken')
JSON.parse(req)
window.location.href = `http://1.localhost:3000?token=${req}`
return true
}
}
Now it comes like this:
{%22token%22:%225ecc2023eb593303e6dea15f5ec775874d656203fdb401805ecc2023eb593303e6dea160%22,%22expiredAt%22:1590522275554,%22timeZone%22:%22Europe/Moscow%22,%22active%22:true}
As needed: {"token":"5eccbf2e94b981c418d675355ec777c894b981c4185100a75eccbf2e94b981c418d67536","expiredAt":1590562990267,"timeZone":"Europe/Moscow","active":true}

Use decodeURI:
let string = '{%22token%22:%225ecc2023eb593303e6dea15f5ec775874d656203fdb401805ecc2023eb593303e6dea160%22,%22expiredAt%22:1590522275554,%22timeZone%22:%22Europe/Moscow%22,%22active%22:true}'
console.log(decodeURI(string))

Related

Issues with identifying and encoding urls

I'm having issues with parsing/manipulating URI:
Problem Statement:
I want to encode f[users.comma] and return it.
[Case-1] I get an url from backend service, encode f[users.comma] and return it.
[Case-2] I get an url from backend service and f[users.comma] is already encoded. So don't double encode and return it.
Expected Output:
`/demo/bigquery/order_items?fields=users.email&f[users.comma]=%22Abbeville%2C+Georgia%22`
Code:
const encodedExample = `/demo/bigquery/order_items?fields=users.email&f[users.comma]=%22Abbeville%2C+Georgia%22` // the last param is encoded
const regularExample2 = `/demo/bigquery/order_items?fields=users.email&f[users.comma]="Abbeville, Georgia"` //
const specialEncode = (url) => {
for (let queryParam of urlObj) {
const [urlKey, urlValue] = queryParam
// Check to see if url contains f[users.comma]
if (urlKey.includes('f[')) {
urlObj.set(urlKey, encodeURI(urlValue))
}
}
return urlObj.toString() // doesn't seem to work
}
I feel like I am going offroad with my approach. I'd appreciate some help here.
Since the backend service returns an encoded or decode url
We can first decode the url from the backend service (this won't produce any exceptions if url is already encoded)
const encodedExample = `/demo/bigquery/order_items?fields=users.email&f[users.comma]=%22Abbeville%2C+Georgia%22` // the last param is encoded
const regularExample2 = `/demo/bigquery/order_items?fields=users.email&f[users.comma]="Abbeville, Georgia"`
const specialEncode = (url) => {
let decodedUrl = decodeURI(url);
let encodedUrl = encodeURI(decodedUrl);
// fix "f[users.comma]" because encodeURI will encode the [ and ] as well
encodedUrl = encodedUrl.replace("f%5Busers.comma%5D", "f[users.comma]")
console.log(encodedUrl);
return encodedUrl;
}
specialEncode(encodedExample); // logs and returns: /demo/bigquery/order_items?fields=users.email&f[users.comma]=%22Abbeville%252C+Georgia%22
specialEncode(regularExample2); // logs and returns: /demo/bigquery/order_items?fields=users.email&f[users.comma]=%22Abbeville%252C+Georgia%22
The code above works fine for both encoded and decoded urls

Trying to split following url

I'm trying to split following URL:
http://www.store.com/products.aspx/Books/The-happy-donkey
in order to get only store.
How can this be done?
To do something as generic as possible I'd do something like:
const url = new URL('http://www.store.com/products.aspx/Books/The-happy-donkey');
const { hostname } = url;
const domain = hostname.match(/^www\.(\w+)\.com/);
console.log(domain[1]);
Use below code
//window.location.href
let str = "http://www.store.com/products.aspx/Books/The-happy-donkey";
const words = str.split('.');
console.log(words[1]);

Javascript - query params from window.location.href

I'm new to Javascript syntax; sorry if this is too basic.
With this query:
const params = queryString.parse(window.location.href)
I am fetching:
{http://localhost:3000/#access_token: "accessToken", refresh_token: "refreshToken"}
and now I can easily do:
const refresh_token = params.refresh_token;
But how do I fetch "accessToken"?
It looks like the hash is malformed JSON. While it'd be parsable by adding a { to the left side, adding "s around word characters before a :, and JSON.parse-ing it:
// const { hash } = window.location;
// const badJSON = '{' + hash.slice(1);
// now, you'll have:
const badJSON = '{' + 'access_token: "accessToken", refresh_token: "refreshToken"}';
const json = badJSON.replace(/\w+(?=:)/g, '"$&"');
const obj = JSON.parse(json);
console.log(obj.access_token);
console.log(obj.refresh_token);
This is extremely awkward, and is a solution to an X/Y problem. It would be much better to fix whatever generates the URL so that its format is in the standard format so you can parse it with URLSearchParams. For example, the URL should be something like
http://localhost:3000/?access_token=accessToken&refresh_token=refreshToken
And then it can be parsed very easily:
const url = 'http://localhost:3000/?access_token=accessToken&refresh_token=refreshToken';
const params = new URLSearchParams(url.match(/\?.*/)[0]);
// in your real code, the above can be replaced with:
// const params = new URLSearchParams(window.location.search);
console.log(params.get('access_token'));
This did the trick:
getHashParams() {
const hashParams = {};
const r = /([^&;=]+)=?([^&;]*)/g;
const q = window.location.hash.substring(1);
let e = r.exec(q);
while (e) {
hashParams[e[1]] = decodeURIComponent(e[2]);
e = r.exec(q);
}
return hashParams;
}
and calling like so:
const params = this.getHashParams();
console.log(params);
logged:
{access_token: "aceessToken", refresh_token: "refreshToken"}

Coinbase Pro authentication: No valid signature

I’m attempting to authenticate to Coinbase Pro from a Google Script. I’ve managed to do this already in Postman using CryptoJS, but I’m running into issues generating the CB-ACCESS-SIGN signature header. I’ve set up a test using a test key and secret string to figure out the differences between CryptoJS.HmacSHA256 and Utilities.computeHmacSha256Signature, the implementation Google offers, and noticed a difference in parameters: CryptoJS.HmacSHA256 expects a secret as WordArray while Utilities.computeHmacSha256Signature expects a secret as string.
In Postman I'm doing the following to get the wordarray of my apiSecret to pass to CryptoJS.HmacSHA256:
var hash = CryptoJS.enc.Base64.parse(pm.variables.get('apiSecret'));
In my Google script I'm doing the sam
var hash = Utilities.base64Decode(apiSecretB64)
I've tried debugging this with the same secret and message, but I'm getting different results.
My implementation in Postman:
function computeSignature(request) {
const data = request.data;
const method = request.method.toUpperCase();
const path = getPath(request.url);
const body = (method === 'GET' || !data) ? '' : JSON.stringify(data);
const message = timestamp + method + path + body;
const apiSecret = CryptoJS.enc.Base64.parse(pm.variables.get('apiSecret'));
const hash = CryptoJS.HmacSHA256(message, apiSecret);
const hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
return hashInBase64;
}
And my implementation in Google Script:
function computeSignature(request, path, timestamp) {
const data = request.data;
const method = request.method;
const body = (method === 'GET' || !data) ? '' : JSON.stringify(data);
const message = timestamp + method + path + body;
var apiSecret = Utilities.base64Decode(apiSecretB64);
var hash = Utilities.computeHmacSha256Signature(message, apiSecret);
hash = Utilities.base64Encode(hash);
return hash;
}
Does anyone know why I'm getting different results?
I've managed to solve the issue by converting the message from string to a byte array:
function computeSignature(request, path, timestamp) {
const data = request.data;
const method = request.method;
const body = (method === 'GET' || !data) ? '' : JSON.stringify(data);
const message = timestamp + method + path + body;
var apiSecretByteArr = Utilities.base64Decode(apiSecretB64);
var messageByteArr = Utilities.base64Decode(Utilities.base64Encode(message));
var hash = Utilities.computeHmacSha256Signature(messageByteArr, apiSecretByteArr);
return Utilities.base64Encode(hash);
}
There is probably a better way of doing this, but at least the correct signature is now being computed.

How to decode jwt token in javascript without using a library?

How can I decode the payload of JWT using JavaScript? Without a library. So the token just returns a payload object that can consumed by my front-end app.
Example token: xxxxxxxxx.XXXXXXXX.xxxxxxxx
And the result is the payload:
{exp: 10012016 name: john doe, scope:['admin']}
Note: this does not validate the signature, it just extracts the JSON payload from the token, which could have been tampered with.
Browser
Working unicode text JWT parser function:
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
}
JWT uses base64url (RFC 4648 §5), so using only atob (which uses base64) isn't enough.
Node.js
function parseJwt (token) {
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
}
Simple function with try - catch
const parseJwt = (token) => {
try {
return JSON.parse(atob(token.split('.')[1]));
} catch (e) {
return null;
}
};
Thanks!
You can use jwt-decode, so then you could write:
import jwt_decode from 'jwt-decode';
var token = 'eyJ0eXAiO.../// jwt token';
var decoded = jwt_decode(token);
console.log(decoded);
/*{exp: 10012016 name: john doe, scope:['admin']}*/
you can use pure javascript atob() function to decode token into a string:
atob(token.split('.')[1]);
or parse directly it into a json object:
JSON.parse(atob(token.split('.')[1]));
read about atob() and btoa() built-in javascript functions Base64 encoding and decoding - Web APIs | MDN.
function parseJwt(token) {
var base64Payload = token.split('.')[1];
var payload = Buffer.from(base64Payload, 'base64');
return JSON.parse(payload.toString());
}
let payload= parseJwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
console.log("payload:- ", payload);
If using node, you might have to use buffer package:
npm install buffer
var Buffer = require('buffer/').Buffer
As "window" object is not present in nodejs environment,
we could use the following lines of code :
let base64Url = token.split('.')[1]; // token you get
let base64 = base64Url.replace('-', '+').replace('_', '/');
let decodedData = JSON.parse(Buffer.from(base64, 'base64').toString('binary'));
It's working for me perfectly. Hope it helps.
If you're using Typescript or vanilla JavaScript, here's a zero-dependency, ready to copy-paste in your project simple function (building on #Rajan Maharjan 's answer).
This answer is particularly good, not only because it does not depend on any npm module, but also because it does not depend an any node.js built-in module (like Buffer) that some other solutions here are using and of course would fail in the browser (unless polyfilled, but there's no reason to do that in the first place). Additionally JSON.parse can fail at runtime and this version (especially in Typescript) will force handling of that. The JSDoc annotations will make future maintainers of your code thankful. :)
/**
* Returns a JS object representation of a Javascript Web Token from its common encoded
* string form.
*
* #template T the expected shape of the parsed token
* #param {string} token a Javascript Web Token in base64 encoded, `.` separated form
* #returns {(T | undefined)} an object-representation of the token
* or undefined if parsing failed
*/
export function getParsedJwt<T extends object = { [k: string]: string | number }>(
token: string,
): T | undefined {
try {
return JSON.parse(atob(token.split('.')[1]))
} catch {
return undefined
}
}
For completion, here's the vanilla javascript version too:
/**
* Returns a JS object representation of a Javascript Web Token from its common encoded
* string form.
*
* #param {string} token a Javascript Web Token in base64 encoded, `.` separated form
* #returns {(object | undefined)} an object-representation of the token
* or undefined if parsing failed
*/
export function getParsedJwt(token) {
try {
return JSON.parse(atob(token.split('.')[1]))
} catch (error) {
return undefined
}
}
If you use Node.JS,
You can use the native Buffer module by doing :
const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImU3YjQ0Mjc4LTZlZDYtNDJlZC05MTZmLWFjZDQzNzhkM2U0YSIsImlhdCI6MTU5NTg3NzUxOCwiZXhwIjoxNTk1ODgxMTE4fQ.WXyDlDMMSJAjOFF9oAU9JrRHg2wio-WolWAkAaY3kg4';
const tokenDecodablePart = token.split('.')[1];
const decoded = Buffer.from(tokenDecodablePart, 'base64').toString();
console.log(decoded)
And you're good to go :-)
#Peheje will work, but you will have problem with unicode.
To fix it I use the code on https://stackoverflow.com/a/30106551/5277071;
let b64DecodeUnicode = str =>
decodeURIComponent(
Array.prototype.map.call(atob(str), c =>
'%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
).join(''))
let parseJwt = token =>
JSON.parse(
b64DecodeUnicode(
token.split('.')[1].replace('-', '+').replace('_', '/')
)
)
let form = document.getElementById("form")
form.addEventListener("submit", (e) => {
form.out.value = JSON.stringify(
parseJwt(form.jwt.value)
)
e.preventDefault();
})
textarea{width:300px; height:60px; display:block}
<form id="form" action="parse">
<textarea name="jwt">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkrDtGhuIETDs8OoIiwiYWRtaW4iOnRydWV9.469tBeJmYLERjlKi9u6gylb-2NsjHLC_6kZNdtoOGsA</textarea>
<textarea name="out"></textarea>
<input type="submit" value="parse" />
</form>
I use this function to get payload , header , exp(Expiration Time), iat (Issued At) based on this answer
function parseJwt(token) {
try {
// Get Token Header
const base64HeaderUrl = token.split('.')[0];
const base64Header = base64HeaderUrl.replace('-', '+').replace('_', '/');
const headerData = JSON.parse(window.atob(base64Header));
// Get Token payload and date's
const base64Url = token.split('.')[1];
const base64 = base64Url.replace('-', '+').replace('_', '/');
const dataJWT = JSON.parse(window.atob(base64));
dataJWT.header = headerData;
// TODO: add expiration at check ...
return dataJWT;
} catch (err) {
return false;
}
}
const jwtDecoded = parseJwt('YOUR_TOKEN') ;
if(jwtDecoded)
{
console.log(jwtDecoded)
}
If using node.js 16 or later, you can use the built-in base64url encoder/decoder.
let payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url"));
I found this code at jwt.io and it works well.
//this is used to parse base64
function url_base64_decode(str) {
var output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) {
case 0:
break;
case 2:
output += '==';
break;
case 3:
output += '=';
break;
default:
throw 'Illegal base64url string!';
}
var result = window.atob(output); //polifyll https://github.com/davidchambers/Base64.js
try{
return decodeURIComponent(escape(result));
} catch (err) {
return result;
}
}
In some cases(certain development platforms),
the best answer(for now) faces a problem of invalid base64 length.
So, I needed a more stable way.
I hope it would help you.
You can define & use this one liner func:
jwtDecode = b => JSON.parse(Buffer.from(b.split('.')[1], 'base64').toString('binary'));
all features of jwt.io doesn't support all languages. In NodeJs you can use
var decoded = jwt.decode(token);
Answer based from GitHub - auth0/jwt-decode. Altered the input/output to include string splitting and return object { header, payload, signature } so you can just pass the whole token.
var jwtDecode = function (jwt) {
function b64DecodeUnicode(str) {
return decodeURIComponent(atob(str).replace(/(.)/g, function (m, p) {
var code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
}));
}
function decode(str) {
var output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
break;
case 2:
output += "==";
break;
case 3:
output += "=";
break;
default:
throw "Illegal base64url string!";
}
try {
return b64DecodeUnicode(output);
} catch (err) {
return atob(output);
}
}
var jwtArray = jwt.split('.');
return {
header: decode(jwtArray[0]),
payload: decode(jwtArray[1]),
signature: decode(jwtArray[2])
};
};
Simple NodeJS Solution for Decoding a JSON Web Token (JWT)
function decodeTokenComponent(value) {
const buff = new Buffer(value, 'base64')
const text = buff.toString('ascii')
return JSON.parse(text)
}
const token = 'xxxxxxxxx.XXXXXXXX.xxxxxxxx'
const [headerEncoded, payloadEncoded, signature] = token.split('.')
const [header, payload] = [headerEncoded, payloadEncoded].map(decodeTokenComponent)
console.log(`header: ${header}`)
console.log(`payload: ${payload}`)
console.log(`signature: ${signature}`)
In Node.js (TypeScript):
import { TextDecoder } from 'util';
function decode(jwt: string) {
const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.');
if (length !== 3) {
throw new TypeError('Invalid JWT');
}
const decode = (input: string): JSON => { return JSON.parse(new TextDecoder().decode(new Uint8Array(Buffer.from(input, 'base64')))); };
return { header: decode(encodedHeader), payload: decode(encodedPayload), signature: signature };
}
With jose by panva on GitHub, you could use the minimal import { decode as base64Decode } from 'jose/util/base64url' and replace new Uint8Array(Buffer.from(input, 'base64')) with base64Decode(input). Code should then work in both browser and Node.js.
Here is a more feature-rich solution I just made after studying this question:
const parseJwt = (token) => {
try {
if (!token) {
throw new Error('parseJwt# Token is required.');
}
const base64Payload = token.split('.')[1];
let payload = new Uint8Array();
try {
payload = Buffer.from(base64Payload, 'base64');
} catch (err) {
throw new Error(`parseJwt# Malformed token: ${err}`);
}
return {
decodedToken: JSON.parse(payload),
};
} catch (err) {
console.log(`Bonus logging: ${err}`);
return {
error: 'Unable to decode token.',
};
}
};
Here's some usage samples:
const unhappy_path1 = parseJwt('sk4u7vgbis4ewku7gvtybrose4ui7gvtmalformedtoken');
console.log('unhappy_path1', unhappy_path1);
const unhappy_path2 = parseJwt('sk4u7vgbis4ewku7gvtybrose4ui7gvt.malformedtoken');
console.log('unhappy_path2', unhappy_path2);
const unhappy_path3 = parseJwt();
console.log('unhappy_path3', unhappy_path3);
const { error, decodedToken } = parseJwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c');
if (!decodedToken.exp) {
console.log('almost_happy_path: token has illegal claims (missing expires_at timestamp)', decodedToken);
// note: exp, iat, iss, jti, nbf, prv, sub
}
I wasn't able to make that runnable in StackOverflow code snippet tool, but here's approximately what you would see if you ran that code:
I made the parseJwt function always return an object (to some degree for static-typing reasons).
This allows you to utilize syntax such as:
const { decodedToken, error } = parseJwt(token);
Then you can test at run-time for specific types of errors and avoid any naming collision.
If anyone can think of any low effort, high value changes to this code, feel free to edit my answer for the benefit of next(person).
Based on answers here and here:
const dashRE = /-/g;
const lodashRE = /_/g;
module.exports = function jwtDecode(tokenStr) {
const base64Url = tokenStr.split('.')[1];
if (base64Url === undefined) return null;
const base64 = base64Url.replace(dashRE, '+').replace(lodashRE, '/');
const jsonStr = Buffer.from(base64, 'base64').toString();
return JSON.parse(jsonStr);
};
An es-module friendly simplified version of jwt-decode.js
function b64DecodeUnicode(str) {
return decodeURIComponent(
atob(str).replace(/(.)/g, function (m, p) {
var code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = "0" + code;
}
return "%" + code;
})
);
}
function base64_url_decode(str) {
var output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
break;
case 2:
output += "==";
break;
case 3:
output += "=";
break;
default:
throw "Illegal base64url string!";
}
try {
return b64DecodeUnicode(output);
} catch (err) {
return atob(output);
}
}
export function jwtDecode(token, options) {
options = options || {};
var pos = options.header === true ? 0 : 1;
try {
return JSON.parse(base64_url_decode(token.split(".")[pos]));
} catch (e) {
console.log(e.message);
}
}

Categories

Resources