timdorr / tesla-api

🚘 A Ruby gem and unofficial documentation of Tesla's JSON API for the Model S, 3, X, and Y.
https://tesla-api.timdorr.com/
MIT License
1.98k stars 532 forks source link

HTTP Error: 403 Client Error: Forbidden for url: https://auth.tesla.com/oauth2/v3/authorize #740

Closed eliaspfeffer closed 11 months ago

eliaspfeffer commented 11 months ago

Im getting this terminal error after writing the code for Step 2:

Step 1 - Login page obtained successfully.
Login data saved to file.
HTTP Error: 403 Client Error: Forbidden for url: https://auth.tesla.com/oauth2/v3/authorize
Please check your credentials and other parameters.

I can't figure out whats wrong. I've written the following code, following the documentation

What am I doing wrong? Have been troubleshooting since 2 days Python Version 23.1.2 (on windows if important)

import requests
from bs4 import BeautifulSoup
import time  # Add the 'time' module import here
import secrets
import hashlib
import base64
import Keys

# Function to generate a random string of a given length
def random_string(length):
    alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    return ''.join(secrets.choice(alphabet) for _ in range(length))

# Function to create code challenge from code verifier
def create_code_challenge(code_verifier):
    sha256_hash = hashlib.sha256(code_verifier.encode()).digest()
    code_challenge = base64.urlsafe_b64encode(sha256_hash).rstrip(b'=').decode()
    return code_challenge

# Step 1: Obtain the login page
login_url = "https://auth.tesla.com/oauth2/v3/authorize"
redirect_url = "https://auth.tesla.com/void/callback"
code_verifier = random_string(86)
code_challenge = create_code_challenge(code_verifier)
state = random_string(16)

params = {
    "client_id": "ownerapi",
    "code_challenge": code_challenge,
    "code_challenge_method": "S256",
    "redirect_uri": redirect_url,
    "response_type": "code",
    "scope": "openid email offline_access",
    "state": state,
    "login_hint": Keys.tesla_email
}

headers = {
    "User-Agent": "MyApp/1.0"  # Replace this with a non-browser-like User-Agent
}

try:
    # Make the GET request to obtain the login page
    response = requests.get(login_url, params=params, headers=headers)
    response.raise_for_status()

    # Introduce a delay after making the GET request to give the server time to respond
    time.sleep(2)  # You can adjust the delay time as needed (e.g., 2 seconds)

    # Parse the HTML response to extract hidden input fields
    soup = BeautifulSoup(response.content, 'html.parser')

    # Extract values of the hidden input fields
    csrf_token_value = soup.find('input', {'name': '_csrf'})['value']
    phase_token_value = soup.find('input', {'name': '_phase'})['value']
    cancel_token_value = soup.find('input', {'name': 'cancel'})['value']
    transaction_id_value = soup.find('input', {'name': 'transaction_id'})['value']

    # Store the session ID cookie from the response
    session_cookie = response.cookies.get('tesla_auth_session')

    print("Step 1 - Login page obtained successfully.")

    # Save the values to a file
    with open('tesla_login_data.txt', 'w', encoding='utf-8') as file:
        file.write(f"{csrf_token_value}\n")
        file.write(f"{phase_token_value}\n")
        file.write(f"{cancel_token_value}\n")
        file.write(f"{transaction_id_value}\n")
        file.write(f"{session_cookie}\n")

    print("Login data saved to file.")

    # Continue with the rest of your code for Step 1...
except requests.exceptions.RequestException as e:
    print(f"Step 1 - Error occurred: {e}")

# ... (Code 2: Step 2 - Obtaining an authorization code) ...

# Read the values from the file
with open('tesla_login_data.txt', 'r', encoding='utf-8') as file:
    csrf_token_value = file.readline().strip()
    phase_token_value = file.readline().strip()
    cancel_token_value = file.readline().strip()
    transaction_id_value = file.readline().strip()
    session_cookie = file.readline().strip()

# Continue with the rest of Code 2, using the values obtained from the file
# Step 2: Obtain an authorization code
authorize_url = "https://auth.tesla.com/oauth2/v3/authorize"

# Request parameters for the POST request
post_data = {
    "client_id": "ownerapi",
    "code_challenge": code_challenge,
    "code_challenge_method": "S256",
    "redirect_uri": redirect_url,
    "response_type": "code",
    "scope": "openid email offline_access",
    "state": state,
    "identity": Keys.tesla_email,
    "credential": Keys.tesla_password
}

# Add the session cookie to the headers for the POST request
headers['Cookie'] = f'tesla_auth_session={session_cookie}'

# Make the POST request to obtain the authorization code
response = requests.post(authorize_url, data=post_data, headers=headers)

# Check for HTTP errors
try:
    response.raise_for_status()
except requests.exceptions.HTTPError as e:
    print(f"HTTP Error: {e}")
    print("Please check your credentials and other parameters.")
    exit()

# Check the response for a 302 redirect
if response.status_code == 302:
    # Extract the authorization code from the Location header
    location_header = response.headers.get('Location')
    if location_header:
        # Extract the 'code' query parameter from the URL
        import urllib.parse
        parsed_url = urllib.parse.urlparse(location_header)
        query_params = urllib.parse.parse_qs(parsed_url.query)
        authorization_code = query_params.get('code', [''])[0]

        if authorization_code:
            print("Authorization code:", authorization_code)
        else:
            print("Authorization code not found in the response.")
    else:
        print("Location header not found in the response.")
else:
    print("Unexpected response:", response.status_code, response.text)