From efa180fce86e27aeba9d51b76e3e864c57899a99 Mon Sep 17 00:00:00 2001 From: Boris Yankov Date: Wed, 1 Aug 2018 01:58:22 +0300 Subject: [PATCH] encoding: Add `strToBase64` function and tests The function encodes a JavaScript string to a string in Base64 format. The way to reliably encode strings to Base64 is described in many places, including: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa#Unicode_strings Instead of the `btoa` function we use `base64.encode` from our external base64 library to prevent the error: 'InvalidCharacterError: The string to be encoded contains characters outside of the Latin1 range.' Note: JavaScript strings are UCS-2 or UTF-16 (not UTF-8) http://es5.github.io/x2.html --- src/utils/__tests__/encoding-test.js | 32 +++++++++++++++++++++++++++- src/utils/encoding.js | 5 +++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/encoding-test.js b/src/utils/__tests__/encoding-test.js index 2c870c4e566..9d53c8cbeaf 100644 --- a/src/utils/__tests__/encoding-test.js +++ b/src/utils/__tests__/encoding-test.js @@ -5,6 +5,7 @@ import { hexToAscii, asciiToHex, xorHexStrings, + strToBase64, extractApiKey, } from '../encoding'; @@ -61,11 +62,40 @@ describe('asciiToHex', () => { }); }); +describe('strToBase64', () => { + test('can handle an empty string', () => { + const obj = ''; + const expected = ''; + + const result = strToBase64(obj); + + expect(result).toBe(expected); + }); + + test('can encode any string', () => { + const obj = { + key: 'ABCabc123', + empty: null, + array: [1, 2, 3], + }; + const expected = 'W29iamVjdCBPYmplY3Rd'; + + const result = strToBase64(obj); + + expect(result).toBe(expected); + }); + + test('supports unicode characters', () => { + const obj = { key: '😇😈' }; + const expected = 'W29iamVjdCBPYmplY3Rd'; + const result = strToBase64(obj); + expect(result).toBe(expected); + }); +}); describe('extractApiKey', () => { test('correctly extracts an API key that has been XORed with a OTP', () => { const key = 'testing'; const otp = 'A8348A93A83493'; - const encoded = xorHexStrings(asciiToHex(key), otp); expect(extractApiKey(encoded, otp)).toBe(key); }); diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 3e2d9cdf8e2..6908022685a 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -32,6 +32,11 @@ export const base64ToHex = (bytes: string) => asciiToHex(base64.decode(bytes)); export const hexToBase64 = (hex: string) => base64.encode(hexToAscii(hex)); +// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding +// `base64.encode` used instead of `btoa` to support Unicode input +export const strToBase64 = (text: string): string => + base64.encode(unescape(encodeURIComponent(text))); + // Extract an API key encoded as a hex string XOR'ed with a one time pad (OTP) // (this is used during the OAuth flow) export const extractApiKey = (encoded: string, otp: string) =>