Code Monkey home page Code Monkey logo

Comments (3)

frank-lie avatar frank-lie commented on July 28, 2024

Hello,

I don't know about openhab, but I think it will be easier to implement the tokensaver of Apollon77 instead of recompiling or backtranslating the code from perl to java. I also just compare the engine of Apollon77 (https://github.com/Apollon77/daikin-controller-cloud) and Rospogrigio (https://github.com/rospogrigio/daikin_residential/blob/master/custom_components/daikin_residential/daikin_api.py) and translate it to perl. It uses a OPENID-CLIENT connection.

In my module, the authorization process is implemented in the sub "DaikinCloud_BlockAuth" (

DaikinCloud/58_DaikinCloud.pm

Lines 1044 to 1192 in 2658275

sub DaikinCloud_BlockAuth($)
{
my $DAIKIN_ISSUER = 'https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_SLI9qJpc7/.well-known/openid-configuration';
my $DAIKIN_CLOUD_URL = 'https://daikin-unicloud-prod.auth.eu-west-1.amazoncognito.com';
my $APIKEY = '3_xRB3jaQ62bVjqXU1omaEsPDVYC0Twi1zfq1zHPu_5HFT0zWkDvZJS97Yw1loJnTm';
my $APIKEY2 = '3_QebFXhxEWDc8JhJdBWmvUd1e0AaWJCISbqe4QIHrk_KzNVJFJ4xsJ2UZbl8OIIFY';
my ($hash) = @_;
## ask issuer for the actual endpoints
my $param = { url => $DAIKIN_ISSUER, timeout => 5, method => 'GET', ignoreredirects => 1};
my ($err,$response) = HttpUtils_BlockingGet($param);
my $auth_endpoint = '';
my $token_endpoint = '';
if (($err eq '') && ($response ne '')) {
($auth_endpoint) = ( $response =~ m/"authorization.?endpoint"\s*:\s*"([^"]+)/i );
($token_endpoint) = ( $response =~ m/"token.?endpoint"\s*:\s*"([^"]+)/i );
}
## if response gives no endpoints back, take the generally known endpoints
$auth_endpoint = $DAIKIN_CLOUD_URL.'/oauth2/authorize' if (!defined($auth_endpoint) || ($auth_endpoint eq ''));
$token_endpoint = $DAIKIN_CLOUD_URL.'/oauth2/token' if (!defined($token_endpoint) || ($token_endpoint eq ''));
my $saml2_endpoint = $auth_endpoint;
$saml2_endpoint =~ s/oauth2\/authorize//i;
$saml2_endpoint .= 'saml2/idpresponse';
## create client secret
my @chars = ('0'..'9', 'A'..'Z','a'..'z');
my $len = 32;
my $secret = '';
while($len--){ $secret .= $chars[rand @chars] };
## create initial url
my $url = $auth_endpoint.'?response_type=code&state='.$secret;
$url.= '&client_id='.$OPENID_CLIENT_ID.'&scope=openid&redirect_uri=daikinunified%3A%2F%2Flogin';
$param = { url => $url, timeout => 5, method => 'GET', ignoreredirects => 1};
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (1) $err" if ($err ne '');
return 'Error (2) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 302 );
### get crsf cookies
my $cookies = (join '; ', ($param->{httpheader} =~ m/((?:xsrf-token|csrf-state|csrf-state-legacy)=[^;]+)/ig)).'; ';
return 'Error (3) no cookies found: '.$param->{httpheader} if (!defined($cookies) || length($cookies)<50);
### get forward-url
($url) = ($param->{httpheader} =~ m/Location: ([^;\s]+)/i );
return 'Error (4) no forward-url found: '.$param->{httpheader} if (!defined($url) || length($url)<50);
### prepare samlContext request
$param = { url => $url, timeout => 5, method => 'GET', ignoreredirects => 1 };
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (5) $err" if ($err ne '');
return 'Error (6) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 302 );
### searching for samlContext
my ($samlContext) = ( $param->{httpheader} =~ m/samlContext=([^&]+)/i );
return 'Error (7) no samlContext found: '.$param->{httpheader} if (!defined($samlContext) || length($samlContext)<50);
### prepare request to get Api-Version
$url ='https://cdns.gigya.com/js/gigya.js?apiKey='.$APIKEY;
$param = { url => $url, timeout => 5, method => 'GET' };
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (8) $err" if ($err ne '' || $response eq '');
return 'Error (9) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 200 );
### searching for Api-Version
my ($version) = ($response =~ m/(\d+-\d-\d+)/ );
return 'Error (10) no Api-Version found.' if (!defined($version) || length($version)<5);
### prepare request to get single-sign-on cookies
$url = 'https://cdc.daikin.eu/accounts.webSdkBootstrap?apiKey='.$APIKEY.'&sdk=js_latest&format=json' ;
$param = { url => $url, timeout => 5, method => 'GET' };
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (11) $err" if ($err ne '');
return 'Error (12) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 200 );
### extrakt single-sign-on cookies
my $ssocookies = (join '; ', ($param->{httpheader} =~ m/((?:gmid|ucid|hasGmid)=[^;]+)/ig)).'; ';
return 'Error (13) no single-sign-on cookies found: '.$param->{httpheader} if (!defined($ssocookies) || length($ssocookies)<50);
$ssocookies .= 'gig_bootstrap_'.$APIKEY.'=cdc_ver4; ';
$ssocookies .= 'gig_canary_'.$APIKEY2.'=false; ';
$ssocookies .= 'gig_canary_ver_'.$APIKEY2.'='.$version.'; ';
$ssocookies .= 'apiDomain_'.$APIKEY2.'=cdc.daikin.eu; ';
### prepare login to get login-token
my (undef, $username) = getKeyValue('DaikinCloud_username');
$username = DaikinCloud_decrypt($username);
my (undef, $password) = getKeyValue('DaikinCloud_password');
$password = DaikinCloud_decrypt($password);
my $header = { 'content-type' => 'application/x-www-form-urlencoded', 'cookie' => $ssocookies };
my $body = { 'loginID' => $username, 'password' => $password, 'sessionExpiration' => '31536000',
'targetEnv' => 'jssdk','include' => 'profile,', 'loginMode' => 'standard',
'riskContext' => '{"b0":7527,"b2":4,"b5":1', 'APIKey' => $APIKEY, 'sdk' => 'js_latest', 'authMode' => 'cookie',
'pageURL' => 'https://my.daikin.eu/content/daikinid-cdc-saml/en/login.html?samlContext='.$samlContext,
'sdkBuild'=> '12208', 'format' => 'json'
};
$url = 'https://cdc.daikin.eu/accounts.login';
$param = { url => $url, timeout => 5, method => 'POST', header => $header, data => $body };
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (14) $err" if ($err ne '');
return 'Error (15) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 200 );
### extract login-token
my ($logintoken) = ($response =~ m/"login_token": "([^"]+)/i );
return 'Error (16) no login-token found (wrong username or password).' if (!defined($logintoken) || length($logintoken)<10);
### expand single-sign-on cookies with login-token
my $time = time()+ 3600000;
$ssocookies .= 'glt_'.$APIKEY.'='.$logintoken.'; ';
$ssocookies .= 'gig_loginToken_'.$APIKEY2.'='.$logintoken.'; ';
$ssocookies .= 'gig_loginToken_'.$APIKEY2.'_exp='.$time.'; ';
$ssocookies .= 'gig_loginToken_'.$APIKEY2.'_visited=%2C'.$APIKEY.'; ';
### prepare SAMLResponse request
$header = { 'cookie' => $ssocookies };
$body = { 'samlContext' => $samlContext, 'loginToken' => $logintoken };
$url = 'https://cdc.daikin.eu/saml/v2.0/'.$APIKEY .'/idp/sso/continue';
$param = { url => $url, timeout => 5, method => 'POST', header => $header, data => $body};
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (17) $err" if ($err ne '');
return 'Error (18) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 200 );
### extract samlResponse and relayState
my ($samlResponse) = ($response =~ m/name="SAMLResponse" value="([^"]+)/i );
return 'Error (19) no samlResponse found.' if (!defined($samlResponse) || length($samlResponse)<10);
my ($relayState) = ($response =~ m/name="RelayState" value="([^"]+)/i );
return 'Error (20) no relayState found.' if (!defined($relayState) || length($relayState)<10);
### prepare request to get authorization code
$header = { 'content-type' => 'application/x-www-form-urlencoded', 'cookie' => $cookies };
$body = { 'SAMLResponse' => $samlResponse, 'RelayState' => $relayState };
$url = $saml2_endpoint;
$param = { url => $url, timeout => 5, method => 'POST', header => $header, data => $body, ignoreredirects => 1 };
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (21) $err" if ($err ne '');
return 'Error (22) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 302 );
### extract authorization code
my ($code) = ($param->{httpheader} =~ m/daikinunified:\/\/login\?code=([^;]+)/i );
return 'Error (23) no authorization code found.' if (!defined($code) || length($code)<10);
### prepare request to get tokenset
$header = { 'content-type' => 'application/x-www-form-urlencoded', 'cookie' => $cookies };
$url = $token_endpoint.'?grant_type=authorization_code&code='.$code.'&state='.$secret;
$url .= '&client_id='.$OPENID_CLIENT_ID.'&redirect_uri=daikinunified%3A%2F%2Flogin';
$param = { url => $url, header => $header, timeout => 5, method => 'POST' };
($err,$response) = HttpUtils_BlockingGet($param);
return "Error (24) $err" if ($err ne '');
return 'Error (25) HTTP-Status-Code: '.$param->{code} if ($param->{code} != 200 );
## extract tokenset quick and dirty
my ($a_token) = ( $response =~ m/"access.?token"\s*:\s*"([^"]+)/i );
my ($r_token) = ( $response =~ m/"refresh.?token"\s*:\s*"([^"]+)/i );
my ($exp) = ( $response =~ m/"expires.?in"\s*:\s*"?([^",}]+)/i );
my ($t_type) = ( $response =~ m/"token.?type"\s*:\s*"([^"]+)/i );
return 'Error (26) no tokenset found.' if (!defined($a_token) || !defined($r_token) || !defined($exp) || !defined($t_type));
## return tokenset per telnet
return '.access_token|'.$a_token.'|.refresh_token|'.$r_token.'|expires_in|'.$exp.'|token_type|'.$t_type;
}
), where I also use some special FHEM-functions (HttpUtils_BlockingGet) for the HTTP calls. The individual steps are explained by comments (##) in the source code.

The following points are called for:

  1. Get the authorization-endpoint-url and the token-endpoint-url from the ISSUER-URL
  2. Call the authorization-endpoint-url and share a client secret -> extract crsf-cookies and forward-url from request
  3. Call the forward-url -> extract samlContext from request
  4. Call "https://cdns.gigya.com/js/gigya.js.." -> extract Api-Version from request
  5. Call "https://cdc.daikin.eu/accounts.webSdkBootstrap.." -> extract single-sign-on-cookies (sso-cookies) from request
  6. Call "https://cdc.daikin.eu/accounts.login" with sso-cookies, samlContext , username, password -> extract one-time-login-token from request
  7. Call "https://cdc.daikin.eu/saml/.." with samlContext, sso-cookies & one-time-login-token -> extract SAMLResponse, relayState from request
  8. Call saml_endpoint_url with SAMLResponse, relayState, crsf-cookies -> extract authorization-code
  9. Call token-endpoint-url with shared client secret, authorization-code, crsf-cookies -> extract the token-Set

That's it ;-)

Greatings Frank

from daikincloud.

adr001db avatar adr001db commented on July 28, 2024

Hello Frank,
Thanks for extensive response. I'm going to test it right now.

Great :)

Alexander

from daikincloud.

adr001db avatar adr001db commented on July 28, 2024

Hi Frank,
After two days of searching and trying, I managed to create the process in Java. The help you provided has helped me a lot with this.

Again, thank you very much.
Greatings Alexander

from daikincloud.

Related Issues (2)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.