> uploadtext_

v1.0.0 - Secure text sharing node

OAuth 2.0 Client Credentials Grant Flow for Server-to-Server Authentication (Python)

Owner: SnippetBot Created: 2026-09-08 00:00:54 Size: 6.30 KB Expires: Never
[ RAW ] [ NEW ]
tty1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
import requests
import os
import time
from datetime import datetime, timedelta

class OAuth2Client:
    def __init__(self, token_url, client_id, client_secret, scope=None):
        self.token_url = token_url
        self.client_id = client_id
        self.client_secret = client_secret
        self.scope = scope
        self.access_token = None
        self.expires_at = None # datetime object when token expires

    def _get_new_token(self):
        print("Requesting new OAuth2 access token...")
        headers = {
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = {
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_secret": self.client_secret
        }
        if self.scope:
            data["scope"] = self.scope

        try:
            response = requests.post(self.token_url, headers=headers, data=data)
            response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)
            token_data = response.json()

            self.access_token = token_data.get("access_token")
            expires_in = token_data.get("expires_in") # seconds until expiration

            if self.access_token and expires_in:
                # Set expiration time a bit before actual expiration for safety (e.g., 60 seconds)
                self.expires_at = datetime.now() + timedelta(seconds=expires_in - 60)
                print(f"Successfully obtained new token. Expires at: {self.expires_at}")
            else:
                raise ValueError("Access token or expiration time missing from response.")

        except requests.exceptions.RequestException as e:
            print(f"Error obtaining OAuth2 token: {e}")
            self.access_token = None
            self.expires_at = None
            raise

    def get_access_token(self):
        # Check if token is still valid or needs refreshing
        if not self.access_token or (self.expires_at and datetime.now() >= self.expires_at):
            self._get_new_token()
        return self.access_token

    def make_api_request(self, api_url, method="GET", **kwargs):
        token = self.get_access_token()
        if not token:
            raise Exception("Could not obtain access token.")

        headers = kwargs.pop("headers", {})
        headers["Authorization"] = f"Bearer {token}"
        
        try:
            response = requests.request(method, api_url, headers=headers, **kwargs)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 401: # Token might be invalid, try to refresh and retry
                print("API request failed with 401. Attempting to refresh token and retry...")
                self.access_token = None # Invalidate current token
                token = self.get_access_token() # Get new token
                if not token:
                    raise Exception("Could not refresh token after 401.")
                headers["Authorization"] = f"Bearer {token}"
                response = requests.request(method, api_url, headers=headers, **kwargs) # Retry request
                response.raise_for_status()
                return response.json()
            raise # Re-raise other HTTP errors
        except requests.exceptions.RequestException as e:
            print(f"API request failed: {e}")
            raise

# --- Usage Example ---
if __name__ == "__main__":
    # --- Configuration (ideally from environment variables) ---
    TOKEN_URL = os.getenv("OAUTH_TOKEN_URL", "https://your-oauth-server.com/oauth/token")
    CLIENT_ID = os.getenv("OAUTH_CLIENT_ID", "your_client_id")
    CLIENT_SECRET = os.getenv("OAUTH_CLIENT_SECRET", "your_client_secret")
    API_BASE_URL = os.getenv("API_BASE_URL", "https://your-resource-server.com/api/v1")
    API_SCOPE = "read write" # Optional scope

    if "your_client_id" in CLIENT_ID or "your-oauth-server" in TOKEN_URL:
         print("WARNING: Please configure OAUTH_TOKEN_URL, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, and API_BASE_URL environment variables or update values in script.")
         # Exit or use dummy values for demonstration if not configured
         TOKEN_URL = "https://example.com/oauth/token" # Placeholder
         CLIENT_ID = "test_client" # Placeholder
         CLIENT_SECRET = "test_secret" # Placeholder
         API_BASE_URL = "https://example.com/api/v1" # Placeholder


    client = OAuth2Client(TOKEN_URL, CLIENT_ID, CLIENT_SECRET, scope=API_SCOPE)

    try:
        # First API call - token will be obtained
        print("
--- Making first API call ---")
        data = client.make_api_request(f"{API_BASE_URL}/data")
        print(f"API Response (first call): {data}")

        # Simulate token expiration (for testing, do not do this in production)
        # client.expires_at = datetime.now() - timedelta(seconds=10) 
        
        # Second API call - if token is still valid, it will be reused
        print("
--- Making second API call (should reuse token) ---")
        data = client.make_api_request(f"{API_BASE_URL}/another-endpoint")
        print(f"API Response (second call): {data}")

        # Wait until just before theoretical token expiration to force refresh
        print("
--- Waiting to force token refresh ---")
        # In a real scenario, client.expires_at would be set correctly.
        # Here, we'll simulate waiting by making expires_at expire soon
        if client.expires_at:
            wait_time = (client.expires_at - datetime.now()).total_seconds() + 5 # Wait 5 seconds past expiration
            if wait_time > 0:
                print(f"Waiting for {int(wait_time)} seconds to force token refresh...")
                time.sleep(wait_time)
            else:
                print("Token already expired or about to expire, will refresh on next call.")
        else:
             print("Cannot determine token expiration, assuming it will be refreshed if needed.")
             time.sleep(2) # Give it some time if expires_at was not set


        # Third API call - token should be refreshed
        print("
--- Making third API call (should trigger token refresh) ---")
        data = client.make_api_request(f"{API_BASE_URL}/status")
        print(f"API Response (third call, after refresh): {data}")

    except Exception as e:
        print(f"An error occurred during API interaction: {e}")