#!/usr/bin/env bash set -euo pipefail script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$script_dir/lib/common.sh" repo_root="$(backup_repo_root)" env_file="${WORKS_DRIVE_ENV_FILE:-$repo_root/.env}" if [[ -f "$env_file" ]]; then set -a # shellcheck source=/dev/null source "$env_file" set +a fi token_grant="${WORKS_DRIVE_TOKEN_GRANT:-refresh-token}" token_url="${WORKS_ADMIN_OAUTH_TOKEN_URL:-https://auth.worksmobile.com/oauth2/v2.0/token}" authorize_url="${WORKS_DRIVE_OAUTH_AUTHORIZE_URL:-https://auth.worksmobile.com/oauth2/v2.0/authorize}" client_id="${WORKS_DRIVE_OAUTH_CLIENT_ID:-}" client_secret="${WORKS_DRIVE_OAUTH_CLIENT_SECRET:-}" redirect_uri="${WORKS_DRIVE_OAUTH_REDIRECT_URI:-}" scope="${WORKS_DRIVE_OAUTH_SCOPE:-file}" curl_bin="${WORKS_DRIVE_CURL_BIN:-curl}" set_auth_mode="${WORKS_DRIVE_TOKEN_SET_AUTH_MODE:-true}" backup_require_command jq urlencode() { jq -nr --arg value "$1" '$value|@uri' } redact_for_log() { sed -E 's/("(access_token|refresh_token|client_secret)"[[:space:]]*:[[:space:]]*)"[^"]*"/\1"REDACTED"/Ig' } split_curl_response() { local response="$1" local __body_var="$2" local __status_var="$3" local status local body status="$(tail -n 1 <<<"$response")" if [[ "$status" =~ ^[0-9][0-9][0-9]$ ]]; then body="$(sed '$d' <<<"$response")" else status="200" body="$response" fi printf -v "$__body_var" '%s' "$body" printf -v "$__status_var" '%s' "$status" } require_oauth_client() { [[ -n "$client_id" ]] || backup_die "WORKS_DRIVE_OAUTH_CLIENT_ID is required." [[ -n "$client_secret" ]] || backup_die "WORKS_DRIVE_OAUTH_CLIENT_SECRET is required." } extract_code_from_callback_url() { local raw_url="$1" local query local param local code="" local decoded query="${raw_url#*\?}" query="${query%%#*}" IFS='&' read -r -a params <<<"$query" for param in "${params[@]}"; do if [[ "$param" == code=* ]]; then code="${param#code=}" break fi done decoded="${code//+/ }" printf '%b' "${decoded//%/\\x}" } write_env_value() { local key="$1" local value="$2" local tmp_file local env_uid="" local env_gid="" local env_mode="" [[ -n "$key" ]] || backup_die "env key is required." [[ "$value" != *$'\n'* ]] || backup_die "env value for $key must not contain a newline." mkdir -p "$(dirname "$env_file")" tmp_file="$(mktemp "$env_file.tmp.XXXXXX")" if [[ -f "$env_file" ]]; then env_uid="$(stat -c '%u' "$env_file")" env_gid="$(stat -c '%g' "$env_file")" env_mode="$(stat -c '%a' "$env_file")" awk -v key="$key" -v value="$value" ' BEGIN { written = 0 } $0 ~ "^[[:space:]]*" key "=" { print key "=" value written = 1 next } { print } END { if (!written) { print key "=" value } } ' "$env_file" >"$tmp_file" else printf '%s=%s\n' "$key" "$value" >"$tmp_file" fi if [[ -n "$env_mode" ]]; then chmod "$env_mode" "$tmp_file" else chmod 600 "$tmp_file" fi if [[ -n "$env_uid" && -n "$env_gid" ]]; then chown "$env_uid:$env_gid" "$tmp_file" 2>/dev/null || true fi mv "$tmp_file" "$env_file" } print_authorize_url() { [[ -n "$client_id" ]] || backup_die "WORKS_DRIVE_OAUTH_CLIENT_ID is required." [[ -n "$redirect_uri" ]] || backup_die "WORKS_DRIVE_OAUTH_REDIRECT_URI is required." printf '%s?client_id=%s&redirect_uri=%s&scope=%s&response_type=code&state=%s\n' \ "$authorize_url" \ "$(urlencode "$client_id")" \ "$(urlencode "$redirect_uri")" \ "$(urlencode "$scope")" \ "$(urlencode "${WORKS_DRIVE_OAUTH_STATE:-baron-sso-backup}")" } request_refresh_token_grant() { require_oauth_client local refresh_token="${WORKS_DRIVE_OAUTH_REFRESH_TOKEN:-}" local response local response_body local http_status [[ -n "$refresh_token" ]] || backup_die "WORKS_DRIVE_OAUTH_REFRESH_TOKEN is required for refresh-token grant." backup_require_command "$curl_bin" response="$("$curl_bin" -sS -w $'\n%{http_code}' -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "refresh_token=$refresh_token" \ --data-urlencode "client_id=$client_id" \ --data-urlencode "client_secret=$client_secret" \ "$token_url")" split_curl_response "$response" response_body http_status if [[ "$http_status" -lt 200 || "$http_status" -ge 300 ]]; then backup_die "WORKS refresh token request failed (HTTP $http_status): $(printf '%s' "$response_body" | redact_for_log)" fi printf '%s\n' "$response_body" } request_authorization_code_grant() { require_oauth_client local code="${WORKS_DRIVE_AUTH_CODE:-}" local response local response_body local http_status if [[ -z "$code" && -n "${WORKS_DRIVE_AUTH_CALLBACK_URL:-}" ]]; then code="$(extract_code_from_callback_url "$WORKS_DRIVE_AUTH_CALLBACK_URL")" fi [[ -n "$code" ]] || backup_die "WORKS_DRIVE_AUTH_CODE or WORKS_DRIVE_AUTH_CALLBACK_URL is required for authorization-code grant." [[ -n "$redirect_uri" ]] || backup_die "WORKS_DRIVE_OAUTH_REDIRECT_URI is required for authorization-code grant." backup_require_command "$curl_bin" response="$("$curl_bin" -sS -w $'\n%{http_code}' -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=$code" \ --data-urlencode "client_id=$client_id" \ --data-urlencode "client_secret=$client_secret" \ --data-urlencode "redirect_uri=$redirect_uri" \ "$token_url")" split_curl_response "$response" response_body http_status if [[ "$http_status" -lt 200 || "$http_status" -ge 300 ]]; then backup_die "WORKS authorization code token request failed (HTTP $http_status): $(printf '%s' "$response_body" | redact_for_log)" fi printf '%s\n' "$response_body" } persist_token_response() { local response_body="$1" local refresh_token local expires_in expires_in="$(jq -r '.expires_in // empty' <<<"$response_body")" refresh_token="$(jq -r '.refresh_token // empty' <<<"$response_body")" if [[ -n "$refresh_token" ]]; then write_env_value WORKS_DRIVE_OAUTH_REFRESH_TOKEN "$refresh_token" backup_log "WORKS Drive refresh token updated: $env_file" else backup_log "WORKS token refresh succeeded without a rotated refresh token." fi if [[ "$set_auth_mode" == "true" ]]; then write_env_value WORKS_DRIVE_AUTH_MODE refresh-token fi if [[ -n "$expires_in" ]]; then backup_log "WORKS Drive access token issued. expires_in=$expires_in" else backup_log "WORKS Drive access token issued." fi } case "$token_grant" in print-authorize-url) print_authorize_url ;; refresh-token) persist_token_response "$(request_refresh_token_grant)" ;; authorization-code) persist_token_response "$(request_authorization_code_grant)" ;; *) backup_die "unknown WORKS_DRIVE_TOKEN_GRANT: $token_grant. Expected refresh-token, authorization-code, or print-authorize-url." ;; esac