Skip to content

Commit 3d60933

Browse files
authored
Fixes for token refresh logic. (#1252)
1 parent 5b17a81 commit 3d60933

File tree

11 files changed

+65
-39
lines changed

11 files changed

+65
-39
lines changed

src/authentication/IAuthenticator.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { AccessToken } from "./accessToken";
22

33
export interface IAuthenticator {
4+
/**
5+
* Returns access token as a string.
6+
*/
7+
getAccessTokenAsString(): Promise<string>;
8+
49
/**
510
* Returns access token for current session.
611
*/
7-
getAccessToken(): Promise<string>;
12+
getAccessToken(): Promise<AccessToken>;
813

914
/**
1015
* Sets new token for the session.

src/authentication/accessTokenRefresher.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as Constants from "./../constants";
22
import { ISettingsProvider } from "@paperbits/common/configuration";
3-
import { EventManager } from "@paperbits/common/events";
43
import { HttpClient } from "@paperbits/common/http";
54
import { Logger } from "@paperbits/common/logging";
65
import { KnownHttpHeaders } from "../models/knownHttpHeaders";
@@ -10,13 +9,13 @@ import { AccessToken, IAuthenticator } from "./../authentication";
109

1110
export class AccessTokenRefrsher {
1211
constructor(
13-
eventManager: EventManager,
1412
private readonly settingsProvider: ISettingsProvider,
1513
private readonly authenticator: IAuthenticator,
1614
private readonly httpClient: HttpClient,
1715
private readonly logger: Logger
1816
) {
19-
eventManager.addEventListener("authenticated", this.refreshToken.bind(this));
17+
this.refreshToken = this.refreshToken.bind(this);
18+
setInterval(() => this.refreshToken(), 60 * 1000);
2019
}
2120

2221
private async refreshToken(): Promise<void> {
@@ -37,10 +36,17 @@ export class AccessTokenRefrsher {
3736
return;
3837
}
3938

39+
const expiresInMs = accessToken.expiresInMs();
40+
const refreshBufferMs = 5 * 60 * 1000; // 5 min
41+
42+
if (expiresInMs > refreshBufferMs) {
43+
return;
44+
}
45+
4046
const response = await this.httpClient.send({
4147
method: "GET",
4248
url: `${managementApiUrl}${Utils.ensureLeadingSlash("/identity")}?api-version=${Constants.managementApiVersion}`,
43-
headers: [{ name: "Authorization", value: accessToken }]
49+
headers: [{ name: "Authorization", value: accessToken.toString() }]
4450
});
4551

4652
const accessTokenHeader = response.headers.find(x => x.name.toLowerCase() === KnownHttpHeaders.OcpApimSasToken.toLowerCase());

src/components/app/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class App {
3131
}
3232

3333
try {
34-
const token = await this.authenticator.getAccessToken();
34+
const token = await this.authenticator.getAccessTokenAsString();
3535

3636
if (!token) {
3737
const managementApiAccessToken = settings["managementApiAccessToken"];

src/components/content/content.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class ContentWorkshop {
3030
}
3131

3232
try {
33-
const accessToken = await this.authenticator.getAccessToken();
33+
const accessToken = await this.authenticator.getAccessTokenAsString();
3434

3535
const publishRootUrl = await this.settingsProvider.getSetting<string>("backendUrl") || "";
3636

src/components/defaultAuthenticator.ts

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,44 @@ import { IAuthenticator, AccessToken } from "./../authentication";
44
export class DefaultAuthenticator implements IAuthenticator {
55
constructor(private readonly eventManager: EventManager) { }
66

7-
public async getAccessToken(): Promise<string> {
8-
const accessToken = sessionStorage.getItem("accessToken");
9-
10-
if (!accessToken && window.location.pathname.startsWith("/signin-sso")) {
7+
private runSsoFlow(): Promise<void> {
8+
return new Promise<void>(async () => {
119
const url = new URL(location.href);
1210
const queryParams = new URLSearchParams(url.search);
1311
const tokenValue = queryParams.get("token");
14-
const token = AccessToken.parse(`SharedAccessSignature ${tokenValue}`);
12+
const tokenString = `SharedAccessSignature ${tokenValue}`;
13+
const token = AccessToken.parse(tokenString);
14+
1515
await this.setAccessToken(token);
1616

1717
const returnUrl = queryParams.get("returnUrl") || "/";
18+
19+
// wait for redirect to happen, deliberatly not resolving the promise
1820
window.location.assign(returnUrl);
21+
});
22+
}
23+
24+
public async getAccessToken(): Promise<AccessToken> {
25+
if (location.pathname.startsWith("/signin-sso")) {
26+
await this.runSsoFlow();
27+
}
28+
29+
const storedToken = sessionStorage.getItem("accessToken");
30+
31+
if (storedToken) {
32+
const accessToken = AccessToken.parse(storedToken);
33+
34+
if (!accessToken.isExpired()) {
35+
return accessToken;
36+
}
1937
}
20-
return accessToken;
38+
39+
return null;
40+
}
41+
42+
public async getAccessTokenAsString(): Promise<string> {
43+
const accessToken = await this.getAccessToken();
44+
return accessToken?.toString();
2145
}
2246

2347
public async setAccessToken(accessToken: AccessToken): Promise<void> {
@@ -27,27 +51,14 @@ export class DefaultAuthenticator implements IAuthenticator {
2751
}
2852

2953
sessionStorage.setItem("accessToken", accessToken.toString());
30-
31-
const expiresInMs = accessToken.expiresInMs();
32-
const refreshBufferMs = 5 * 60 * 1000; // 5 min
33-
const nextRefreshInMs = expiresInMs - refreshBufferMs;
34-
35-
if (expiresInMs < refreshBufferMs) {
36-
// Refresh immediately
37-
this.eventManager.dispatchEvent("authenticated");
38-
}
39-
else {
40-
// Schedule refresh 5 min before expiration.
41-
setTimeout(() => this.eventManager.dispatchEvent("authenticated"), nextRefreshInMs);
42-
}
4354
}
4455

4556
public clearAccessToken(): void {
4657
sessionStorage.removeItem("accessToken");
4758
}
4859

4960
public async isAuthenticated(): Promise<boolean> {
50-
const accessToken = await this.getAccessToken();
61+
const accessToken = await this.getAccessTokenAsString();
5162

5263
if (!accessToken) {
5364
return false;
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
import { IAuthenticator, AccessToken } from "../authentication";
22

33
export class StaticAuthenticator implements IAuthenticator {
4-
private accessToken: string;
4+
private accessToken: AccessToken;
55

6-
public async getAccessToken(): Promise<string> {
6+
public async getAccessToken(): Promise<AccessToken> {
77
return this.accessToken;
88
}
99

10+
public async getAccessTokenAsString(): Promise<string> {
11+
return this.accessToken.toString();
12+
}
13+
1014
public async setAccessToken(accessToken: AccessToken): Promise<void> {
11-
this.accessToken = accessToken.toString();
15+
this.accessToken = accessToken;
1216
}
1317

1418
public clearAccessToken(): void {
1519
this.accessToken = undefined;
1620
}
1721

1822
public async isAuthenticated(): Promise<boolean> {
19-
const accessToken = await this.getAccessToken();
23+
const accessToken = await this.getAccessTokenAsString();
2024
return !!accessToken;
2125
}
2226
}

src/routing/signOutRouteGuard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class SignOutRouteGuard implements RouteGuard {
2323
const isDelegationEnabled = await this.tenantService.isDelegationEnabled();
2424

2525
if (isDelegationEnabled) {
26-
const token = await this.authenticator.getAccessToken();
26+
const token = await this.authenticator.getAccessTokenAsString();
2727

2828
if (token) {
2929
try {

src/services/backendService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export class BackendService {
7070
}
7171

7272
public async sendChangePassword(changePasswordRequest: ChangePasswordRequest): Promise<void> {
73-
const authToken = await this.authenticator.getAccessToken();
73+
const authToken = await this.authenticator.getAccessTokenAsString();
7474

7575
if (!authToken) {
7676
throw Error("Auth token not found");
@@ -94,7 +94,7 @@ export class BackendService {
9494
}
9595

9696
public async getDelegationUrl(action: DelegationAction, delegationParameters: {}): Promise<string> {
97-
const authToken = await this.authenticator.getAccessToken();
97+
const authToken = await this.authenticator.getAccessTokenAsString();
9898

9999
if (!authToken) {
100100
throw Error("Auth token not found");

src/services/mapiClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class MapiClient {
116116
const portalHeader = httpRequest.headers.find(header => header.name === Constants.portalHeaderName);
117117

118118
if (!authHeader?.value) {
119-
const accessToken = await this.authenticator.getAccessToken();
119+
const accessToken = await this.authenticator.getAccessTokenAsString();
120120

121121
if (accessToken) {
122122
httpRequest.headers.push({ name: KnownHttpHeaders.Authorization, value: `${accessToken}` });

src/services/provisioningService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class ProvisionService {
4040
try {
4141
const dataObj = await this.fetchData(dataUrl);
4242
const keys = Object.keys(dataObj);
43-
const accessToken = await this.authenticator.getAccessToken();
43+
const accessToken = await this.authenticator.getAccessTokenAsString();
4444

4545
if (!accessToken) {
4646
this.viewManager.notifyError(`Unable to setup website`, `Management API access token is empty or invald.`);
@@ -79,7 +79,7 @@ export class ProvisionService {
7979

8080
private async cleanupContent(): Promise<void> {
8181
const managementApiUrl = await this.getManagementUrl();
82-
const accessToken = await this.authenticator.getAccessToken();
82+
const accessToken = await this.authenticator.getAccessTokenAsString();
8383

8484
try {
8585
const request: HttpRequest = {

0 commit comments

Comments
 (0)