tesla-php-client
tesla-php-client copied to clipboard
New Authentication
I am using your tesla-api class. Because the new Authentication of Tesla i had to make some changes: Its not so nice but it works:
public function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
public function getAccessToken(string $username, string $password)
{
###Step 1: Obtain the login page
$code_verifier = substr(hash('sha512', mt_rand()), 0, 86);
$state = $this->base64url_encode(substr(hash('sha256', mt_rand()), 0, 12));
$code_challenge = $this->base64url_encode($code_verifier);
$data =[
'client_id' => 'ownerapi',
'code_challenge' => $code_challenge,
'code_challenge_method' => 'S256',
'redirect_uri' => 'https://auth.tesla.com/void/callback',
'response_type' => 'code',
'scope' => 'openid email offline_access',
'state' => $state,
];
$GetUrl = $this->accessUrl.'?'.http_build_query ($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $GetUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_HEADER, 1);
$apiResult = curl_exec($ch);
curl_close($ch);
$dom = new DomDocument();
@ $dom->loadHTML($apiResult);
$child_elements = $dom->getElementsByTagName('input'); //DOMNodeList
foreach( $child_elements as $h2 ) {
$hiddeninputs[$h2->getAttribute('name')]=$h2->getAttribute('value');
}
$headers = [];
$output = rtrim($apiResult);
$data = explode("\n",$output);
$headers['status'] = $data[0];
array_shift($data);
foreach($data as $part){
//some headers will contain ":" character (Location for example), and the part after ":" will be lost, Thanks to @Emanuele
$middle = explode(":",$part,2);
//Supress warning message if $middle[1] does not exist, Thanks to @crayons
if ( !isset($middle[1]) ) { $middle[1] = null; }
$headers[trim($middle[0])] = trim($middle[1]);
}
$cookie = $headers['set-cookie'];
###Step 2: Obtain an authorization code
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $GetUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded','Cookie: '.$cookie));
$postData = array(
'_csrf' => $hiddeninputs['_csrf'],
'_phase' => $hiddeninputs['_phase'],
'_process' => $hiddeninputs['_process'],
'transaction_id' => $hiddeninputs['transaction_id'],
'cancel' =>$hiddeninputs['cancel'],
'identity' => $username,
'credential' => $password
);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
$apiResult = curl_exec($ch);
curl_close($ch);
$code= explode('&',explode('https://auth.tesla.com/void/callback?code=',$apiResult)[1])[0];
###Step 3: Exchange authorization code for bearer token
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->tokenUrlNew);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json',
'User-Agent: Mozilla/5.0 (Linux; Android 9.0.0; VS985 4G Build/LRX21Y; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36',
'X-Tesla-User-Agent: TeslaApp/3.4.4-350/fad4a582e/android/9.0.0',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'grant_type' => 'authorization_code',
'client_id' => 'ownerapi',
'code' => $code,
'code_verifier' => $code_verifier,
'redirect_uri' => 'https://auth.tesla.com/void/callback'
]));
$apiResult = curl_exec($ch);
curl_close($ch);
$apiResultJson = json_decode($apiResult, true);
$BearerToken = $apiResultJson['access_token'];
$RefreshToken = $apiResultJson['refresh_token'];
###Step 4: Exchange bearer token for access token
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->tokenUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer '.$BearerToken,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'client_id' => getenv('TESLA_CLIENT_ID'),
'client_secret' => getenv('TESLA_CLIENT_SECRET'),
]));
$apiResult = curl_exec($ch);
curl_close($ch);
$apiResultJson = json_decode($apiResult, true);
$apiResultJson['refresh_token']=$RefreshToken;
#print_r($apiResultJson);exit;
$this->accessToken = $apiResultJson['access_token'];
return $apiResultJson;
}
public function refreshAccessToken(string $refreshToken)
{
$tesla = new Tesla();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://pastebin.com/raw/pS7Z6yyP');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
$api = explode(PHP_EOL,$result);
$id=explode('=',$api[0]);
$secret=explode('=',$api[1]);
$tesla->setClientId(trim($id[1]));
$tesla->setClientSecret(trim($secret[1]));
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->tokenUrlNew);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json',
'User-Agent: Mozilla/5.0 (Linux; Android 9.0.0; VS985 4G Build/LRX21Y; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36',
'X-Tesla-User-Agent: TeslaApp/3.4.4-350/fad4a582e/android/9.0.0',
]);
#print 'XX '.getenv('TESLA_CLIENT_ID');exit;
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'grant_type' => 'refresh_token',
'client_id' => 'ownerapi',
'client_secret' => getenv('TESLA_CLIENT_SECRET'),
'refresh_token' => $refreshToken,
'scope' => 'openid email offline_access'
]));
$apiResult = curl_exec($ch);
$apiResultJson = json_decode($apiResult, true);
curl_close($ch);
#print_r($apiResult);exit;
$apiResultJson = json_decode($apiResult, true);
$BearerToken = $apiResultJson['access_token'];
$RefreshToken = $apiResultJson['refresh_token'];
###Step 4: Exchange bearer token for access token
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->tokenUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer '.$BearerToken,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'client_id' => getenv('TESLA_CLIENT_ID'),
'client_secret' => getenv('TESLA_CLIENT_SECRET'),
]));
$apiResult = curl_exec($ch);
curl_close($ch);
$apiResultJson = json_decode($apiResult, true);
$apiResultJson['refresh_token']=$RefreshToken;
#print_r($apiResultJson);exit;
$this->accessToken = $apiResultJson['access_token'];
return $apiResultJson;
}
Please go here for the latest code: https://github.com/timdorr/tesla-api/discussions/296
@ralfonso4 Hey there, would you be willing to submit a PR to implement this? Thanks for your contributions.
I would encourage switching back to the original usage of environmental variables as utilizing the Pastebin URL could be utilized as a MITM attack vector (and also breaks functionality if the service goes down).
Edit: Will note this does not provide support for accounts with 2FA nor handles errors relating to it.