Skip to content

Commit

Permalink
Fix token deserialization with binary signatures (#298)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcospassos authored Dec 16, 2022
1 parent 3f93924 commit 6d0d95a
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 35 deletions.
36 changes: 22 additions & 14 deletions src/base64Url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,28 @@ function base64Escape(value: string): string {
.replace(/=/g, '');
}

export function base64UrlEncode(value: string): string {
return base64Escape(
window.btoa(
window.encodeURIComponent(value)
.replace(/%([0-9A-F]{2})/g, (_, p1) => String.fromCharCode(Number.parseInt(p1, 16))),
),
);
export function base64UrlEncode(value: string, utf8 = false): string {
if (utf8) {
return base64Escape(
window.btoa(
window.encodeURIComponent(value)
.replace(/%([0-9A-F]{2})/g, (_, p1) => String.fromCharCode(Number.parseInt(p1, 16))),
),
);
}

return base64Escape(window.btoa(value));
}

export function base64UrlDecode(value: string): string {
return window.decodeURIComponent(
Array.prototype.map.call(
window.atob(base64Unescape(value)),
(char: string) => `%${(`00${char.charCodeAt(0).toString(16)}`).slice(-2)}`,
).join(''),
);
export function base64UrlDecode(value: string, utf8 = false): string {
if (utf8) {
return window.decodeURIComponent(
Array.prototype.map.call(
window.atob(base64Unescape(value)),
(char: string) => `%${(`00${char.charCodeAt(0).toString(16)}`).slice(-2)}`,
).join(''),
);
}

return window.atob(base64Unescape(value));
}
6 changes: 3 additions & 3 deletions src/token/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ export class Token {
let signature;

try {
headers = JSON.parse(base64UrlDecode(parts[0]));
payload = JSON.parse(base64UrlDecode(parts[1]));
headers = JSON.parse(base64UrlDecode(parts[0], true));
payload = JSON.parse(base64UrlDecode(parts[1], true));

if (parts.length === 3) {
signature = base64UrlDecode(parts[2]);
signature = base64UrlDecode(parts[2], false);
}
} catch {
throw new Error('The token is corrupted.');
Expand Down
39 changes: 24 additions & 15 deletions test/base64Url.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import {base64UrlDecode, base64UrlEncode} from '../src/base64Url';

describe('A base64 URL encoder/decoder function', () => {
const encodeTests = [
['000000', 'MDAwMDAw'],
['', ''],
['f', 'Zg'],
['fo', 'Zm8'],
['foo', 'Zm9v'],
['foob', 'Zm9vYg'],
['fooba', 'Zm9vYmE'],
['foobar', 'Zm9vYmFy'],
['Jacaré', 'SmFjYXLDqQ'],
const encodeTests: Array<[string, string, boolean]> = [
['000000', 'MDAwMDAw', false],
['\0\0\0\0', 'AAAAAA', false],
['\xff', '_w', false],
['\xff\xff', '__8', false],
['\xff\xff\xff', '____', false],
['\xff\xff\xff\xff', '_____w', false],
['\xfb', '-w', false],
['', '', false],
['f', 'Zg', false],
['fo', 'Zm8', false],
['foo', 'Zm9v', false],
['foob', 'Zm9vYg', false],
['fooba', 'Zm9vYmE', false],
['foobar', 'Zm9vYmFy', false],
// UTF-8 tests
['Jacaré', 'SmFjYXLDqQ', true],
['\u00e9', 'w6k', true],
['\u00e9\u00e9', 'w6nDqQ', true],
];

it.each(encodeTests)('should encode "%s" as "%s"', (decoded: string, encoded: string) => {
expect(base64UrlEncode(decoded)).toBe(encoded);
it.each(encodeTests)('should encode "%s" as "%s"', (decoded: string, encoded: string, utf8: boolean) => {
expect(base64UrlEncode(decoded, utf8)).toBe(encoded);
});

const decodeTests = encodeTests.map(([encoded, decoded]) => [decoded, encoded]);
const decodeTests = encodeTests.map(([encoded, decoded, utf8]) => [decoded, encoded, utf8]);

it.each(decodeTests)('should decode "%s" as "%s"', (encoded: string, decoded: string) => {
expect(base64UrlDecode(encoded)).toBe(decoded);
it.each(decodeTests)('should decode "%s" as "%s"', (encoded: string, decoded: string, utf8: boolean) => {
expect(base64UrlDecode(encoded, utf8)).toBe(decoded);
});
});
10 changes: 7 additions & 3 deletions test/token/token.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Token, FixedTokenProvider} from '../../src/token';
import {base64UrlEncode} from '../../src/base64Url';
import {base64UrlEncode, base64UrlDecode} from '../../src/base64Url';

describe('A token', () => {
const appId = '7e9d59a9-e4b3-45d4-b1c7-48287f1e5e8a';
Expand Down Expand Up @@ -29,9 +29,13 @@ describe('A token', () => {
});

it('may contain a signature', () => {
const token = Token.parse(`${anonymousSerializedToken}${base64UrlEncode('some-signature')}`);
const binarySignature = 'uLvpiRxDrYpU1BO4Y6rLyFv3uj3PuPD3KFg1RA_Wu5S4'
+ 'svht8KsdS1WR8Sr-L55e-7_y9Do8LCTo3ZWp92JZDQ';

expect(token.getSignature()).toBe('some-signature');
const token = Token.parse(`${anonymousSerializedToken}${binarySignature}`);

// The result is a binary string
expect(token.getSignature()).toBe(base64UrlDecode(binarySignature, false));
});

it('should have an issue time', () => {
Expand Down

0 comments on commit 6d0d95a

Please sign in to comment.