diff --git a/package.json b/package.json index cdf4cff..ab1e34b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bluelinky", - "version": "9.1.0", + "version": "9.2.0", "description": "An unofficial nodejs API wrapper for Hyundai bluelink", "main": "dist/index.cjs", "module": "dist/index.esm.js", diff --git a/src/constants/europe.ts b/src/constants/europe.ts index fdcdc17..6471722 100644 --- a/src/constants/europe.ts +++ b/src/constants/europe.ts @@ -79,12 +79,14 @@ const getHyundaiEnvironment = ({ stampsFile, }: EnvironmentConfig): EuropeanBrandEnvironment => { const host = 'prd.eu-ccapi.hyundai.com:8080'; + const idpUrl = 'https://idpconnect-eu.hyundai.com'; const baseUrl = `https://${host}`; const clientId = '6d477c38-3ca4-4cf3-9557-2a1929a94654'; const appId = '1eba27d2-9a5b-4eba-8ec7-97eb6c62fb51'; return { brand: 'hyundai', host, + idpUrl, baseUrl, clientId, appId, @@ -112,12 +114,15 @@ const getKiaEnvironment = ({ stampsFile, }: EnvironmentConfig): EuropeanBrandEnvironment => { const host = 'prd.eu-ccapi.kia.com:8080'; + const idpUrl = 'https://idpconnect-eu.kia.com'; const baseUrl = `https://${host}`; const clientId = 'fdc85c00-0a2f-4c64-bcb4-2cfb1500730a'; - const appId = 'a2b8469b-30a3-4361-8e13-6fceea8fbe74'; +// const appId = 'a2b8469b-30a3-4361-8e13-6fceea8fbe74'; + const appId = '1518dd6b-2759-4995-9ae5-c9ad4a9ddad1'; return { brand: 'kia', host, + idpUrl, baseUrl, clientId, appId, @@ -141,7 +146,7 @@ const getKiaEnvironment = ({ export const getBrandEnvironment = ({ brand, - stampMode = StampMode.DISTANT, + stampMode = StampMode.LOCAL, stampsFile, }: BrandEnvironmentConfig): EuropeanBrandEnvironment => { switch (brand) { diff --git a/src/controllers/authStrategies/authStrategy.ts b/src/controllers/authStrategies/authStrategy.ts index b02ab58..38a0869 100644 --- a/src/controllers/authStrategies/authStrategy.ts +++ b/src/controllers/authStrategies/authStrategy.ts @@ -1,6 +1,6 @@ import got from 'got'; import { CookieJar } from 'tough-cookie'; -import { DEFAULT_LANGUAGE, EULanguages, EuropeanBrandEnvironment } from '../../constants/europe'; +import { EuropeanBrandEnvironment } from '../../constants/europe'; export type Code = string; @@ -14,15 +14,9 @@ export interface AuthStrategy { export async function initSession( environment: EuropeanBrandEnvironment, - language: EULanguages = DEFAULT_LANGUAGE, cookies?: CookieJar ): Promise { const cookieJar = cookies ?? new CookieJar(); await got(environment.endpoints.session, { cookieJar }); - await got(environment.endpoints.language, { - method: 'POST', - body: `{"lang":"${language}"}`, - cookieJar, - }); return cookieJar; } diff --git a/src/controllers/authStrategies/european.brandAuth.strategy.ts b/src/controllers/authStrategies/european.brandAuth.strategy.ts index f5ea94b..663fca9 100644 --- a/src/controllers/authStrategies/european.brandAuth.strategy.ts +++ b/src/controllers/authStrategies/european.brandAuth.strategy.ts @@ -2,7 +2,8 @@ import got from 'got'; import { CookieJar } from 'tough-cookie'; import { EULanguages, EuropeanBrandEnvironment } from '../../constants/europe'; import { AuthStrategy, Code, initSession } from './authStrategy'; -import Url, { URLSearchParams } from 'url'; +//import Url, +import { URLSearchParams } from 'url'; const stdHeaders = { 'User-Agent': @@ -31,38 +32,50 @@ export class EuropeanBrandAuthStrategy implements AuthStrategy { } public async login(user: { username: string; password: string; }, options?: { cookieJar?: CookieJar }): Promise<{ code: Code, cookies: CookieJar }> { - const cookieJar = await initSession(this.environment, this.language, options?.cookieJar); + const cookieJar = await initSession(this.environment, options?.cookieJar); const { body: { userId, serviceId } } = await got(this.environment.endpoints.integration, { cookieJar, json: true, headers: stdHeaders }); const brandAuthUrl = this.environment.brandAuthUrl({ language: this.language, userId, serviceId }); - const parsedBrandUrl = Url.parse(brandAuthUrl, true); - const { body: authForm } = await got( + //const parsedBrandUrl = Url.parse(brandAuthUrl, true); + //const { body: authForm } = + await got( brandAuthUrl, { cookieJar, headers: stdHeaders }); - const actionUrl = /action="([a-z0-9:/\-.?_=&;]*)"/gi.exec(authForm); - const preparedUrl = actionUrl?.[1].replace(/&/g, '&'); - if (!preparedUrl) { - throw new Error('@EuropeanBrandAuthStrategy.login: cannot found the auth url from the form.'); - } - const formData = new URLSearchParams(); - formData.append('username', user.username); - formData.append('password', user.password); - formData.append('credentialId', ''); - formData.append('rememberMe', 'on'); - const { headers: { location: redirectTo }, body: afterAuthForm } = await manageGot302(got.post(preparedUrl, { - cookieJar, - body: formData.toString(), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - ...stdHeaders - }, - followRedirect: false, - })); + + const authUrl = `${this.environment.idpUrl}/auth/api/v2/user/oauth2/authorize`; + const params = { + response_type: 'code', + client_id: serviceId, + redirect_uri: this.environment.endpoints.redirectUri, // `${this.environment.baseUrl}/api/v1/user/oauth2/redirect`, + state: 'ccsp', + lang: 'en' + }; + await got(authUrl, { params }); + + const loginUrl = `${this.environment.idpUrl}/auth/account/signin`; + const loginData = new URLSearchParams({ + username: user.username, + password: user.password, + encryptedPassword: 'false', + remember_me: 'false', + redirect_uri: this.environment.endpoints.redirectUri, // `${this.environment.baseUrl}/api/v1/user/oauth2/redirect`, + state: 'ccsp', + client_id: this.environment.clientId + }); + const { headers: { location: redirectTo }, body: afterAuthForm } = await manageGot302(got.post(loginUrl, { + body: loginData.toString(), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + followRedirect: false, // equivalent to maxRedirects: 0 + throwHttpErrors: false // needed so it doesn't throw on 302 + })); + if(!redirectTo) { const errorMessage = /(.+)<\/span>/gm.exec(afterAuthForm); if (errorMessage) { @@ -70,65 +83,9 @@ export class EuropeanBrandAuthStrategy implements AuthStrategy { } throw new Error('@EuropeanBrandAuthStrategy.login: Authentication failed, cannot retrieve error message'); } - const authResult = await got(redirectTo, { - cookieJar, - headers: stdHeaders - }); - let url = authResult.url; - let htmlPage = authResult.body; - if(!url) { - throw new Error(`@EuropeanBrandAuthStrategy.login: after login redirection got stuck : ${htmlPage}`); - } - if(url.includes('login-actions/required-action')) { - const loginActionUrl = /action="([a-z0-9:/\-.?_=&;]*)"/gi.exec(htmlPage); - const loginActionCode = /name="code" value="(.*)"/gi.exec(htmlPage); - if (!loginActionUrl) { - throw new Error('@EuropeanBrandAuthStrategy.login: Cannot find login-actions url.'); - } - if (!loginActionCode) { - throw new Error('@EuropeanBrandAuthStrategy.login: Cannot find login-actions code.'); - } - const actionUrl = (loginActionUrl[1].startsWith('/')) ? `${parsedBrandUrl.protocol}//${parsedBrandUrl.host}${loginActionUrl[1]}` : loginActionUrl[1]; - const loginActionForm = new URLSearchParams(); - loginActionForm.append('code', loginActionCode[1]); - loginActionForm.append('accept', ''); - const { headers: { location: loginActionRedirect }, body: AfterLoginActionAuthForm } = await manageGot302(got.post(actionUrl, { - cookieJar, - body: loginActionForm.toString(), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - ...stdHeaders - }, - })); - if(!loginActionRedirect) { - const errorMessage = /