3
3
getFusionAuthClient ,
4
4
isPartialClientResponse ,
5
5
} from '../fusionAuth' ;
6
- import { AuthTokenRefreshError } from '../errors/AuthTokenRefreshError' ;
7
6
import type { KeyboardAuthContext } from 'ssh2' ;
8
7
import type { TwoFactorMethod } from '@fusionauth/typescript-client' ;
9
8
@@ -13,108 +12,36 @@ enum FusionAuthStatusCode {
13
12
SuccessNeedsTwoFactorAuth = 242 ,
14
13
}
15
14
16
- export class AuthenticationSession {
17
- private authToken = '' ;
18
-
19
- public refreshToken = '' ;
20
-
21
- public readonly authContext ;
15
+ type SuccessHandler = ( refreshToken : string ) => void ;
22
16
23
- private authTokenExpiresAt = new Date ( ) ;
17
+ export class AuthenticationSession {
18
+ private readonly authContext ;
24
19
25
20
private readonly fusionAuthClient ;
26
21
22
+ private readonly successHandler : SuccessHandler ;
23
+
27
24
private twoFactorId = '' ;
28
25
29
26
private twoFactorMethods : TwoFactorMethod [ ] = [ ] ;
30
27
31
- private fusionAuthAppId = '' ;
32
-
33
28
private fusionAuthClientId = '' ;
34
29
35
- private fusionAuthClientSecret = '' ;
36
-
37
30
public constructor (
38
31
authContext : KeyboardAuthContext ,
39
- fusionAuthAppId : string ,
40
32
fusionAuthClientId : string ,
41
- fusionAuthClientSecret : string ,
33
+ successHandler : SuccessHandler ,
42
34
) {
43
35
this . authContext = authContext ;
44
- this . fusionAuthAppId = fusionAuthAppId ;
45
36
this . fusionAuthClientId = fusionAuthClientId ;
46
- this . fusionAuthClientSecret = fusionAuthClientSecret ;
47
37
this . fusionAuthClient = getFusionAuthClient ( ) ;
38
+ this . successHandler = successHandler ;
48
39
}
49
40
50
41
public invokeAuthenticationFlow ( ) : void {
51
42
this . promptForPassword ( ) ;
52
43
}
53
44
54
- public async getAuthToken ( ) {
55
- if ( this . tokenWouldExpireSoon ( ) ) {
56
- await this . getAuthTokenUsingRefreshToken ( ) ;
57
- }
58
- return this . authToken ;
59
- }
60
-
61
- private async getAuthTokenUsingRefreshToken ( ) : Promise < void > {
62
- let clientResponse ;
63
- try {
64
- /**
65
- * Fusion auth sdk wrongly mandates last two params (scope, user_code)
66
- * hence the need to pass two empty strings here.
67
- * See: https://github.com/FusionAuth/fusionauth-typescript-client/issues/42
68
- */
69
- clientResponse = await this . fusionAuthClient . exchangeRefreshTokenForAccessToken (
70
- this . refreshToken ,
71
- this . fusionAuthClientId ,
72
- this . fusionAuthClientSecret ,
73
- '' ,
74
- '' ,
75
- ) ;
76
- } catch ( error : unknown ) {
77
- let message : string ;
78
- if ( isPartialClientResponse ( error ) ) {
79
- message = error . exception . message ;
80
- } else {
81
- message = error instanceof Error ? error . message : JSON . stringify ( error ) ;
82
- }
83
- logger . verbose ( `Error obtaining refresh token: ${ message } ` ) ;
84
- throw new AuthTokenRefreshError ( `Error obtaining refresh token: ${ message } ` ) ;
85
- }
86
-
87
- if ( ! clientResponse . response . access_token ) {
88
- logger . warn ( 'No access token in response:' , clientResponse . response ) ;
89
- throw new AuthTokenRefreshError ( 'Response does not contain access_token' ) ;
90
- }
91
-
92
- if ( ! clientResponse . response . expires_in ) {
93
- logger . warn ( 'Response lacks token TTL (expires_in):' , clientResponse . response ) ;
94
- throw new AuthTokenRefreshError ( 'Response lacks token TTL (expires_in)' ) ;
95
- }
96
-
97
- /**
98
- * The exchange refresh token for access token endpoint does not return a timestamp,
99
- * it returns expires_in in seconds.
100
- * So we need to create the timestamp to be consistent with what is first
101
- * returned upon initial authentication
102
- */
103
- this . authToken = clientResponse . response . access_token ;
104
- this . authTokenExpiresAt = new Date (
105
- Date . now ( ) + ( clientResponse . response . expires_in * 1000 ) ,
106
- ) ;
107
- logger . debug ( 'New access token obtained:' , clientResponse . response ) ;
108
- }
109
-
110
- private tokenWouldExpireSoon ( expirationThresholdInSeconds = 300 ) : boolean {
111
- const currentTime = new Date ( ) ;
112
- const remainingTokenLife = (
113
- ( this . authTokenExpiresAt . getTime ( ) - currentTime . getTime ( ) ) / 1000
114
- ) ;
115
- return remainingTokenLife <= expirationThresholdInSeconds ;
116
- }
117
-
118
45
private promptForPassword ( ) : void {
119
46
this . authContext . prompt (
120
47
{
@@ -129,37 +56,30 @@ export class AuthenticationSession {
129
56
130
57
private processPasswordResponse ( [ password ] : string [ ] ) : void {
131
58
this . fusionAuthClient . login ( {
132
- applicationId : this . fusionAuthAppId ,
59
+ applicationId : this . fusionAuthClientId ,
133
60
loginId : this . authContext . username ,
134
61
password,
135
62
} ) . then ( ( clientResponse ) => {
136
63
switch ( clientResponse . statusCode ) {
137
64
case FusionAuthStatusCode . Success : {
138
- if ( clientResponse . response . token !== undefined ) {
139
- logger . verbose ( 'Successful password authentication attempt.' , {
140
- username : this . authContext . username ,
141
- } ) ;
142
- this . authToken = clientResponse . response . token ;
143
- if ( clientResponse . response . refreshToken ) {
144
- this . refreshToken = clientResponse . response . refreshToken ;
145
- this . authTokenExpiresAt = new Date (
146
- clientResponse . response . tokenExpirationInstant ?? 0 ,
147
- ) ;
148
- } else {
149
- logger . warn ( 'No refresh token in response :' , clientResponse . response ) ;
150
- this . authContext . reject ( ) ;
151
- }
65
+ logger . verbose ( 'Successful password authentication attempt.' , {
66
+ username : this . authContext . username ,
67
+ } ) ;
68
+ if ( clientResponse . response . refreshToken ) {
69
+ this . successHandler ( clientResponse . response . refreshToken ) ;
152
70
this . authContext . accept ( ) ;
153
71
} else {
154
- logger . warn ( 'No auth token in response' , clientResponse . response ) ;
72
+ logger . warn ( 'No refresh token in response : ' , clientResponse . response ) ;
155
73
this . authContext . reject ( ) ;
156
74
}
157
75
return ;
158
76
}
159
77
case FusionAuthStatusCode . SuccessButUnregisteredInApp : {
160
78
const userId : string = clientResponse . response . user ?. id ?? '' ;
161
79
this . registerUserInApp ( userId )
162
- . then ( ( ) => { this . processPasswordResponse ( [ password ] ) ; } )
80
+ . then ( ( ) => {
81
+ this . processPasswordResponse ( [ password ] ) ;
82
+ } )
163
83
. catch ( ( error ) => {
164
84
logger . warn ( 'Error during registration and authentication:' , error ) ;
165
85
this . authContext . reject ( ) ;
@@ -203,7 +123,7 @@ export class AuthenticationSession {
203
123
try {
204
124
const clientResponse = await this . fusionAuthClient . register ( userId , {
205
125
registration : {
206
- applicationId : this . fusionAuthAppId ,
126
+ applicationId : this . fusionAuthClientId ,
207
127
} ,
208
128
} ) ;
209
129
0 commit comments