Skip to content

Commit 2090319

Browse files
authored
Abstract logout-causing error type from tokenRefreshFunction calls (#4765)
* Abstract logout-causing error type from tokenRefreshFunction calls Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
1 parent f4de883 commit 2090319

File tree

4 files changed

+48
-7
lines changed

4 files changed

+48
-7
lines changed

spec/unit/oidc/tokenRefresher.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ limitations under the License.
2020

2121
import fetchMock from "fetch-mock-jest";
2222

23-
import { OidcTokenRefresher } from "../../../src";
23+
import { OidcTokenRefresher, TokenRefreshLogoutError } from "../../../src";
2424
import { logger } from "../../../src/logger";
2525
import { makeDelegatedAuthConfig } from "../../test-utils/oidc";
2626

@@ -266,5 +266,27 @@ describe("OidcTokenRefresher", () => {
266266
refreshToken: "second-new-refresh-token",
267267
});
268268
});
269+
270+
it("should throw TokenRefreshLogoutError when expired", async () => {
271+
fetchMock.post(
272+
config.token_endpoint,
273+
{
274+
status: 400,
275+
headers: {
276+
"Content-Type": "application/json",
277+
},
278+
body: {
279+
error: "invalid_grant",
280+
error_description: "The provided access grant is invalid, expired, or revoked.",
281+
},
282+
},
283+
{ overwriteRoutes: true },
284+
);
285+
286+
const refresher = new OidcTokenRefresher(authConfig.issuer, clientId, redirectUri, deviceId, idTokenClaims);
287+
await refresher.oidcClientReady;
288+
289+
await expect(refresher.doRefreshAccessToken("refresh-token")).rejects.toThrow(TokenRefreshLogoutError);
290+
});
269291
});
270292
});

src/http-api/errors.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,17 @@ export class TokenRefreshError extends Error {
212212
return "TokenRefreshError";
213213
}
214214
}
215+
216+
/**
217+
* Construct a TokenRefreshError. This indicates that a request failed due to the token being expired,
218+
* and attempting to refresh said token failed in a way indicative of token invalidation.
219+
*/
220+
export class TokenRefreshLogoutError extends Error {
221+
public constructor(cause?: Error) {
222+
super(cause?.message ?? "");
223+
}
224+
225+
public get name(): string {
226+
return "TokenRefreshLogoutError";
227+
}
228+
}

src/http-api/fetch.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ limitations under the License.
1818
* This is an internal module. See {@link MatrixHttpApi} for the public class.
1919
*/
2020

21-
import { ErrorResponse as OidcAuthError } from "oidc-client-ts";
22-
2321
import { checkObjectHasKeys, encodeParams } from "../utils.ts";
2422
import { type TypedEventEmitter } from "../models/typed-event-emitter.ts";
2523
import { Method } from "./method.ts";
26-
import { ConnectionError, MatrixError, TokenRefreshError } from "./errors.ts";
24+
import { ConnectionError, MatrixError, TokenRefreshError, TokenRefreshLogoutError } from "./errors.ts";
2725
import {
2826
HttpApiEvent,
2927
type HttpApiEventHandlerMap,
@@ -234,7 +232,8 @@ export class FetchHttpApi<O extends IHttpOpts> {
234232
return TokenRefreshOutcome.Success;
235233
} catch (error) {
236234
this.opts.logger?.warn("Failed to refresh token", error);
237-
if (error instanceof OidcAuthError || error instanceof MatrixError) {
235+
// If we get a TokenError or MatrixError, we should log out, otherwise assume transient
236+
if (error instanceof TokenRefreshLogoutError || error instanceof MatrixError) {
238237
return TokenRefreshOutcome.Logout;
239238
}
240239
return TokenRefreshOutcome.Failure;

src/oidc/tokenRefresher.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { type IdTokenClaims, OidcClient, WebStorageStateStore } from "oidc-client-ts";
17+
import { type IdTokenClaims, OidcClient, WebStorageStateStore, ErrorResponse } from "oidc-client-ts";
1818

19-
import { type AccessTokens } from "../http-api/index.ts";
19+
import { type AccessTokens, TokenRefreshLogoutError } from "../http-api/index.ts";
2020
import { generateScope } from "./authorize.ts";
2121
import { discoverAndValidateOIDCIssuerWellKnown } from "./discovery.ts";
2222
import { logger } from "../logger.ts";
@@ -104,6 +104,12 @@ export class OidcTokenRefresher {
104104
try {
105105
const tokens = await this.inflightRefreshRequest;
106106
return tokens;
107+
} catch (e) {
108+
// If we encounter an OIDC error then signal that it should cause a logout by upgrading it to a TokenRefreshLogoutError
109+
if (e instanceof ErrorResponse) {
110+
throw new TokenRefreshLogoutError(e);
111+
}
112+
throw e;
107113
} finally {
108114
this.inflightRefreshRequest = undefined;
109115
}

0 commit comments

Comments
 (0)