diff --git a/.env.example b/.env.example index 52b4b335..12dd2928 100644 --- a/.env.example +++ b/.env.example @@ -40,3 +40,4 @@ PERMANENT_API_BASE_PATH=${LOCAL_TEMPORARY_AUTH_TOKEN} # See https://fusionauth.io/docs/v1/tech/apis/api-keys FUSION_AUTH_HOST=${FUSION_AUTH_HOST} FUSION_AUTH_KEY=${FUSION_AUTH_KEY} +FUSION_AUTH_APP_ID=${FUSION_AUTH_APP_ID} diff --git a/src/classes/AuthenticationSession.ts b/src/classes/AuthenticationSession.ts index 9d43f3ba..666dae14 100644 --- a/src/classes/AuthenticationSession.ts +++ b/src/classes/AuthenticationSession.ts @@ -15,10 +15,16 @@ enum FusionAuthStatusCode { export class AuthenticationSession { public authToken = ''; + public refreshToken = ''; + public readonly authContext; + private authTokenExpiresAt = 0; + private readonly fusionAuthClient; + private readonly fusionAuthAppId = process.env.FUSION_AUTH_APP_ID ?? ''; + private twoFactorId = ''; private twoFactorMethods: TwoFactorMethod[] = []; @@ -32,6 +38,32 @@ export class AuthenticationSession { this.promptForPassword(); } + public obtainNewAuthTokenUsingRefreshToken(): void { + this.fusionAuthClient.exchangeRefreshTokenForAccessToken(this.refreshToken, '', '', '', '') + .then((clientResponse) => { + this.authToken = clientResponse.response.access_token ?? ''; + }) + .catch((clientResponse: unknown) => { + const message = isPartialClientResponse(clientResponse) + ? clientResponse.exception.message + : ''; + logger.warn(`Error obtaining refresh token : ${message}`); + this.authContext.reject(); + }); + } + + public tokenExpired(): boolean { + const expirationDate = new Date(this.authTokenExpiresAt); + return expirationDate <= new Date(); + } + + public tokenWouldExpireSoon(minutes = 5): boolean { + const expirationDate = new Date(this.authTokenExpiresAt); + const currentTime = new Date(); + const timeDifferenceMinutes = (expirationDate.getTime() - currentTime.getTime()) / (1000 * 60); + return timeDifferenceMinutes <= minutes; + } + private promptForPassword(): void { this.authContext.prompt( { @@ -46,6 +78,7 @@ export class AuthenticationSession { private processPasswordResponse([password]: string[]): void { this.fusionAuthClient.login({ + applicationId: this.fusionAuthAppId, loginId: this.authContext.username, password, }).then((clientResponse) => { @@ -57,6 +90,8 @@ export class AuthenticationSession { username: this.authContext.username, }); this.authToken = clientResponse.response.token; + this.authTokenExpiresAt = clientResponse.response.tokenExpirationInstant ?? 0; + this.refreshToken = clientResponse.response.refreshToken ?? ''; this.authContext.accept(); return; } diff --git a/src/classes/SftpSessionHandler.ts b/src/classes/SftpSessionHandler.ts index 2be54cb3..d86d16d5 100644 --- a/src/classes/SftpSessionHandler.ts +++ b/src/classes/SftpSessionHandler.ts @@ -1070,6 +1070,12 @@ export class SftpSessionHandler { } private getCurrentPermanentFileSystem(): PermanentFileSystem { + if ( + this.authenticationSession.tokenExpired() + || this.authenticationSession.tokenWouldExpireSoon() + ) { + this.authenticationSession.obtainNewAuthTokenUsingRefreshToken(); + } return this.permanentFileSystemManager .getCurrentPermanentFileSystemForUser( this.authenticationSession.authContext.username,