forked from labbots/google-drive-upload
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
google-oauth2.[sh|bash]: Make changes for refactored functions | Misc…
… changes * as it is supposed to be a sample script, so don't modify default config * new flag "add" - authenticates a new user but will use the client id and secret if available. If not, then same as create flag.
- Loading branch information
1 parent
040981c
commit 1302aba
Showing
2 changed files
with
131 additions
and
224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,132 +1,85 @@ | ||
#!/usr/bin/env bash | ||
# shellcheck source=/dev/null | ||
|
||
# A simple curl OAuth2 authenticator | ||
# | ||
# Usage: | ||
# ./google-oauth2.sh create - authenticates a user | ||
# ./google-oauth2.sh refresh <token> - gets a new token | ||
# | ||
# Set CLIENT_ID and CLIENT_SECRET and SCOPE | ||
# See SCOPES at https://developers.google.com/identity/protocols/oauth2/scopes#docsv1 | ||
|
||
set -o errexit -o noclobber -o pipefail | ||
|
||
_short_help() { | ||
printf " | ||
No valid arguments provided. | ||
_usage() { | ||
printf "%s\n" " | ||
A simple curl OAuth2 authenticator for google drive. Utilizing api v3. | ||
Usage: | ||
./%s create - authenticates a user. | ||
./%s refresh - gets a new access token. | ||
./${0##*/} create - authenticates a new user with a fresh client id and secret. | ||
./${0##*/} add - authenticates a new user but will use the client id and secret if available. If not, then same as create flag. | ||
./${0##*/} refresh - gets a new access token. Make sure CLIENT_SECRET, CLIENT_ID and REFRESH_TOKEN is exported as an environment variable or CONFIG | ||
./${0##*/} help - show this help. | ||
Make sure to export CONFIG as an environment variable if you want to use save new changes or want to use values from it. It should be in the format required by gupload. | ||
Use update as second argument to update the local config with the new REFRESH TOKEN. | ||
e.g: ./%s create/refresh update\n" "${0##*/}" "${0##*/}" "${0##*/}" | ||
Variable names - CLIENT_SECRET, CLIENT_ID and REFRESH_TOKEN | ||
You can also export CLIENT_SECRET, CLIENT_ID and REFRESH_TOKEN as an environment variable if you don't want to use above method." | ||
exit 0 | ||
} | ||
|
||
UTILS_FOLDER="${UTILS_FOLDER:-$(pwd)}" | ||
{ . "${UTILS_FOLDER}"/common-utils.bash && . "${UTILS_FOLDER}"/drive-utils.bash; } || { printf "Error: Unable to source util files.\n" && exit 1; } | ||
|
||
[[ ${1} = create ]] || [[ ${1} = refresh ]] || _short_help | ||
{ . "${UTILS_FOLDER}"/common-utils.bash && . "${UTILS_FOLDER}"/auth-utils.bash; } || { printf "Error: Unable to source util files.\n" && exit 1; } | ||
|
||
[[ ${2} = update ]] || _update_config() { :; } | ||
[[ $# = 0 ]] && _usage | ||
|
||
_check_debug | ||
|
||
CLIENT_ID="" | ||
CLIENT_SECRET="" | ||
SCOPE="https://www.googleapis.com/auth/drive" | ||
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" | ||
TOKEN_URL="https://accounts.google.com/o/oauth2/token" | ||
|
||
INFO_PATH="${HOME}/.google-drive-upload" CONFIG_INFO="${INFO_PATH}/google-drive-upload.configpath" | ||
[[ -f ${CONFIG_INFO} ]] && . "${CONFIG_INFO}" | ||
CONFIG="${CONFIG:-${HOME}/.googledrive.conf}" | ||
_cleanup() { | ||
# unhide the cursor if hidden | ||
[[ -n ${SUPPORT_ANSI_ESCAPES} ]] && printf "\e[?25h\e[?7h" | ||
{ | ||
# grab all script children pids | ||
script_children_pids="$(ps --ppid="${MAIN_PID}" -o pid=)" | ||
|
||
[[ -f ${CONFIG} ]] && . "${CONFIG}" | ||
# kill all grabbed children processes | ||
# shellcheck disable=SC2086 | ||
kill ${script_children_pids} 1>| /dev/null | ||
|
||
! [[ -t 2 ]] && [[ -z ${CLIENT_ID:+${CLIENT_SECRET:+${REFRESH_TOKEN}}} ]] && { | ||
printf "%s\n" "Error: Script is not running in a terminal, cannot ask for credentials." | ||
printf "%s\n" "Add in config manually if terminal is not accessible. CLIENT_ID, CLIENT_SECRET and REFRESH_TOKEN is required." && return 1 | ||
export abnormal_exit && if [[ -n ${abnormal_exit} ]]; then | ||
printf "\n\n%s\n" "Script exited manually." | ||
kill -- -$$ & | ||
fi | ||
} 2>| /dev/null || : | ||
return 0 | ||
} | ||
|
||
_print_center "justify" "Checking credentials.." "-" | ||
|
||
# Following https://developers.google.com/identity/protocols/oauth2#size | ||
CLIENT_ID_REGEX='[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com' | ||
CLIENT_SECRET_REGEX='[0-9A-Za-z_-]+' | ||
AUTHORIZATION_CODE_REGEX='[0-9]/[0-9A-Za-z_-]+' # 256 bytes | ||
|
||
until [[ -n ${CLIENT_ID} && -n ${CLIENT_ID_VALID} ]]; do | ||
[[ -n ${CLIENT_ID} ]] && { | ||
if [[ ${CLIENT_ID} =~ ${CLIENT_ID_REGEX} ]]; then | ||
[[ -n ${client_id} ]] && _update_config CLIENT_ID "${CLIENT_ID}" "${CONFIG}" | ||
CLIENT_ID_VALID="true" && continue | ||
else | ||
{ [[ -n ${client_id} ]] && message="- Try again"; } || message="in config ( ${CONFIG} )" | ||
"${QUIET:-_print_center}" "normal" " Invalid Client ID ${message} " "-" && unset CLIENT_ID client_id | ||
fi | ||
} | ||
[[ -z ${client_id} ]] && printf "\n" && "${QUIET:-_print_center}" "normal" " Enter Client ID " "-" | ||
[[ -n ${client_id} ]] && _clear_line 1 | ||
printf -- "-> " | ||
read -r CLIENT_ID && client_id=1 | ||
done | ||
|
||
until [[ -n ${CLIENT_SECRET} && -n ${CLIENT_SECRET_VALID} ]]; do | ||
[[ -n ${CLIENT_SECRET} ]] && { | ||
if [[ ${CLIENT_SECRET} =~ ${CLIENT_SECRET_REGEX} ]]; then | ||
[[ -n ${client_secret} ]] && _update_config CLIENT_SECRET "${CLIENT_SECRET}" "${CONFIG}" | ||
CLIENT_SECRET_VALID="true" && continue | ||
else | ||
{ [[ -n ${client_secret} ]] && message="- Try again"; } || message="in config ( ${CONFIG} )" | ||
"${QUIET:-_print_center}" "normal" " Invalid Client Secret ${message} " "-" && unset CLIENT_SECRET client_secret | ||
fi | ||
} | ||
[[ -z ${client_secret} ]] && printf "\n" && "${QUIET:-_print_center}" "normal" " Enter Client Secret " "-" | ||
[[ -n ${client_secret} ]] && _clear_line 1 | ||
printf -- "-> " | ||
read -r CLIENT_SECRET && client_secret=1 | ||
done | ||
|
||
_clear_line 1 | ||
|
||
if [[ ${1} = create ]]; then | ||
printf "\n" && "${QUIET:-_print_center}" "normal" "Visit the below URL, tap on allow and then enter the code obtained" " " | ||
URL="https://accounts.google.com/o/oauth2/auth?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPE}&response_type=code&prompt=consent" | ||
printf "\n%s\n" "${URL}" | ||
until [[ -n ${AUTHORIZATION_CODE} && -n ${AUTHORIZATION_CODE_VALID} ]]; do | ||
[[ -n ${AUTHORIZATION_CODE} ]] && { | ||
if grep -qE "${AUTHORIZATION_CODE_REGEX}" <<< "${AUTHORIZATION_CODE}"; then | ||
AUTHORIZATION_CODE_VALID="true" && continue | ||
else | ||
"${QUIET:-_print_center}" "normal" " Invalid CODE given, try again.. " "-" && unset AUTHORIZATION_CODE authorization_code | ||
fi | ||
} | ||
{ [[ -z ${authorization_code} ]] && printf "\n" && "${QUIET:-_print_center}" "normal" " Enter the authorization code " "-"; } || _clear_line 1 | ||
printf -- "-> " | ||
read -r AUTHORIZATION_CODE && authorization_code=1 | ||
done | ||
RESPONSE="$(curl --compressed "${CURL_PROGRESS}" -X POST \ | ||
--data "code=${AUTHORIZATION_CODE}&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&redirect_uri=${REDIRECT_URI}&grant_type=authorization_code" "${TOKEN_URL}")" || : | ||
_clear_line 1 1>&2 | ||
|
||
REFRESH_TOKEN="$(_json_value refresh_token 1 1 <<< "${RESPONSE}" || :)" | ||
if _get_access_token_and_update "${RESPONSE}"; then | ||
_update_config REFRESH_TOKEN "${REFRESH_TOKEN}" "${CONFIG}" | ||
printf "Access Token: %s\n" "${ACCESS_TOKEN}" | ||
printf "Refresh Token: %s\n" "${REFRESH_TOKEN}" | ||
else | ||
return 1 | ||
fi | ||
elif [[ ${1} = refresh ]]; then | ||
if [[ -n ${REFRESH_TOKEN} ]]; then | ||
_print_center "justify" "Required credentials set." "=" | ||
{ _get_access_token_and_update && _clear_line 1; } || return 1 | ||
printf "Access Token: %s\n" "${ACCESS_TOKEN}" | ||
else | ||
"${QUIET:-_print_center}" "normal" "Refresh Token not set" ", use ${0##*/} create to generate one." "=" | ||
exit 1 | ||
fi | ||
fi | ||
trap 'abnormal_exit="1"; exit' INT TERM | ||
trap '_cleanup' EXIT | ||
trap '' TSTP # ignore ctrl + z | ||
|
||
export MAIN_PID="$$" | ||
|
||
export API_URL="https://www.googleapis.com" | ||
export API_VERSION="v3" \ | ||
SCOPE="${API_URL}/auth/drive" \ | ||
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" \ | ||
TOKEN_URL="https://accounts.google.com/o/oauth2/token" | ||
|
||
# the credential functions require a config file to update, so just provide /dev/null if CONFIG variable is not exported | ||
export CONFIG="${CONFIG:-"/dev/null"}" | ||
_reload_config || return 1 | ||
|
||
case "${1}" in | ||
help) _usage ;; | ||
create) unset CLIENT_SECRET CLIENT_ID REFRESH_TOKEN ACCESS_TOKEN && CREATE_ACCOUNT="true" ;; | ||
add) unset REFRESH_TOKEN ACCESS_TOKEN && CREATE_ACCOUNT="true" ;; | ||
refresh) | ||
unset ACCESS_TOKEN | ||
[[ -z ${CLIENT_ID} ]] && printf "%s\n" "Missing CLIENT_ID variable, make sure to export to use refresh option." && _usage | ||
[[ -z ${CLIENT_SECRET} ]] && printf "%s\n" "Missing CLIENT_SECRET variable, make sure to export to use refresh option." && _usage | ||
[[ -z ${REFRESH_TOKEN} ]] && printf "%s\n" "Missing REFRESH_TOKEN variable, make sure to export to use refresh option." && _usage | ||
;; | ||
esac | ||
|
||
_check_account_credentials || exit 1 | ||
[[ -n ${CREATE_ACCOUNT} ]] && printf "Refresh Token: %s\n\n" "${REFRESH_TOKEN}" 1>&2 | ||
printf "Access Token: %s\n" "${ACCESS_TOKEN}" 1>&2 | ||
exit 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,132 +1,86 @@ | ||
#!/usr/bin/env sh | ||
# shellcheck source=/dev/null | ||
|
||
# A simple curl OAuth2 authenticator | ||
# | ||
# Usage: | ||
# ./google-oauth2.sh create - authenticates a user | ||
# ./google-oauth2.sh refresh <token> - gets a new token | ||
# | ||
# Set CLIENT_ID and CLIENT_SECRET and SCOPE | ||
# See SCOPES at https://developers.google.com/identity/protocols/oauth2/scopes#docsv1 | ||
|
||
set -o errexit -o noclobber | ||
|
||
_short_help() { | ||
printf " | ||
No valid arguments provided. | ||
_usage() { | ||
printf "%s\n" " | ||
A simple curl OAuth2 authenticator for google drive. Utilizing api v3. | ||
Usage: | ||
./%s create - authenticates a user. | ||
./%s refresh - gets a new access token. | ||
./${0##*/} create - authenticates a new user. | ||
./${0##*/} add - authenticates a new user but will use the client id and secret if available. If not, then same as create flag. | ||
./${0##*/} refresh - gets a new access token. Make sure CLIENT_SECRET, CLIENT_ID and REFRESH_TOKEN is exported as an environment variable or CONFIG | ||
./${0##*/} help - show this help. | ||
Make sure to export CONFIG as an environment variable if you want to use save new changes or want to use values from it. It should be in the format required by gupload. | ||
Use update as second argument to update the local config with the new REFRESH TOKEN. | ||
e.g: ./%s create/refresh update\n" "${0##*/}" "${0##*/}" "${0##*/}" | ||
Variable names - CLIENT_SECRET, CLIENT_ID and REFRESH_TOKEN | ||
You can also export CLIENT_SECRET, CLIENT_ID and REFRESH_TOKEN as an environment variable if you don't want to use above method." | ||
exit 0 | ||
} | ||
|
||
UTILS_FOLDER="${UTILS_FOLDER:-$(pwd)}" | ||
{ . "${UTILS_FOLDER}"/common-utils.sh && . "${UTILS_FOLDER}"/drive-utils.sh; } || { printf "Error: Unable to source util files.\n" && exit 1; } | ||
|
||
[ "${1}" = create ] || [ "${1}" = refresh ] || _short_help | ||
{ . "${UTILS_FOLDER}"/common-utils.bash && . "${UTILS_FOLDER}"/auth-utils.bash; } || { printf "Error: Unable to source util files.\n" && exit 1; } | ||
|
||
[ "${2}" = update ] || _update_config() { :; } | ||
[ $# = 0 ] && _usage | ||
|
||
_check_debug | ||
|
||
CLIENT_ID="" | ||
CLIENT_SECRET="" | ||
SCOPE="https://www.googleapis.com/auth/drive" | ||
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" | ||
TOKEN_URL="https://accounts.google.com/o/oauth2/token" | ||
|
||
INFO_PATH="${HOME}/.google-drive-upload" CONFIG_INFO="${INFO_PATH}/google-drive-upload.configpath" | ||
[ -f "${CONFIG_INFO}" ] && . "${CONFIG_INFO}" | ||
CONFIG="${CONFIG:-${HOME}/.googledrive.conf}" | ||
_cleanup() { | ||
# unhide the cursor if hidden | ||
[ -n "${SUPPORT_ANSI_ESCAPES}" ] && printf "\e[?25h\e[?7h" | ||
{ | ||
# grab all script children pids | ||
script_children_pids="$(ps --ppid="${MAIN_PID}" -o pid=)" | ||
|
||
[ -f "${CONFIG}" ] && . "${CONFIG}" | ||
# kill all grabbed children processes | ||
# shellcheck disable=SC2086 | ||
kill ${script_children_pids} 1>| /dev/null | ||
|
||
! [ -t 2 ] && [ -z "${CLIENT_ID:+${CLIENT_SECRET:+${REFRESH_TOKEN}}}" ] && { | ||
printf "%s\n" "Error: Script is not running in a terminal, cannot ask for credentials." | ||
printf "%s\n" "Add in config manually if terminal is not accessible. CLIENT_ID, CLIENT_SECRET and REFRESH_TOKEN is required." && return 1 | ||
export abnormal_exit && if [ -n "${abnormal_exit}" ]; then | ||
printf "\n\n%s\n" "Script exited manually." | ||
kill -9 -$$ & | ||
fi | ||
} 2>| /dev/null || : | ||
return 0 | ||
} | ||
|
||
_print_center "justify" "Checking credentials.." "-" | ||
|
||
# Following https://developers.google.com/identity/protocols/oauth2#size | ||
CLIENT_ID_REGEX='[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com' | ||
CLIENT_SECRET_REGEX='[0-9A-Za-z_-]+' | ||
AUTHORIZATION_CODE_REGEX='[0-9]/[0-9A-Za-z_-]+' # 256 bytes minus initial fixed 2 bytes | ||
|
||
until [ -n "${CLIENT_ID}" ] && [ -n "${CLIENT_ID_VALID}" ]; do | ||
[ -n "${CLIENT_ID}" ] && { | ||
if printf "%s\n" "${CLIENT_ID}" | grep -qE "${CLIENT_ID_REGEX}"; then | ||
[ -n "${client_id}" ] && _update_config CLIENT_ID "${CLIENT_ID}" "${CONFIG}" | ||
CLIENT_ID_VALID="true" && continue | ||
else | ||
{ [ -n "${client_id}" ] && message="- Try again"; } || message="in config ( ${CONFIG} )" | ||
"${QUIET:-_print_center}" "normal" " Invalid Client ID ${message} " "-" && unset CLIENT_ID client_id | ||
fi | ||
} | ||
[ -z "${client_id}" ] && printf "\n" && "${QUIET:-_print_center}" "normal" " Enter Client ID " "-" | ||
[ -n "${client_id}" ] && _clear_line 1 | ||
printf -- "-> " | ||
read -r CLIENT_ID && client_id=1 | ||
done | ||
|
||
until [ -n "${CLIENT_SECRET}" ] && [ -n "${CLIENT_SECRET_VALID}" ]; do | ||
[ -n "${CLIENT_SECRET}" ] && { | ||
if printf "%s\n" "${CLIENT_SECRET}" | grep -qE "${CLIENT_SECRET_REGEX}"; then | ||
[ -n "${client_secret}" ] && _update_config CLIENT_SECRET "${CLIENT_SECRET}" "${CONFIG}" | ||
CLIENT_SECRET_VALID="true" && continue | ||
else | ||
{ [ -n "${client_secret}" ] && message="- Try again"; } || message="in config ( ${CONFIG} )" | ||
"${QUIET:-_print_center}" "normal" " Invalid Client Secret ${message} " "-" && unset CLIENT_SECRET client_secret | ||
fi | ||
} | ||
[ -z "${client_secret}" ] && printf "\n" && "${QUIET:-_print_center}" "normal" " Enter Client Secret " "-" | ||
[ -n "${client_secret}" ] && _clear_line 1 | ||
printf -- "-> " | ||
read -r CLIENT_SECRET && client_secret=1 | ||
done | ||
|
||
_clear_line 1 | ||
|
||
if [ "${1}" = create ]; then | ||
printf "\n" && "${QUIET:-_print_center}" "normal" "Visit the below URL, tap on allow and then enter the code obtained" " " | ||
URL="https://accounts.google.com/o/oauth2/auth?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPE}&response_type=code&prompt=consent" | ||
printf "\n%s\n" "${URL}" | ||
until [ -n "${AUTHORIZATION_CODE}" ] && [ -n "${AUTHORIZATION_CODE_VALID}" ]; do | ||
[ -n "${AUTHORIZATION_CODE}" ] && { | ||
if printf "%s\n" "${AUTHORIZATION_CODE}" | grep -qE "${AUTHORIZATION_CODE_REGEX}"; then | ||
AUTHORIZATION_CODE_VALID="true" && continue | ||
else | ||
"${QUIET:-_print_center}" "normal" " Invalid CODE given, try again.. " "-" && unset AUTHORIZATION_CODE authorization_code | ||
fi | ||
} | ||
{ [ -z "${authorization_code}" ] && printf "\n" && "${QUIET:-_print_center}" "normal" " Enter the authorization code " "-"; } || _clear_line 1 | ||
printf -- "-> " | ||
read -r AUTHORIZATION_CODE && authorization_code=1 | ||
done | ||
RESPONSE="$(curl --compressed "${CURL_PROGRESS}" -X POST \ | ||
--data "code=${AUTHORIZATION_CODE}&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&redirect_uri=${REDIRECT_URI}&grant_type=authorization_code" "${TOKEN_URL}")" || : | ||
_clear_line 1 1>&2 | ||
|
||
REFRESH_TOKEN="$(printf "%s\n" "${RESPONSE}" | _json_value refresh_token 1 1 || :)" | ||
if _get_access_token_and_update "${RESPONSE}"; then | ||
_update_config REFRESH_TOKEN "${REFRESH_TOKEN}" "${CONFIG}" | ||
printf "Access Token: %s\n" "${ACCESS_TOKEN}" | ||
printf "Refresh Token: %s\n" "${REFRESH_TOKEN}" | ||
else | ||
return 1 | ||
fi | ||
elif [ "${1}" = refresh ]; then | ||
if [ -n "${REFRESH_TOKEN}" ]; then | ||
"${QUIET:-_print_center}" "justify" "Required credentials set." "=" | ||
{ _get_access_token_and_update && _clear_line 1; } || return 1 | ||
printf "Access Token: %s\n" "${ACCESS_TOKEN}" | ||
else | ||
"${QUIET:-_print_center}" "normal" "Refresh Token not set" ", use ${0##*/} create to generate one." "=" | ||
exit 1 | ||
fi | ||
fi | ||
trap 'abnormal_exit="1" ; exit' INT TERM | ||
trap '_cleanup' EXIT | ||
trap '' TSTP # ignore ctrl + z | ||
|
||
export MAIN_PID="$$" | ||
|
||
unset ROOT_FOLDER ROOT_FOLDER_NAME CLIENT_ID CLIENT_SECRET REFRESH_TOKEN ACCESS_TOKEN | ||
export API_URL="https://www.googleapis.com" | ||
export API_VERSION="v3" \ | ||
SCOPE="${API_URL}/auth/drive" \ | ||
REDIRECT_URI="urn:ietf:wg:oauth:2.0:oob" \ | ||
TOKEN_URL="https://accounts.google.com/o/oauth2/token" | ||
|
||
# the credential functions require a config file to update, so just provide /dev/null if CONFIG variable is not exported | ||
export CONFIG="${CONFIG:-"/dev/null"}" | ||
_reload_config || return 1 | ||
|
||
case "${1}" in | ||
help) _usage ;; | ||
create) unset CLIENT_SECRET CLIENT_ID REFRESH_TOKEN ACCESS_TOKEN && CREATE_ACCOUNT="true" ;; | ||
add) unset REFRESH_TOKEN ACCESS_TOKEN && CREATE_ACCOUNT="true" ;; | ||
refresh) | ||
unset ACCESS_TOKEN | ||
[ -z "${CLIENT_ID}" ] && printf "%s\n" "Missing CLIENT_ID variable, make sure to export to use refresh option." && _usage | ||
[ -z "${CLIENT_SECRET}" ] && printf "%s\n" "Missing CLIENT_SECRET variable, make sure to export to use refresh option." && _usage | ||
[ -z "${REFRESH_TOKEN}" ] && printf "%s\n" "Missing REFRESH_TOKEN variable, make sure to export to use refresh option." && _usage | ||
;; | ||
esac | ||
|
||
_check_account_credentials || exit 1 | ||
[ -n "${CREATE_ACCOUNT}" ] && printf "Refresh Token: %s\n\n" "${REFRESH_TOKEN}" 1>&2 | ||
printf "Access Token: %s\n" "${ACCESS_TOKEN}" 1>&2 | ||
exit 0 |