Skip to content

Commit

Permalink
(simatec) next test
Browse files Browse the repository at this point in the history
  • Loading branch information
simatec committed Sep 2, 2024
1 parent 76d4311 commit 10b4c7d
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 45 deletions.
153 changes: 109 additions & 44 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,44 @@ const axios = require('axios');
const crypto = require('crypto');
const qs = require('qs');
const { URL, URLSearchParams } = require('url');
const base64url = require('base64url');

class RemehaHomeAdapter extends utils.Adapter {
constructor(options) {
super({ ...options, name: 'remeha-home' });

this.cookies = {};
this.account = '';
this.password = '';
this.pollInterval = 600;
this.accessToken = null;
this.refreshToken = null;
this.csrfToken = null;
this.codeVerifier = crypto.randomBytes(32).toString('hex');
this.codeChallenge = '';
this.state = '';
this.client = axios.create({
timeout: 10000,
withCredentials: true,
baseURL: 'https://remehalogin.bdrthermea.net'
});

this.client.interceptors.response.use(response => {
const setCookieHeader = response.headers['set-cookie'];
if (setCookieHeader) {
setCookieHeader.forEach(cookieString => {
const [name, ...rest] = cookieString.split(';')[0].split('=');
this.cookies[name] = rest.join('=');
});
}
return response;
});


this.client.interceptors.request.use(request => {
console.log('Starting Request', JSON.stringify(request, null, 2));
return request;
});


this.onReady = this.onReady.bind(this);
this.onMessage = this.onMessage.bind(this);
Expand Down Expand Up @@ -90,26 +115,33 @@ class RemehaHomeAdapter extends utils.Adapter {
}
}

/*
async generateRandomState() {
const base64 = crypto.randomBytes(32).toString('base64');
return base64
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
*/

getCookie(name) {
return this.cookies[name];
}

async resolveExternalData() {
try {
this.state = await this.generateRandomState();
//this.state = crypto.randomBytes(32).toString('base64url');
//const codeChallenge = await this.generateCodeChallenge(this.codeVerifier);
//this.state = await this.generateRandomState();
this.state = crypto.randomBytes(32).toString('base64url');
const codeChallenge = await this.generateRandomToken(64);
this.codeChallenge = codeChallenge;
const codeChallengeSha256 = await this.computeCodeChallenge(codeChallenge);

this.log.debug(`Using state: ${this.state}`);
this.log.debug(`Code challenge: ${codeChallenge}`);
this.log.debug(`Code codeChallengeSha256: ${codeChallengeSha256}`);

const response = await axios.get('https://remehalogin.bdrthermea.net/bdrb2cprod.onmicrosoft.com/oauth2/v2.0/authorize', {
const response = await this.client.get(`/bdrb2cprod.onmicrosoft.com/oauth2/v2.0/authorize`, {
params: {
response_type: 'code',
client_id: '6ce007c6-0628-419e-88f4-bee2e6418eec',
Expand All @@ -125,66 +157,96 @@ class RemehaHomeAdapter extends utils.Adapter {
prompt: 'login',
signUp: 'False'
},
/*
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
*/
});

this.log.debug('Response get Auth: ' + response.status);
this.log.debug('Response get header: ' + response.headers);
this.log.debug('x-request-id: ' + response.headers["x-request-id"]);


let csrfTokenCookie;
const cookies = response.headers['set-cookie'];
if (cookies) {

csrfTokenCookie = cookies.find(cookie => cookie.startsWith('x-ms-cpim-csrf=') && cookie.includes('domain=remehalogin.bdrthermea.net'));

if (csrfTokenCookie) {
this.csrfToken = csrfTokenCookie.split('=')[1].split(';')[0];
//this.csrfToken = csrfTokenCookie.split(';')[0].replace("x-ms-cpim-csrf=", "").replace(/;$/, "");
this.log.debug('csrfToken Alt: ' + csrfTokenCookie.split(';')[0].replace("x-ms-cpim-csrf=", "").replace(/;$/, ""));
} else {
throw new Error('CSRF-Token not found in response headers.');
}

}
const csrfToken = this.getCookie('x-ms-cpim-csrf');
this.csrfToken = csrfToken;
this.log.debug('csrfToken Neu: ' + this.csrfToken);

const statePropertiesJson = JSON.stringify({ "state": this.state });
const statePropertiesBase64 = Buffer.from(statePropertiesJson).toString('base64');
const stateProperties = statePropertiesBase64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
// Extract the request_id from headers
const requestId = response.headers['x-request-id'];

// Create state_properties JSON and encode it in base64 URL-safe format
const statePropertiesJson = `{"TID":"${requestId}"}`;
const stateProperties = base64url.encode(statePropertiesJson);
/*
const stateProperties = Buffer.from(statePropertiesJson, 'ascii')
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
*/

this.log.debug(`stateProperties: ${stateProperties}`);

const authorizationCode = await this.login(stateProperties, this.csrfToken);
this.log.debug(`authorizationCode: ${authorizationCode}`)

if (!authorizationCode) throw new Error('Authorization code is missing.');
//if (!authorizationCode) throw new Error('Authorization code is missing.');

await this.fetchAccessToken(authorizationCode);
//await this.fetchAccessToken(authorizationCode);
} catch (error) {
this.log.error(`Error resolving external data: ${error.message}`);
}
}

async sleep(ms) {
return new Promise(async (resolve) => {
// @ts-ignore
this.setTimeout(async () => resolve(), ms);
});
}

async login(stateProperties, csrfToken) {
try {
this.log.debug(`Attempting login with stateProperties: ${stateProperties}`);
this.log.debug(`CSRF-Token: ${csrfToken}`);

const postData = qs.stringify({
request_type: 'RESPONSE',
signInName: this.account,
password: this.password
});

// POST to Login-URL
const response = await axios.post(
'https://remehalogin.bdrthermea.net/bdrb2cprod.onmicrosoft.com/B2C_1A_RPSignUpSignInNewRoomv3.1/SelfAsserted',
postData,
const response = await this.client.post(`/bdrb2cprod.onmicrosoft.com/B2C_1A_RPSignUpSignInNewRoomv3.1/SelfAsserted`,
{
request_type: 'RESPONSE',
signInName: this.account,
password: this.password,
},
{
params: {
tx: `StateProperties=${stateProperties}`,
tx: encodeURIComponent(`StateProperties=${stateProperties}`),
p: 'B2C_1A_RPSignUpSignInNewRoomv3.1',
},
headers: {
'x-csrf-token': csrfToken,
'Content-Type': 'application/x-www-form-urlencoded'
},
//'Content-Type': 'application/x-www-form-urlencoded'
}
}
);

this.log.debug('Status Text:' + response.statusText);
this.log.debug('Login response status:' + response.status);
this.log.debug('Login response headers:' + response.headers);

} catch (error) {
this.log.error('Error during login:' + error.message);
Expand All @@ -193,26 +255,26 @@ class RemehaHomeAdapter extends utils.Adapter {
}
throw error;
}
this.log.debug('Part 2');

await this.sleep(1000);

try {
const response1 = await axios.get(
"https://remehalogin.bdrthermea.net/bdrb2cprod.onmicrosoft.com/B2C_1A_RPSignUpSignInNewRoomv3.1/api/CombinedSigninAndSignup/confirmed",
const response = await this.client.get(`/bdrb2cprod.onmicrosoft.com/B2C_1A_RPSignUpSignInNewRoomv3.1/api/CombinedSigninAndSignup/confirmed`,
{
maxRedirects: 0,
params: {
rememberMe: false,
rememberMe: 'false',
csrf_token: csrfToken,
tx: `StateProperties=${stateProperties}`,
tx: encodeURIComponent(`StateProperties=${stateProperties}`),
p: 'B2C_1A_RPSignUpSignInNewRoomv3.1',
},
headers: {
"Access-Control-Expose-Headers": "location" // ???
}
withCredentials: true,
maxRedirects: 0,
validateStatus: (status) => true
});

}
);
this.log.debug('Login response1 status:' + response1.status);
this.log.debug('Login response1 headers:' + response1.headers);
this.log.debug('Login response1 status:' + response.status);
this.log.debug('Login response1 headers:' + response.headers);

/*
const locationHeader = response1.headers['location'];
Expand All @@ -222,7 +284,7 @@ class RemehaHomeAdapter extends utils.Adapter {
const code = url.searchParams.get('code');
*/
const parsedCallbackUrl = new URL(response1.headers.location);
const parsedCallbackUrl = new URL(response.headers.location);
if (parsedCallbackUrl) {
const queryStringDict = parsedCallbackUrl.searchParams;
const code = queryStringDict.get('code');
Expand All @@ -241,8 +303,9 @@ class RemehaHomeAdapter extends utils.Adapter {

} catch (error) {
this.log.error('Error get code:' + error.message);
if (error.response1) {
this.log.error('Response status:' + error.response1.status);
if (error.response) {
this.log.error('Response status:' + error.response.status);
//this.log.error('Response status:' + error.response.headers);
}
throw error;
}
Expand Down Expand Up @@ -281,6 +344,7 @@ class RemehaHomeAdapter extends utils.Adapter {
return base64Url;
}

/*
async generateCodeChallenge(codeVerifier) {
const hash = crypto.createHash('sha256');
hash.update(codeVerifier);
Expand All @@ -290,6 +354,7 @@ class RemehaHomeAdapter extends utils.Adapter {
.replace(/=/g, '');
return codeChallenge;
}
*/

async fetchAccessToken(code) {
try {
Expand All @@ -299,10 +364,10 @@ class RemehaHomeAdapter extends utils.Adapter {
code: code,
redirect_uri: 'com.b2c.remehaapp://login-callback',
client_id: '6ce007c6-0628-419e-88f4-bee2e6418eec',
code_verifier: this.codeVerifier
code_verifier: this.codeChallenge
};

const response = await axios.post(tokenUrl, qs.stringify(tokenParams), {
const response = await this.client.post(tokenUrl, qs.stringify(tokenParams), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});

Expand All @@ -318,7 +383,7 @@ class RemehaHomeAdapter extends utils.Adapter {

async refreshAccessToken() {
try {
const response = await axios.post('https://remehalogin.bdrthermea.net/bdrb2cprod.onmicrosoft.com/oauth2/v2.0/token', qs.stringify({
const response = await this.client.post('https://remehalogin.bdrthermea.net/bdrb2cprod.onmicrosoft.com/oauth2/v2.0/token', qs.stringify({
grant_type: 'refresh_token',
refresh_token: this.refreshToken,
client_id: '6ce007c6-0628-419e-88f4-bee2e6418eec'
Expand All @@ -342,7 +407,7 @@ class RemehaHomeAdapter extends utils.Adapter {
await this.fetchAccessToken(); // or refreshAccessToken()
}

const response = await axios.get('https://api.bdrthermea.net/Mobile/api/homes/dashboard', {
const response = await this.client.get('https://api.bdrthermea.net/Mobile/api/homes/dashboard', {
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Ocp-Apim-Subscription-Key': 'df605c5470d846fc91e848b1cc653ddf',
Expand All @@ -369,7 +434,7 @@ class RemehaHomeAdapter extends utils.Adapter {

async checkTokenValidity(token) {
try {
const response = await axios.get('https://api.bdrthermea.net/Mobile/api/homes/dashboard', {
const response = await this.client.get('https://api.bdrthermea.net/Mobile/api/homes/dashboard', {
headers: {
'Authorization': `Bearer ${token}`,
'Ocp-Apim-Subscription-Key': 'df605c5470d846fc91e848b1cc653ddf',
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"crypto": "^1.0.1",
"axios": "^1.7.5",
"qs": "^6.13.0",
"url": "^0.11.4"
"url": "^0.11.4",
"base64url": "^3.0.1"
},
"devDependencies": {
"@alcalzone/release-script": "^3.8.0",
Expand Down

0 comments on commit 10b4c7d

Please sign in to comment.