#!/bin/bash # pkce - Generate a PKCE code verifier and its corresponding S256 code challenge # # Usage: # pkce [-n|--newline] # pkce -h # # Options: # -n, --newline Separate verifier and challenge with a newline (default: tab) # -h, --help Show help message # # Reference: # https://datatracker.ietf.org/doc/html/rfc7636 _pkce() ( local SCRIPT_NAME; SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" case "${BASH_SOURCE[0]}" in /dev/*|/proc/*) SCRIPT_NAME="" ;; esac case "$SCRIPT_NAME" in ""|bash|sh|zsh|dash) SCRIPT_NAME="pkce" ;; esac _error() { echo "[ERR][$SCRIPT_NAME] $*" >&2; } # Converts base64 to base64-url (URL-safe version) _base64_to_base64url() { # Remove padding (=) and newlines, replace '+' with '-' and '/' with '_' tr -d '=\n' | tr '+/' '-_' } _show_help() { cat </dev/null 2>&1; then _error "openssl is required" return 3 fi # RFC7636 specifies code verifier should be 43 to 128 random characters # 32 bytes in base64 is 43 characters (unpadded) # 96 bytes in base64 is 128 characters (unpadded) # https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 local code_verifier; code_verifier="$(openssl rand -base64 96 | _base64_to_base64url)" local code_challenge; code_challenge="$(printf %s "$code_verifier" | openssl dgst -binary -sha256 | openssl enc -base64 | _base64_to_base64url)" # Print code verifier and code challenge, separated by a tab or newline printf "%s$delimiter%s" "$code_verifier" "$code_challenge" ) _pkce "$@" __pkce_rc=$? unset -f _pkce if [ -n "${BASH_SOURCE[0]}" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then eval "unset __pkce_rc; return $__pkce_rc" fi eval "unset __pkce_rc; exit $__pkce_rc"