Skip to content

Instantly share code, notes, and snippets.

@ivorpad
Created July 25, 2024 16:48
Show Gist options
  • Select an option

  • Save ivorpad/d0e427438a6d94dd9593dc24d1053f5f to your computer and use it in GitHub Desktop.

Select an option

Save ivorpad/d0e427438a6d94dd9593dc24d1053f5f to your computer and use it in GitHub Desktop.

Revisions

  1. ivorpad created this gist Jul 25, 2024.
    160 changes: 160 additions & 0 deletions xero_oauth.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,160 @@
    #!/bin/bash

    source ./send_email.sh

    # OAuth 2.0 configuration
    CLIENT_ID=""
    CLIENT_SECRET=""
    REDIRECT_URI="http://localhost:3000/api/xoauth/callback"
    AUTH_URL="https://login.xero.com/identity/connect/authorize"
    TOKEN_URL="https://identity.xero.com/connect/token"
    SCOPES="openid profile email accounting.attachments.read accounting.budgets.read accounting.contacts.read accounting.journals.read accounting.reports.read accounting.reports.tenninetynine.read accounting.settings.read accounting.transactions.read assets.read offline_access"

    REDIS_URL=""

    save_tokens_to_redis() {
    local tokens="$1"
    /usr/local/bin/redis-cli -u "$REDIS_URL" SET xero_tokens "$tokens"
    }

    save_state_to_redis() {
    local state="$1"
    /usr/local/bin/redis-cli -u "$REDIS_URL" SET xero_state:$state "$state"
    }

    get_tokens_from_redis() {
    /usr/local/bin/redis-cli -u "$REDIS_URL" GET xero_tokens
    }

    get_state_from_redis() {
    local state="$1"
    /usr/local/bin/redis-cli -u "$REDIS_URL" GET xero_state:$state
    }

    generate_auth_url() {
    STATE=$(openssl rand -hex 16)
    AUTH_REQUEST_URL="${AUTH_URL}?response_type=code&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&scope=${SCOPES}&state=${STATE}"
    save_state_to_redis "$STATE"
    echo "$AUTH_REQUEST_URL"
    }

    refresh_token() {
    local refresh_token="$1"
    TOKEN_RESPONSE=$(curl -s -X POST ${TOKEN_URL} \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=refresh_token" \
    -d "refresh_token=${refresh_token}" \
    -d "client_id=${CLIENT_ID}" \
    -d "client_secret=${CLIENT_SECRET}")

    if echo "$TOKEN_RESPONSE" | jq -e '.access_token' > /dev/null; then
    echo "Token refreshed successfully."
    save_tokens_to_redis "$TOKEN_RESPONSE"
    echo "New tokens saved to Redis."
    else
    echo "Error refreshing token. Response:"
    echo "$TOKEN_RESPONSE"
    AUTH_URL=$(generate_auth_url)
    AUTH_URL=$(echo "$AUTH_URL" | sed 's/^OK[[:space:]]*//' | tr -d '\n')
    TEXT_MESSAGE="Token refresh failed. Please re-authorize: $AUTH_URL"
    HTML_MESSAGE="Token refresh failed. Please <a href=\"$AUTH_URL\">re-authorize</a>"
    send_email "$TEXT_MESSAGE" "$HTML_MESSAGE"
    exit 1
    fi
    }

    exchange_code_for_tokens() {
    local auth_code="$1"
    TOKEN_RESPONSE=$(curl -s -X POST ${TOKEN_URL} \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=authorization_code" \
    -d "code=${auth_code}" \
    -d "redirect_uri=${REDIRECT_URI}" \
    -d "client_id=${CLIENT_ID}" \
    -d "client_secret=${CLIENT_SECRET}")

    if echo "$TOKEN_RESPONSE" | jq -e '.access_token' > /dev/null; then
    echo "Authorization code exchanged successfully."
    save_tokens_to_redis "$TOKEN_RESPONSE"
    echo "Tokens saved to Redis."
    else
    echo "Error exchanging authorization code. Response:"
    echo "$TOKEN_RESPONSE"
    exit 1
    fi
    }

    case "$1" in
    --refresh)
    echo "Refreshing token..."
    STORED_TOKENS=$(get_tokens_from_redis)
    REFRESH_TOKEN=$(echo $STORED_TOKENS | jq -r '.refresh_token')
    if [ -z "$REFRESH_TOKEN" ]; then
    echo "Error: Refresh token not found in Redis"
    AUTH_URL=$(generate_auth_url)
    AUTH_URL=$(echo "$AUTH_URL" | sed 's/^OK[[:space:]]*//' | tr -d '\n')
    TEXT_MESSAGE="Refresh token not found in Redis: $REDIS_URL. Please re-authorize: $AUTH_URL"
    HTML_MESSAGE="Refresh token not found in Redis: $REDIS_URL. Please <a href=\"$AUTH_URL\">re-authorize</a>"
    send_email "$TEXT_MESSAGE" "$HTML_MESSAGE"
    exit 1
    fi
    refresh_token "$REFRESH_TOKEN"
    ;;
    --code)
    if [ -z "$2" ]; then
    echo "Error: Authorization code not provided"
    exit 1
    fi
    echo "Exchanging authorization code for tokens..."
    exchange_code_for_tokens "$2"
    ;;
    "")
    # Generate a random state value
    STATE=$(openssl rand -hex 16)
    # Construct the authorization URL
    AUTH_REQUEST_URL=$(generate_auth_url)
    echo "Please visit this URL in a browser on your local machine to authorize the application:"
    echo $AUTH_REQUEST_URL
    echo
    echo "After authorization, you will be redirected to a URL starting with $REDIRECT_URI"
    echo "Look for the 'code' parameter in this URL."
    echo
    # Wait for the authorization code
    read -p "Enter the authorization code from the redirect URL: " AUTH_CODE
    exchange_code_for_tokens "$AUTH_CODE"
    ;;
    *)
    echo "Usage: $0 [--refresh | --code <authorization_code>]"
    exit 1
    ;;
    esac

    # Use the new access token to update configuration
    STORED_TOKENS=$(get_tokens_from_redis)
    NEW_ACCESS_TOKEN=$(echo $STORED_TOKENS | jq -r '.access_token')

    CURL_RESPONSE=$(curl --request PATCH \
    --url 'http://localhost:8000/api/public/v1/sources/<source_id>' \
    --header 'accept: application/json' \
    --header 'authorization: Basic base64(username:password)' \
    --header 'Content-Type: application/json' \
    --data "{
    \"configuration\": {
    \"credentials\": {
    \"access_token\": \"$NEW_ACCESS_TOKEN\"
    }
    }
    }")

    echo "CURL Response:"
    echo "$CURL_RESPONSE"

    if echo "$CURL_RESPONSE" | jq -e '.sourceId' > /dev/null; then
    echo "Configuration updated successfully."
    echo "Updated source details:"
    echo "$CURL_RESPONSE" | jq '.'
    else
    echo "Error updating configuration. Response:"
    echo "$CURL_RESPONSE"
    send_email "Error updating configuration: $CURL_RESPONSE" "Error updating configuration: $CURL_RESPONSE"
    fi