Base64URL Codec

RFC 7515 base64url encoding and decoding without padding:

Javascript implementation:


/**
 * https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
 */
const base64Encode = str =>
  btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) =>
      String.fromCharCode("0x" + p1)
    )
  );

const base64Decode = str =>
  decodeURIComponent(
    atob(str)
      .split("")
      .map(c => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
      .join("")
  );

/**
 * https://tools.ietf.org/html/rfc7515#appendix-C
 */
const base64UrlEncode = str =>
  base64Encode(str)
    .split("=")[0]
    .replace("+", "-")
    .replace("/", "_");

const base64UrlDecode = str =>
  base64Decode(
    str
      .replace("-", "+")
      .replace("_", "/")
      .padEnd(str.length + (4 - str.length % 4) % 4, "=")
  );

Download: base64url.js

Bash implementation:

#!/bin/bash

usage() {
  cat <<EOF
RFC 7515 base64url encoding and decoding without padding.

usage:
       echo abdc | base64url -E
       base64url -E abdc
       base64url.sh -D YWJjZA
       echo YWJjZA | base64url.sh -D

EOF
}

encode() {
  tr -d '[:space:]' | openssl base64 -e -A | tr -- '+/' '-_' | tr -d =
}

decode() {
  local str
  local pad
  str=$(tr -d '[:space:]' | tr -- '-_' '+/')
  pad=$(( (4 - ${#str} % 4) %4 ))
  printf "%s%s" "$str" "$(printf '=%.0s' $(seq $pad))" | openssl base64 -d -A
}

if [[ $1 == -E ]]
then
  if [[ -z $2 ]]
  then
    encode
  else
    printf "%s" "$2" | encode | xargs printf "%s\\n"
  fi

elif [[ $1 == -D ]]
then
  if [[ -z $2 ]]
  then
    decode
  else
    printf "%s" "$2" | decode | xargs printf "%s\\n"
  fi

else
  usage
fi

Download: base64url.sh