diff --git a/encoding/base64url.ts b/encoding/base64url.ts new file mode 100644 index 000000000..726ea2eb8 --- /dev/null +++ b/encoding/base64url.ts @@ -0,0 +1,45 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { + decode as convertBase64ToArrayBuffer, + encode as convertArrayBufferToBase64, +} from "./base64.ts"; + +/* + * Some variants allow or require omitting the padding '=' signs: + * https://en.wikipedia.org/wiki/Base64#URL_applications + */ +export function addPaddingToBase64url(base64url: string): string { + if (base64url.length % 4 === 2) return base64url + "=="; + if (base64url.length % 4 === 3) return base64url + "="; + if (base64url.length % 4 === 1) + throw new TypeError("Illegal base64url string!"); + return base64url; +} + +function convertBase64urlToBase64(base64url: string): string { + return addPaddingToBase64url(base64url) + .replace(/\-/g, "+") + .replace(/_/g, "/"); +} + +function convertBase64ToBase64url(base64: string): string { + return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); +} + +/** + * Converts given data with base64url encoding. + * Removes paddings '='. + * @param data input to encode + */ +export function encode(data: string | ArrayBuffer): string { + return convertBase64ToBase64url(convertArrayBufferToBase64(data)); +} + +/** + * Converts given base64url encoded data back to original + * @param data input to decode + */ +export function decode(data: string): ArrayBuffer { + return convertBase64ToArrayBuffer(convertBase64urlToBase64(data)); +} diff --git a/encoding/base64url_test.ts b/encoding/base64url_test.ts new file mode 100644 index 000000000..2af9096a4 --- /dev/null +++ b/encoding/base64url_test.ts @@ -0,0 +1,42 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +const { test } = Deno; +import { assertEquals } from "../testing/asserts.ts"; +import { encode, decode } from "./base64url.ts"; + +const testsetString = [ + ["", ""], + ["f", "Zg"], + ["fo", "Zm8"], + ["foo", "Zm9v"], + ["foob", "Zm9vYg"], + ["fooba", "Zm9vYmE"], + ["foobar", "Zm9vYmFy"], + [">?>d?ß", "Pj8-ZD_f"], +]; + +const testsetBinary = [ + [new TextEncoder().encode("\x00"), "AA"], + [new TextEncoder().encode("\x00\x00"), "AAA"], + [new TextEncoder().encode("\x00\x00\x00"), "AAAA"], + [new TextEncoder().encode("\x00\x00\x00\x00"), "AAAAAA"], +]; + +test("[encoding/base64url] testBase64urlEncodeString", () => { + for (const [input, output] of testsetString) { + assertEquals(encode(input), output); + } +}); + +test("[encoding/base64url] testBase64urlEncodeBinary", () => { + for (const [input, output] of testsetBinary) { + assertEquals(encode(input), output); + } +}); + +test("[encoding/base64ur] testBase64urDecodeBinary", () => { + for (const [input, output] of testsetBinary) { + const outputBinary = new Uint8Array(decode(output as string)); + assertEquals(outputBinary, input as Uint8Array); + } +});