#!/bin/bash
# Based on
#   https://gist.github.com/thomasdarimont/46358bc8167fce059d83a1ebdb92b0e7
# Modified to understand WLCG Bearer Token Discovery and to have an option
#  to print the algorithm in the first part of the token

usage()
{
    echo 'Usage: htdecodetoken [-a] [-H] [-s|-f] [-v] [file]'
    echo
    echo 'Decodes a JSON Web Token'
    echo '  -a: show algorithm portion of JWT'
    echo '  -H: show dates in human readable format instead of epoch'
    echo '  -s: skip scitokens-verify validation (cannot be used with -f)'
    echo '        This is the default if stdout is not a TTY, or if scitokens-verify' 
    echo '        is not available in the PATH.'
    echo '  -f: force scitokens-verify validation (cannot be used with -s)'
    echo '  -v: enable verbose mode, showing in stderr where token came from'
    echo 'File name may be "-" to read from stdin.'
    echo 'If file name not given, follows WLCG Bearer Token Discovery'
    echo '  which is to first try $BEARER_TOKEN, next $BEARER_TOKEN_FILE,'
    echo '  and next ${XDG_RUNTIME_DIR:-/tmp}/bt_u`id -u`.'
    exit 1
} >&2

decode_base64_url() {
  local len=$((${#1} % 4))
  local result="$1"
  if [ $len -eq 2 ]; then result="$1"'=='
  elif [ $len -eq 3 ]; then result="$1"'=' 
  fi
  echo "$result" | tr '_-' '/+' | base64 --decode
}


decode_jwt() {
  local showalg=$1
  local token=$2
  if "$showalg" ; then
      decode_base64_url "$(echo "$token"|cut -d. -f1)" | jq .
  fi
  decode_base64_url "$(echo "$token"|cut -d. -f2)" | jq .
}



human_dates() {
    local wrd
    local w1
    local date

    for wrd in "$@"; do
        w1=${wrd/,/}
        if [[ "$w1" =~ ^[0-9]+$ ]]; then
            # field is entirely numeric
            echo -n "\"$(date --date=@$w1)\""
            if [ "$wrd" != "$w1" ]; then
                # add the comma back
                echo ','
            else
                echo
            fi
        else
            echo "$wrd"
        fi
    done
}


# script starts here -- "main"

set -e

# If stdout is not a TTY, we should not validate the token with scitokens-verify by default
SKIPVERIFY=false
if [ ! -t 1 ]; then
    SKIPVERIFY=true
fi

VERBOSE=false
SHOWALG=false
HUMANDATE=false
SKIPVERIFYFLAGSET=false
FORCEVERIFYFLAGSET=false
NUMSHIFT=0
while getopts ":vaHsf" opt; do
    case "$opt" in
       a)
          SHOWALG=true
          (( NUMSHIFT+=1 ))
          ;;
       H)
          HUMANDATE=true
          (( NUMSHIFT+=1 ))
          ;;
       s)
          if "$FORCEVERIFYFLAGSET" ; then
            echo "Cannot use both -s and -f options together" >&2
            usage
          fi
          SKIPVERIFY=true
          SKIPVERIFYFLAGSET=true
          (( NUMSHIFT+=1 ))
          ;;
       f)
          if "$SKIPVERIFYFLAGSET" ; then
              echo "Cannot use both -s and -f options together" >&2
              usage
          fi
          SKIPVERIFY=false
          FORCEVERIFYFLAGSET=true
          (( NUMSHIFT+=1 ))
          ;;
       v) 
          VERBOSE=true
          (( NUMSHIFT+=1 ))
          ;;
       *)
          usage
    esac
done
shift "$NUMSHIFT"

TOKEN=""
TOKENFILE=""
if [ $# = 0 ]; then
    if [ -n "$BEARER_TOKEN" ]; then
        TOKEN="$BEARER_TOKEN"
        if $VERBOSE; then
            echo 'Token source: $BEARER_TOKEN' >&2
        fi
    else
        TOKENFILE="${BEARER_TOKEN_FILE:-${XDG_RUNTIME_DIR:-/tmp}/bt_u`id -u`}"
    fi
elif [ $# = 1 ]; then
    if [ "$1" = - ]; then
        read TOKEN # from stdin
        if $VERBOSE; then
            echo 'Token source: stdin' >&2
        fi
    else
        TOKENFILE="$1"
    fi
else
    usage
fi
if [ -n "$TOKENFILE" ]; then
    if [ ! -e "$TOKENFILE" ]; then
        echo "$TOKENFILE not found" >&2
        exit 1
    fi
    # the -n is needed here for when there is no ending newline in the file
    read TOKEN <$TOKENFILE || [ -n "$TOKEN" ]
    if $VERBOSE; then
        echo "Token source: $TOKENFILE" >&2
    fi
fi
if [ -z "$TOKEN" ]; then
    echo "Token is empty" >&2
    usage
fi


JWT="$(decode_jwt "$SHOWALG" "$TOKEN")"

if "$HUMANDATE"  ; then
   READABLE="$(human_dates $JWT)"
   echo $READABLE | jq .
else
   echo "$JWT" | jq .
fi
RET=$?

if "$SKIPVERIFY" ; then
    # If we want to skip token verification, exit now
    exit $RET
fi

set +e
VERIFY="$(command -v scitokens-verify)"
if [ $? != 0 ]; then
    # silently exit if scitokens-verify not found
    exit
fi
VERIFYOUT="$($VERIFY $TOKEN)"
RET=$?
if [ $RET != 0 ]; then
    if [ -n "$VERIFYOUT" ]; then
        echo "$VERIFYOUT" >&2
    fi
    exit $RET
fi
