I'd like to implement an application that uses a different way of OAuth authentication compared to the examples in the SDK (makeOAuthRequest).
Instead of asking the user for username/password in the browser, they have to be provided already as parameters in the request.
Unfortunately I couldn't get it to work neither with makeOAuthRequest, nor with makeWebRequest. Maybe one of you already run into a similar problem and can assist here.
There's working example code (written in Python) for the usecase here: https://github.com/ChristianKuehnel/...init__.py#L70:
url = 'customer.bmwgroup.com/.../authenticate'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
params = {
'username': self._username,
'password': self._password,
'client_id': self._clientid,
'redirect_uri': 'www.bmw-connecteddrive.com/.../external-dispatch.html',
'response_type': 'token',
'scope': 'authenticate_user fupo',
'state': self._random_string(79),
}
response = requests.post(url, data=urlencode(params), headers=headers, allow_redirects=False)
if response.status_code != 302:
raise IOError('Unknown status code {}'.format(response.status_code))
url_with_token = urllib.parse.parse_qs(response.headers['Location'])
self._oauth_token = url_with_token['access_token'][0]
Here's my attempt with makeOAuthRequest:
var url = "customer.bmwgroup.com/.../authenticate";
var params = {
"username" => "xxx",
"password" => "yyy",
"client_id" => client_id,
"redirect_uri" => "www.bmw-connecteddrive.com/.../external-dispatch.html",
"response_type" => "token",
"scope" => "authenticate_user fupo",
"state" => state
};
Communications.makeOAuthRequest(
url,
params,
"www.bmw-connecteddrive.com/.../external-dispatch.html",
Communications.OAUTH_RESULT_TYPE_URL,
{"access_token" => "token"}
);
This opens an 'empty' browser window (405 status code) because it does a GET instead of the expected POST.
The OAuthCallback is not called.
Then I tried to use a simple makeWebRequest instead (which would probably be preferable as there's no user input required on the mobile phone side):
var url = "customer.bmwgroup.com/.../authenticate";
var params = {
"username" => "xxx",
"password" => "yyy",
"client_id" => client_id,
"redirect_uri" => "www.bmw-connecteddrive.com/.../external-dispatch.html",
"response_type" => "token",
"scope" => "authenticate_user fupo",
"state" => state
};
var options = {
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED,
},
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_URL_ENCODED
};
Communications.makeWebRequest(url, params, options, method(:onOAuthResponse));
Here I always get the callback called with error 400.
I've changed the URL to a local webserver that would just print the received headers to compare it with the request of the Python script to make sure that everything is sent correctly. The problem here might be that the server doesn't reply with 200, but with 302 and maybe this redirect is not handled correctly (there's a allow_redirects=False necessary in Python). Even if it would work, I'm wondering if it would be possible to extract the token because that would be sent as parameter of the 'Location' header in the response.
Is it possible to make this kind of OAuth request from the watch only? Once I've manually added the access_token in my calls to request the data, everything works flawlessly. Therefore it would be really unfortunate if a companion app would be required just to proxy the token request.
If the necessary bits are not yet available, maybe Garmin could extend their APIs to allow this.