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.99k stars 531 forks source link

MFA support #215

Closed rawmean closed 3 years ago

rawmean commented 4 years ago

Tesla app now uses a true oAuth2 protocol. specifically, the user no longer enters their credentials directly to the app; instead the user enters their credentials to a Tesla website directly.

This is the URL that is used to initiate oAuth: https://auth.tesla.com/oauth2/v3/authorize?client_id=ownerapi&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fauth.tesla.com%2Fvoid%2Fcallback&locale=en&prompt=login&response_type=code&scope=email&state=xlni7Qb4ON540kwGBopz

The problem is that the redirect_uri is set to "https://auth.tesla.com/void/callback" and changing that will cause http 400 error.

Do you have plans to support the new oAuth2. Thanks!

timdorr commented 4 years ago

I presume this has to do with the MFA options coming down the pipe.

We'll definitely need to update the docs. Presumably the older auth method will continue to work (it's also OAuth 2.0, just with a password grant).

timdorr commented 3 years ago

Looks like they just flipped the switch. I turned it on, so I'm going to poke around for a bit and see what's up.

rawmean commented 3 years ago

Yes, but the old authentication endpoints that use email/password still work. Strange

timdorr commented 3 years ago

Yup, they don't appear to respect the MFA requirement yet:

I, [2020-10-06T15:55:05.999917 #97301]  INFO -- request: POST https://owner-api.teslamotors.com/oauth/token
I, [2020-10-06T15:55:05.999982 #97301]  INFO -- request: User-Agent: "github.com/timdorr/tesla-api v:3.0.6"
I, [2020-10-06T15:55:06.618454 #97301]  INFO -- response: Status 200
timdorr commented 3 years ago

Ah, I see why. MFA is being implemented on their separate SSO server. The oAuth client ID for that server? "ownerapi" So, the Owner API is basically a client of the SSO OAuth.

I would expect that direct auth to the Owner API will go away eventually, once enough app users have upgraded to the latest app version. I don't know their internal deprecation policy, but for the streaming API transition, that took at least a year to move to WebSockets. I don't know if they'll be so laid back for something like auth, but I don't think we're going to be shut out in the next few days or anything.

The very interesting thing is this may finally open the door for official third-party apps. If we can specify our own callback URLs (and those aren't yet locked down), you could auth to a third party site without giving away your password. Fingers crossed!

keymakerfd commented 3 years ago

Hey, I have the solution and was able to successfully integrate it into my application. If you make a request on "oauth/token" you get an error message if the MFA is activated. If you add mfaCode to the request with the MFA code you will get the tokens.

I hope I could help you

bperlman commented 3 years ago

@keymakerfd - to confirm, you are getting an error with a request to https://owner-api.teslamotors.com/oauth/token using password auth? I'm not seeing that, anyone else?

keymakerfd commented 3 years ago

yes, i do a request to: https://owner-api.teslamotors.com/oauth/token image

bperlman commented 3 years ago

Right, that makes sense but if you were to do a request to https://owner-api.teslamotors.com/oauth/token omitting the mfaCode, it returns an error?

I am not seeing that, the request always seems to succeeds even with MFA enabled. Unless they are rolling something out now...

keymakerfd commented 3 years ago

@bperlman I wanted to deal with the topic today and activated MFA. Always got an error until I tried the "solution" mfaCode. After that I got my new tokens immediately. It may well be that they are rolling out something. (Or maybe a rollback if you don't see a error without mfaCode ?)

r24mille commented 3 years ago

@keymakerfd - to confirm, you are getting an error with a request to https://owner-api.teslamotors.com/oauth/token using password auth? I'm not seeing that, anyone else?

Same. I enabled MFA yesterday. I can use the https://owner-api.teslamotors.com/oauth/token endpoint using grant_type of password or refresh_token (with other valid form values) like normal without adding mfaCode. Nothing has changed for me either.

bperlman commented 3 years ago

@bperlman I wanted to deal with the topic today and activated MFA. Always got an error until I tried the "solution" mfaCode. After that I got my new tokens immediately. It may well be that they are rolling out something. (Or maybe a rollback if you don't see a error without mfaCode ?)

Could be something changing on their end I suppose - I've not come across any errors with password grant_type or refresh_token requests to the https://owner-api.teslamotors.com/oauth/token endpoint.

I guess another question would be, do you get an error if you send along a mfaCode that is incorrect?

keymakerfd commented 3 years ago

ok, now it gets crazy. Now I only get the tokens directly without the MFA code. My steps before: 1) Tokens worked 2) MFA activated 3) Tokens were still working 4) My Tesla password changed 5) Tokens became invalid 6) changed the new password in my database 7) Error received (found the message): "{"response": "https://myt-websvc-vip1.tesla.com:443/teslamobileservices/users/authenticate_=>_authorization_required"}" (HTTP/1.1 401 Unauthorized) 8) tested, made the mistake again and again until I came across mfaCode. 9) passed mfaCode with 10) Receive Tokens

Now: Now I always get my tokens without transmitting an mfaCode. No matter if it is correct or not. I tried it again on another server of mine and it is the same there. In the meantime I changed the Tesla password again, with the same effect.

My fear: 1) Tesla is trying something 2) Or the authentication of Tesla did not work in the time frame I tested and it was a "damn" coincidence?

I'm cursing something right now

Vortec4800 commented 3 years ago

I'm working on implementing the "real" OAuth flow with the web login, so we don't ever have custody of the user's email or password.

So far I've gotten the initial login all working properly. The initial URL is https://auth.tesla.com/oauth2/v3/authorize and the keys are a little different. The secret stays the same as before, but the key changes to ownerapi as stated above. You also need to add the PKCE stuff which is a code verifier and challenge, the verifier is generated using the old client ID, and then the challenge is an SHA256 hash of the verifier. Then token URL is https://auth.tesla.com/oauth2/v3/token and all is good, you get a token with a refresh token and an expiration.

Where I'm stuck though is with the refresh token later. Normally you'd call the token URL again with the refresh_token grant along with the original refresh token, and you'd get an updated token and expiration date. The problem is, when making this call to the token URL the request just times out. No error message or other response, just nothing.

Has anyone been messing with this and figure it out yet? Is the refresh call somehow different than normal?

jamesdeck commented 3 years ago

@Vortec4800 are you posting to https://auth.tesla.com/oauth2/v3/authorize like we've always done in the past and it returns the code to further post to the token url? I know you listed what changed but can you share all of the required fields? I'm not able to get as far as you've gotten.

Vortec4800 commented 3 years ago

If you're familiar with OAuthSwift (or can translate to your tool of choice) this is what works for me:

let oauthSwift = OAuth2Swift(
        consumerKey: "ownerapi",
        consumerSecret: kTeslaSecret,
        authorizeUrl: "https://auth.tesla.com/oauth2/v3/authorize",
        accessTokenUrl: "https://auth.tesla.com/oauth2/v3/token",
        responseType: "code"
    )
private func verifier(forKey key: String) -> String {
    let verifier = key.data(using: .utf8)!.base64EncodedString()
        .replacingOccurrences(of: "+", with: "-")
        .replacingOccurrences(of: "/", with: "_")
        .replacingOccurrences(of: "=", with: "")
        .trimmingCharacters(in: .whitespaces)
    return verifier
}

private func challenge(forVerifier verifier: String) -> String {
    let hash = verifier.sha256
    let challenge = hash.base64EncodedString()
        .replacingOccurrences(of: "+", with: "-")
        .replacingOccurrences(of: "/", with: "_")
        .replacingOccurrences(of: "=", with: "")
        .trimmingCharacters(in: .whitespaces)
    return challenge
}
let codeVerifier = self.verifier(forKey: kTeslaClientID)
let codeChallenge = self.challenge(forVerifier: codeVerifier)

let internalController = AuthWebViewController()
internalController.callbackURL = "https://auth.tesla.com/void/callback"
internalController.callingViewController = self
oauthSwift.authorizeURLHandler = internalController
let state = generateState(withLength: 20)

oauthSwift.authorize(withCallbackURL: self.callbackURL, scope: "openid email offline_access", state: state, codeChallenge: codeChallenge, codeChallengeMethod: "S256", codeVerifier: codeVerifier) { result in
    switch result {
    case .success(let (credential, _, _)):
        print(success)
    case .failure(let error):
        print(error)
    }
}

The AuthWebViewController is just a web view that monitors URL requests and when it sees the callback URL, it stops and passes it back to OAuthSwift.

OAuthSwift does have a token refresh call that uses the normal flow, but that's what doesn't work on the Tesla API. I still haven't been able to figure out why the API won't return a response, maybe you'll have better luck.

Geczy commented 3 years ago

@Vortec4800 what is the value of kTeslaSecret ?

Vortec4800 commented 3 years ago

That's just the normal Tesla client ID and secret. They're available from a few places online, it's in this repo as well.

Geczy commented 3 years ago

Would be cool if someone could translate your code into nodejs, I couldn't find a library the supports all that

SohrabYY commented 3 years ago

@Vortec4800 thanks for the details, can you please share the implication of AuthWebViewController, and the value of self.callbackURL?

Vortec4800 commented 3 years ago

Would be cool if someone could translate your code into nodejs, I couldn't find a library the supports all that

I'm familiar with node but I haven't tried to implement client-style OAuth from there, usually auth involves server keys or direct token communication that happened on a client somewhere. I do know it's pretty standard OAuth2 with PKCE, so if a library supports that it should work in theory.

@Vortec4800 thanks for the details, can you please share the implication of AuthWebViewController, and the value of self.callbackURL?

The callback URL is the same as the one a couple lines above, the void/callback URL.

The view controller came from OAuthSwift, I made some modifications to fit my needs but this should get you up and running. https://github.com/OAuthSwift/OAuthSwift/blob/master/Demo/Common/WebViewController.swift

SohrabYY commented 3 years ago

anyone here was able to use token refresh call without timeout or error?

Husqvik commented 3 years ago

The token refresh times out if scope parameter is not included. It seems to be hardcoded to openid email offline_access

fkhera commented 3 years ago

When I call for a token using the old api, i keep getting challenged with: response: {"response": "https://myt-websvc-vip1.tesla.com:443/teslamobileservices/users/authenticate_=>_authorization_required"}

Does this mean they stopped the old interface?

https://tesla-api.timdorr.com/api-basics/authentication

Can these steps still work"?? OST /oauth/token?grant_type=password The initial authentication process is via an OAuth 2.0 Password Grant with the same credentials used for tesla.com and the mobile apps. The current client ID and secret are available here.

fkhera commented 3 years ago

Yes, but the old authentication endpoints that use email/password still work. Strange

Mine does not work anymore I keep getting challenged with obscure URL l

Kemmey commented 3 years ago

For anyone struggling to get this working, in the interest of helping as many convert smoothly to MFA support, I cobbled this project together based on @Vortec4800 's enormously helpful comments above. If you'd just like a working example to inspect - this should do. Code is in no way cleaned up or production ready - but it does illustrate a working token flow with initial grant and subsequent refreshes (thanks @Husqvik !).

https://github.com/Kemmey/TeslaMFA

And HUGE THANK YOU to @timdorr for all your hard work on this - without this repo, my apps would not exist.

ddaddy commented 3 years ago

@Kemmey thanks for that. Have you experienced this giving the error stateNotEqual[j1DR8Y9OZ1cIE5PkFziz<>e8dyr5SfGQjuB070g1Bb]?

It actually seems random, but some login attempts fail with none matching state and some work.

Kemmey commented 3 years ago

@ddaddy I have not. But I'm also only in the early testing stages of mfa implementation. I did stumble on some request state matching in the oauthswift code while debugging - basically, if states don't match, you'll have crossing requests. So multiple auth requests would be fired off, with responses not being properly matched to their originators. Oauthswift should take care of that, though?

Vortec4800 commented 3 years ago

For anyone struggling to get this working, in the interest of helping as many convert smoothly to MFA support, I cobbled this project together based on @Vortec4800 's enormously helpful comments above. If you'd just like a working example to inspect - this should do. Code is in no way cleaned up or production ready - but it does illustrate a working token flow with initial grant and subsequent refreshes (thanks @Husqvik !).

https://github.com/Kemmey/TeslaMFA

And HUGE THANK YOU to @timdorr for all your hard work on this - without this repo, my apps would not exist.

Nice job, I guess I shouldn't have been lazy and done something similar as this looks very similar to what I had put together :).

Did you run into the refresh token issue I described above? Have you gotten token refreshing working? That was the last piece of the puzzle for me.

fkhera commented 3 years ago

Would it be hard for some one to create a python example? We use a lot of automation on python and just looking at how to use MFA without applications as well.

On Tue, Nov 3, 2020 at 7:21 AM Cory Imdieke notifications@github.com wrote:

For anyone struggling to get this working, in the interest of helping as many convert smoothly to MFA support, I cobbled this project together based on @Vortec4800 https://github.com/Vortec4800 's enormously helpful comments above. If you'd just like a working example to inspect - this should do. Code is in no way cleaned up or production ready - but it does illustrate a working token flow with initial grant and subsequent refreshes (thanks @Husqvik https://github.com/Husqvik !).

https://github.com/Kemmey/TeslaMFA

And HUGE THANK YOU to @timdorr https://github.com/timdorr for all your hard work on this - without this repo, my apps would not exist.

Nice job, I guess I shouldn't have been lazy and done something similar as this looks very similar to what I had put together :).

Did you run into the refresh token issue I described above? Have you gotten token refreshing working? That was the last piece of the puzzle for me.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/timdorr/tesla-api/issues/215#issuecomment-721144933, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABU4JYBR7AH67GMTPEECW43SOAGXPANCNFSM4Q4ZKKOQ .

Kemmey commented 3 years ago

@Vortec4800 look for @Husqvik 's comment about scope parameter. As soon as I manually added that, it worked perfectly. So yeah, refresh tokens work!

Vortec4800 commented 3 years ago

Oh cool, good looking out I totally missed that. Thank you!

lannsjo commented 3 years ago

Im trying to authenticate using a webapplication and not swift. Im stuck because when trying to set the callbackURL to my own callback URL I get a bad request "Invalid redirect URI" back from Tesla. How should I fix this?

Vortec4800 commented 3 years ago

I don't think you can, the redirect URL is fixed and can't be changed. In a native app we can look for that URL and catch it, but I don't know how you would do the same on a webapp.

lannsjo commented 3 years ago

You are correct @Vortec4800, have tried using an iframe to catch the data but Tesla does not allow running the authentication via an iframe: Refused to display 'https://auth.tesla.com/oauth2/v3/authorize?... in a frame because it set 'X-Frame-Options' to 'deny' To bad, hope Telsa will release a public version soon with possibility to set our own redirect_uri.

n-gineer commented 3 years ago

I was previously using the owner api through an Arduino WiFi Rev 2. Am I understanding correctly that there is currently no "easy" way to update the HTTPS Post call to get new tokens via password method? I tried with mfacode added as a header with no luck.

Deprecation as workaround: I also turned of MFA to regain functionality, again with no luck. Tried logging into tesla directly=>account locked from too many attempts. Reset and I'm in without MFA.

Question about "easy" update to HTTPS POST remains...

fkhera commented 3 years ago

This is pretty scary - I hope they don't break Tesla owners API, i found when i entered the wrong username/password that I really got some mixed messages from API. To unlock yourself, go change your username/password to saem username/password on Tesla, then try again, and inspect user name / pass is correct, hope it helps!

On Mon, Nov 23, 2020 at 10:26 AM n-gineer notifications@github.com wrote:

I was previously using the owner api through an Arduino WiFi Rev 2. Am I understanding correctly that there is currently no "easy" way to update the HTTPS Post call to get new tokens via password method? I tried with mfacode added as a header with no luck.

Deprecation as workaround: I also turned of MFA to regain functionality, again with no luck. Tried logging into tesla directly=>account locked from too many attempts.

Question about "easy" update to HTTPS POST remains...

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/timdorr/tesla-api/issues/215#issuecomment-732306833, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABU4JYE62HJXLEGPJFPEWYLSRKLFDANCNFSM4Q4ZKKOQ .

n-gineer commented 3 years ago

This is pretty scary - I hope they don't break Tesla owners API, i found when i entered the wrong username/password that I really got some mixed messages from API. To unlock yourself, go change your username/password to saem username/password on Tesla, then try again, and inspect user name / pass is correct, hope it helps!

Thanks! that did help me get in with token generation.

Still hoping to find a long term option WITH MFA that doesn't require learning how to get a new language working alongside what I already have on the Arduino. Here's hoping!

timdorr commented 3 years ago

If you get locked out, that's a separate system than the MFA one. That's always happened and something I've had to deal with occasionally while working on the API.

For the moment, even if you have MFA enabled, you can still access the Owner API with just your email and password. The password grant type has not yet been disabled or altered.

If you ever have auth problems, check your account on tesla.com as a first diagnostic step. If you're making a lot of bad auth requests while developing your software, then that's likely to come up regularly.

fkhera commented 3 years ago

It sounds like the approach then is to take more a react and see approach. LIke once they lock down the 'password grant', I imagine a lot of softwares are going to break, including my nice automated powerwall system for managing our double peak energy rates. I will be curious if the group makes progress on like python or ruby or anything where we can see how to make specific http post with tokens. I imagine we're still waiting on the Tesla to publish some type of shared API token like you can get with other services as well.

On Mon, Nov 23, 2020 at 12:15 PM Tim Dorr notifications@github.com wrote:

If you get locked out, that's a separate system than the MFA one. That's always happened and something I've had to deal with occasionally while working on the API.

For the moment, even if you have MFA enabled, you can still access the Owner API with just your email and password. The password grant type has not yet been disabled or altered.

If you ever have auth problems, check your account on tesla.com as a first diagnostic step. If you're making a lot of bad auth requests while developing your software, then that's likely to come up regularly.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/timdorr/tesla-api/issues/215#issuecomment-732368748, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABU4JYBKHXRCLTOLAAWXR5LSRKYERANCNFSM4Q4ZKKOQ .

dburkland commented 3 years ago

I second the request for a python example as I am a novice when it comes to Swift. I have made a few attempts however I must admit my understanding of oauth is a bit lacking.

Dan

jonasman commented 3 years ago

I have update the TeslaSwift library based on some examples here. I'm not using OAuth2Swift and instead i do the oauth by myself, so the code is easier to read: https://github.com/jonasman/TeslaSwift

In essence you need to build the GET URL (https://auth.tesla.com/oauth2/v3/authorize) to open the page, the callback (https://auth.tesla.com/void/callback) will give you a code, no idea how to intercept the callback on web, but on the server should be doable. Relevant code to build the query params: https://github.com/jonasman/TeslaSwift/blob/master/Sources/TeslaSwift/Model/Authentication.swift

Then make a POST request to the token api (https://auth.tesla.com/oauth2/v3/token) with the code received to get the Auth Token.

Kemmey commented 3 years ago

Fantastic @jonasman - and good job on getting rid of dependencies in the solution! ❤️

jonasman commented 3 years ago

@Kemmey thanks! Is your app using this lib?

Kemmey commented 3 years ago

@jonasman no, it is not. I wrote my own implementation before I found yours. And my implementation sucks as swift is not my main language. I might switch over at some point, but atm I'm stuck with my own mess :-D

one4many commented 3 years ago

Just implemented MFA and I see very long access and refresh tokens (around 800 byte each) compared to the 64 byte before. Which is odd but not a big deal, but the strange part is that the token is only 300 seconds valid before it needs refreshing, which is stupid in my eyes. Or do I need a different scope to obtain tokens which get a lifetime closer to what is was before (3888000 seconds). The scope I use currently is: "openid email offline_access".

lannsjo commented 3 years ago

@one4many: Did you implement MFA in a web solution or was it done using swift?

Kemmey commented 3 years ago

@one4many yeah, the MFA tokens have an extremely short lifespan, but on the other hand the refresh token doesn't seem to change very often. So I would co spider the refresh token as your main token, and just accept that most often, all actions require two http requests... which IS stupid, but it is what it is... Do let me know if you find a way to get a longer lived token!

one4many commented 3 years ago

@one4many: Did you implement MFA in a web solution or was it done using swift?

Ok. I wasn't very clear. I use username and password (my account is not MFA enabled yet) to create tokens with the new oauth interface. I get new tokens and they work, but they are ridiculously shot-lived compared to what they were before. This will create a lot of protocol overhead on my (server based) application.

fkhera commented 3 years ago

I know a lot of folks are doing swift, just want to understand what the purpose is:

I assume its cause folks have IOS apps, and you assume your user exists there to insert their Username / Password on the Tesla Website. Can using this method, can you replace the user with a stored user name /password data store or variable?

I am wondering whether this method that is being proposed would be possible to do autmoated login / MFA token retrieval because we use or have applications that run automated that just look up password data store to do read/write actions for Tesla Powerwall like charge / discharge to control Tesla systems.

Is the method possible or is this whole methodology requiring the USER to be present?

On Mon, Nov 30, 2020 at 9:31 AM lannsjo notifications@github.com wrote:

@one4many https://github.com/one4many: Did you implement MFA in a web solution or was it done using swift?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/timdorr/tesla-api/issues/215#issuecomment-735891969, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABU4JYFBUIGJKOI5H6HLNK3SSPBR7ANCNFSM4Q4ZKKOQ .