From 4f6124ca140376d958be71ae10d8e36a57971966 Mon Sep 17 00:00:00 2001 From: fenn-cs Date: Tue, 20 Jun 2023 17:35:57 +0100 Subject: [PATCH] Refresh authToken when it expires Access tokens have lifetimes that might end before the user wants to end their authenticated session. Hence, we need to refresh the user access token after an expiry detection. Resolves: https://github.com/PermanentOrg/sftp-service/issues/175 Signed-off-by: fenn-cs --- .env.example | 1 + src/classes/AuthenticationSession.ts | 35 ++++++++++++++++++++++++++++ src/classes/SftpSessionHandler.ts | 6 +++++ 3 files changed, 42 insertions(+) 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,