@@ -2,24 +2,13 @@ import got from 'got';
2
2
import { CookieJar } from 'tough-cookie' ;
3
3
import { EULanguages , EuropeanBrandEnvironment } from '../../constants/europe' ;
4
4
import { AuthStrategy , Code , initSession } from './authStrategy' ;
5
- import Url , { URLSearchParams } from 'url' ;
5
+ import { URLSearchParams } from 'url' ;
6
6
7
7
const stdHeaders = {
8
8
'User-Agent' :
9
9
'Mozilla/5.0 (iPhone; CPU iPhone OS 11_1 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0 Mobile/15B92 Safari/604.1' ,
10
10
} ;
11
11
12
- const manageGot302 = < T extends Buffer | string | Record < string , unknown > > (
13
- got : Promise < got . Response < T > >
14
- ) : Promise < got . Response < T > > => {
15
- return got . catch ( error => {
16
- if ( error . name === 'HTTPError' && error . statusCode === 302 ) {
17
- return error . response ;
18
- }
19
- return Promise . reject ( error ) ;
20
- } ) ;
21
- } ;
22
-
23
12
export class EuropeanBrandAuthStrategy implements AuthStrategy {
24
13
constructor (
25
14
private readonly environment : EuropeanBrandEnvironment ,
@@ -30,111 +19,101 @@ export class EuropeanBrandAuthStrategy implements AuthStrategy {
30
19
return 'EuropeanBrandAuthStrategy' ;
31
20
}
32
21
33
- public async login ( user : { username : string ; password : string ; } , options ?: { cookieJar ?: CookieJar } ) : Promise < { code : Code , cookies : CookieJar } > {
34
- const cookieJar = await initSession ( this . environment , this . language , options ?. cookieJar ) ;
35
- const { body : { userId, serviceId } } = await got ( this . environment . endpoints . integration , {
36
- cookieJar,
37
- json : true ,
38
- headers : stdHeaders
39
- } ) ;
40
- const brandAuthUrl = this . environment . brandAuthUrl ( { language : this . language , userId, serviceId } ) ;
41
- const parsedBrandUrl = Url . parse ( brandAuthUrl , true ) ;
42
- const { body : authForm } = await got (
43
- brandAuthUrl , {
44
- cookieJar,
45
- headers : stdHeaders
46
- } ) ;
47
- const actionUrl = / a c t i o n = " ( [ a - z 0 - 9 : / \- . ? _ = & ; ] * ) " / gi. exec ( authForm ) ;
48
- const preparedUrl = actionUrl ?. [ 1 ] . replace ( / & a m p ; / g, '&' ) ;
49
- if ( ! preparedUrl ) {
50
- throw new Error ( '@EuropeanBrandAuthStrategy.login: cannot found the auth url from the form.' ) ;
51
- }
52
- const formData = new URLSearchParams ( ) ;
53
- formData . append ( 'username' , user . username ) ;
54
- formData . append ( 'password' , user . password ) ;
55
- formData . append ( 'credentialId' , '' ) ;
56
- formData . append ( 'rememberMe' , 'on' ) ;
57
- const { headers : { location : redirectTo } , body : afterAuthForm } = await manageGot302 ( got . post ( preparedUrl , {
58
- cookieJar,
59
- body : formData . toString ( ) ,
60
- headers : {
61
- 'Content-Type' : 'application/x-www-form-urlencoded' ,
62
- ...stdHeaders
63
- } ,
22
+ public async login ( user : { username : string ; password : string ; } , options ?: { cookieJar ?: CookieJar } ) : Promise < { code : Code , cookies : CookieJar } > {
23
+ const cookieJar = await initSession ( this . environment , options ?. cookieJar ) ;
24
+
25
+ // Build the correct auth URL based on the new KIA/Hyundai authentication
26
+ const authHost = this . environment . brand === 'kia'
27
+ ? 'idpconnect-eu.kia.com'
28
+ : 'idpconnect-eu.hyundai.com' ;
29
+
30
+ const authUrl = `https://${ authHost } /auth/api/v2/user/oauth2/authorize?response_type=code&client_id=${ this . environment . clientId } &redirect_uri=${ this . environment . baseUrl } /api/v1/user/oauth2/redirect&lang=${ this . language } &state=ccsp` ;
31
+
32
+ // Step 1: GET request to auth URL to get connector_session_key
33
+ const authResponse = await got ( authUrl , {
34
+ cookieJar,
35
+ headers : stdHeaders ,
36
+ followRedirect : true ,
37
+ throwHttpErrors : false ,
38
+ } ) ;
39
+
40
+ // Extract connector_session_key from the final URL after redirects
41
+ const urlToCheck = authResponse . url ;
42
+
43
+ // Try multiple regex patterns to find the session key
44
+ let connectorSessionKey : string | null = null ;
45
+
46
+ // Pattern 1: URL encoded
47
+ let match = urlToCheck . match ( / c o n n e c t o r _ s e s s i o n _ k e y % 3 D ( [ 0 - 9 a - f A - F - ] { 36 } ) / ) ;
48
+ if ( match ) {
49
+ connectorSessionKey = match [ 1 ] ;
50
+ }
51
+
52
+ // Pattern 2: Not URL encoded
53
+ if ( ! connectorSessionKey ) {
54
+ match = urlToCheck . match ( / c o n n e c t o r _ s e s s i o n _ k e y = ( [ 0 - 9 a - f A - F - ] { 36 } ) / ) ;
55
+ if ( match ) {
56
+ connectorSessionKey = match [ 1 ] ;
57
+ }
58
+ }
59
+
60
+ if ( ! connectorSessionKey ) {
61
+ throw new Error ( `@EuropeanBrandAuthStrategy.login: Could not extract connector_session_key from URL: ${ urlToCheck } ` ) ;
62
+ }
63
+
64
+ // Step 2: POST to signin endpoint
65
+ const signinUrl = `https://${ authHost } /auth/account/signin` ;
66
+
67
+ const formData = new URLSearchParams ( ) ;
68
+ formData . append ( 'client_id' , this . environment . clientId ) ;
69
+ formData . append ( 'encryptedPassword' , 'false' ) ;
70
+ formData . append ( 'orgHmgSid' , '' ) ;
71
+ formData . append ( 'password' , user . password ) ;
72
+ formData . append ( 'redirect_uri' , `${ this . environment . baseUrl } /api/v1/user/oauth2/redirect` ) ;
73
+ formData . append ( 'state' , 'ccsp' ) ;
74
+ formData . append ( 'username' , user . username ) ;
75
+ formData . append ( 'remember_me' , 'false' ) ;
76
+ formData . append ( 'connector_session_key' , connectorSessionKey ) ;
77
+ formData . append ( '_csrf' , '' ) ;
78
+
79
+ const signinResponse = await got . post ( signinUrl , {
80
+ cookieJar,
81
+ body : formData . toString ( ) ,
82
+ headers : {
83
+ 'content-type' : 'application/x-www-form-urlencoded' ,
84
+ 'origin' : `https://${ authHost } ` ,
85
+ ...stdHeaders
86
+ } ,
64
87
followRedirect : false ,
65
- } ) ) ;
66
- if ( ! redirectTo ) {
67
- const errorMessage = / < s p a n c l a s s = " k c - f e e d b a c k - t e x t " > ( .+ ) < \/ s p a n > / gm. exec ( afterAuthForm ) ;
68
- if ( errorMessage ) {
69
- throw new Error ( `@EuropeanBrandAuthStrategy.login: Authentication failed with message : ${ errorMessage [ 1 ] } ` ) ;
70
- }
71
- throw new Error ( '@EuropeanBrandAuthStrategy.login: Authentication failed, cannot retrieve error message' ) ;
72
- }
73
- const authResult = await got ( redirectTo , {
74
- cookieJar,
75
- headers : stdHeaders
76
- } ) ;
77
- let url = authResult . url ;
78
- let htmlPage = authResult . body ;
79
- if ( ! url ) {
80
- throw new Error ( `@EuropeanBrandAuthStrategy.login: after login redirection got stuck : ${ htmlPage } ` ) ;
81
- }
82
- if ( url . includes ( 'login-actions/required-action' ) ) {
83
- const loginActionUrl = / a c t i o n = " ( [ a - z 0 - 9 : / \- . ? _ = & ; ] * ) " / gi. exec ( htmlPage ) ;
84
- const loginActionCode = / n a m e = " c o d e " v a l u e = " ( .* ) " / gi. exec ( htmlPage ) ;
85
- if ( ! loginActionUrl ) {
86
- throw new Error ( '@EuropeanBrandAuthStrategy.login: Cannot find login-actions url.' ) ;
87
- }
88
- if ( ! loginActionCode ) {
89
- throw new Error ( '@EuropeanBrandAuthStrategy.login: Cannot find login-actions code.' ) ;
90
- }
91
- const actionUrl = ( loginActionUrl [ 1 ] . startsWith ( '/' ) ) ? `${ parsedBrandUrl . protocol } //${ parsedBrandUrl . host } ${ loginActionUrl [ 1 ] } ` : loginActionUrl [ 1 ] ;
92
- const loginActionForm = new URLSearchParams ( ) ;
93
- loginActionForm . append ( 'code' , loginActionCode [ 1 ] ) ;
94
- loginActionForm . append ( 'accept' , '' ) ;
95
- const { headers : { location : loginActionRedirect } , body : AfterLoginActionAuthForm } = await manageGot302 ( got . post ( actionUrl , {
96
- cookieJar,
97
- body : loginActionForm . toString ( ) ,
98
- headers : {
99
- 'Content-Type' : 'application/x-www-form-urlencoded' ,
100
- ...stdHeaders
101
- } ,
102
- } ) ) ;
103
- if ( ! loginActionRedirect ) {
104
- const errorMessage = / < s p a n c l a s s = " k c - f e e d b a c k - t e x t " > ( .+ ) < \/ s p a n > / gm. exec ( AfterLoginActionAuthForm ) ;
105
- if ( errorMessage ) {
106
- throw new Error ( `@EuropeanBrandAuthStrategy.login: Authentication action failed with message : ${ errorMessage [ 1 ] } ` ) ;
107
- }
108
- throw new Error ( '@EuropeanBrandAuthStrategy.login: Authentication action failed, cannot retrieve error message' ) ;
109
- }
110
- const authResult = await got ( loginActionRedirect , {
111
- cookieJar,
112
- headers : stdHeaders
113
- } ) ;
114
- url = authResult . url ;
115
- htmlPage = authResult . body ;
116
- }
117
- const { body, statusCode } = await got . post ( this . environment . endpoints . silentSignIn , {
118
- cookieJar,
119
- body : {
120
- intUserId : ''
121
- } ,
122
- json : true ,
123
- headers : {
124
- ...stdHeaders ,
125
- 'ccsp-service-id' : this . environment . clientId ,
126
- }
127
- } ) ;
128
- if ( ! body . redirectUrl ) {
129
- throw new Error ( `@EuropeanBrandAuthStrategy.login: silent sign In didn't work, could not retrieve auth code. status: ${ statusCode } , body: ${ JSON . stringify ( body ) } ` ) ;
130
- }
131
- const { code } = Url . parse ( body . redirectUrl , true ) . query ;
132
- if ( ! code ) {
133
- throw new Error ( `@EuropeanBrandAuthStrategy.login: Cannot find the argument code in ${ body . redirectUrl } .` ) ;
134
- }
135
- return {
136
- code : code as Code ,
137
- cookies : cookieJar ,
138
- } ;
139
- }
140
- }
88
+ throwHttpErrors : false ,
89
+ } ) ;
90
+
91
+ if ( signinResponse . statusCode !== 302 ) {
92
+ throw new Error ( `@EuropeanBrandAuthStrategy.login: Signin failed with status ${ signinResponse . statusCode } : ${ signinResponse . body } ` ) ;
93
+ }
94
+
95
+ // Step 3: Extract authorization code from Location header
96
+ const location = signinResponse . headers . location ;
97
+ if ( ! location ) {
98
+ throw new Error ( '@EuropeanBrandAuthStrategy.login: No redirect location found after signin' ) ;
99
+ }
100
+
101
+ const codeMatch = location . match ( / c o d e = ( [ 0 - 9 a - f A - F - ] { 36 } \. [ 0 - 9 a - f A - F - ] { 36 } \. [ 0 - 9 a - f A - F - ] { 36 } ) / ) ;
102
+ if ( ! codeMatch ) {
103
+ // Try alternative patterns for different code formats
104
+ const altMatch = location . match ( / c o d e = ( [ ^ & ] + ) / ) ;
105
+ if ( altMatch ) {
106
+ const code = altMatch [ 1 ] ;
107
+ return { code : code as Code , cookies : cookieJar } ;
108
+ }
109
+ throw new Error ( `@EuropeanBrandAuthStrategy.login: Could not extract authorization code from redirect location: ${ location } ` ) ;
110
+ }
111
+
112
+ const code = codeMatch [ 1 ] ;
113
+
114
+ return {
115
+ code : code as Code ,
116
+ cookies : cookieJar ,
117
+ } ;
118
+ }
119
+ }
0 commit comments