Skip to content

Commit 4f6124c

Browse files
committed
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: #175 Signed-off-by: fenn-cs <fenn25.fn@gmail.com>
1 parent d1a327d commit 4f6124c

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ PERMANENT_API_BASE_PATH=${LOCAL_TEMPORARY_AUTH_TOKEN}
4040
# See https://fusionauth.io/docs/v1/tech/apis/api-keys
4141
FUSION_AUTH_HOST=${FUSION_AUTH_HOST}
4242
FUSION_AUTH_KEY=${FUSION_AUTH_KEY}
43+
FUSION_AUTH_APP_ID=${FUSION_AUTH_APP_ID}

src/classes/AuthenticationSession.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@ enum FusionAuthStatusCode {
1515
export class AuthenticationSession {
1616
public authToken = '';
1717

18+
public refreshToken = '';
19+
1820
public readonly authContext;
1921

22+
private authTokenExpiresAt = 0;
23+
2024
private readonly fusionAuthClient;
2125

26+
private readonly fusionAuthAppId = process.env.FUSION_AUTH_APP_ID ?? '';
27+
2228
private twoFactorId = '';
2329

2430
private twoFactorMethods: TwoFactorMethod[] = [];
@@ -32,6 +38,32 @@ export class AuthenticationSession {
3238
this.promptForPassword();
3339
}
3440

41+
public obtainNewAuthTokenUsingRefreshToken(): void {
42+
this.fusionAuthClient.exchangeRefreshTokenForAccessToken(this.refreshToken, '', '', '', '')
43+
.then((clientResponse) => {
44+
this.authToken = clientResponse.response.access_token ?? '';
45+
})
46+
.catch((clientResponse: unknown) => {
47+
const message = isPartialClientResponse(clientResponse)
48+
? clientResponse.exception.message
49+
: '';
50+
logger.warn(`Error obtaining refresh token : ${message}`);
51+
this.authContext.reject();
52+
});
53+
}
54+
55+
public tokenExpired(): boolean {
56+
const expirationDate = new Date(this.authTokenExpiresAt);
57+
return expirationDate <= new Date();
58+
}
59+
60+
public tokenWouldExpireSoon(minutes = 5): boolean {
61+
const expirationDate = new Date(this.authTokenExpiresAt);
62+
const currentTime = new Date();
63+
const timeDifferenceMinutes = (expirationDate.getTime() - currentTime.getTime()) / (1000 * 60);
64+
return timeDifferenceMinutes <= minutes;
65+
}
66+
3567
private promptForPassword(): void {
3668
this.authContext.prompt(
3769
{
@@ -46,6 +78,7 @@ export class AuthenticationSession {
4678

4779
private processPasswordResponse([password]: string[]): void {
4880
this.fusionAuthClient.login({
81+
applicationId: this.fusionAuthAppId,
4982
loginId: this.authContext.username,
5083
password,
5184
}).then((clientResponse) => {
@@ -57,6 +90,8 @@ export class AuthenticationSession {
5790
username: this.authContext.username,
5891
});
5992
this.authToken = clientResponse.response.token;
93+
this.authTokenExpiresAt = clientResponse.response.tokenExpirationInstant ?? 0;
94+
this.refreshToken = clientResponse.response.refreshToken ?? '';
6095
this.authContext.accept();
6196
return;
6297
}

src/classes/SftpSessionHandler.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,12 @@ export class SftpSessionHandler {
10701070
}
10711071

10721072
private getCurrentPermanentFileSystem(): PermanentFileSystem {
1073+
if (
1074+
this.authenticationSession.tokenExpired()
1075+
|| this.authenticationSession.tokenWouldExpireSoon()
1076+
) {
1077+
this.authenticationSession.obtainNewAuthTokenUsingRefreshToken();
1078+
}
10731079
return this.permanentFileSystemManager
10741080
.getCurrentPermanentFileSystemForUser(
10751081
this.authenticationSession.authContext.username,

0 commit comments

Comments
 (0)