Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add global requestTimeoutInSeconds setting to OidcClientSettings #1430

Merged
merged 10 commits into from
Jun 21, 2024
5 changes: 4 additions & 1 deletion docs/oidc-client-ts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ export interface OidcClientSettings {
prompt?: string;
redirect_uri: string;
refreshTokenAllowedScope?: string | undefined;
requestTimeoutInSeconds?: number | undefined;
resource?: string | string[];
response_mode?: "query" | "fragment";
response_type?: string;
Expand All @@ -382,7 +383,7 @@ export interface OidcClientSettings {

// @public
export class OidcClientSettingsStore {
constructor({ authority, metadataUrl, metadata, signingKeys, metadataSeed, client_id, client_secret, response_type, scope, redirect_uri, post_logout_redirect_uri, client_authentication, prompt, display, max_age, ui_locales, acr_values, resource, response_mode, filterProtocolClaims, loadUserInfo, staleStateAgeInSeconds, mergeClaimsStrategy, disablePKCE, stateStore, revokeTokenAdditionalContentTypes, fetchRequestCredentials, refreshTokenAllowedScope, extraQueryParams, extraTokenParams, extraHeaders, }: OidcClientSettings);
constructor({ authority, metadataUrl, metadata, signingKeys, metadataSeed, client_id, client_secret, response_type, scope, redirect_uri, post_logout_redirect_uri, client_authentication, prompt, display, max_age, ui_locales, acr_values, resource, response_mode, filterProtocolClaims, loadUserInfo, requestTimeoutInSeconds, staleStateAgeInSeconds, mergeClaimsStrategy, disablePKCE, stateStore, revokeTokenAdditionalContentTypes, fetchRequestCredentials, refreshTokenAllowedScope, extraQueryParams, extraTokenParams, extraHeaders, }: OidcClientSettings);
// (undocumented)
readonly acr_values: string | undefined;
// (undocumented)
Expand Down Expand Up @@ -430,6 +431,8 @@ export class OidcClientSettingsStore {
// (undocumented)
readonly refreshTokenAllowedScope: string | undefined;
// (undocumented)
readonly requestTimeoutInSeconds: number | undefined;
// (undocumented)
readonly resource: string | string[] | undefined;
// (undocumented)
readonly response_mode: "query" | "fragment" | undefined;
Expand Down
4 changes: 3 additions & 1 deletion src/JsonService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type JwtHandler = (text: string) => Promise<Record<string, unknown>>;
export interface GetJsonOpts {
token?: string;
credentials?: RequestCredentials;
timeoutInSeconds?: number;
}

/**
Expand Down Expand Up @@ -77,6 +78,7 @@ export class JsonService {
public async getJson(url: string, {
token,
credentials,
timeoutInSeconds,
}: GetJsonOpts = {}): Promise<Record<string, unknown>> {
const logger = this._logger.create("getJson");
const headers: HeadersInit = {
Expand All @@ -92,7 +94,7 @@ export class JsonService {
let response: Response;
try {
logger.debug("url:", url);
response = await this.fetchWithTimeout(url, { method: "GET", headers, credentials });
response = await this.fetchWithTimeout(url, { method: "GET", headers, timeoutInSeconds, credentials });
}
catch (err) {
logger.error("Network Error");
Expand Down
8 changes: 4 additions & 4 deletions src/MetadataService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe("MetadataService", () => {
await subject.getMetadata();

// assert
expect(getJsonMock).toHaveBeenCalledWith("authority/.well-known/openid-configuration", { credentials: "same-origin" });
expect(getJsonMock).toHaveBeenCalledWith("authority/.well-known/openid-configuration", { credentials: "same-origin", timeoutInSeconds: undefined } );
});

it("should fail when no authority or metadataUrl configured", async () => {
Expand Down Expand Up @@ -93,7 +93,7 @@ describe("MetadataService", () => {
await subject.getMetadata();

// assert
expect(getJsonMock).toHaveBeenCalledWith("http://sts/metadata", { credentials: "same-origin" });
expect(getJsonMock).toHaveBeenCalledWith("http://sts/metadata", { credentials: "same-origin", timeoutInSeconds: undefined });
});

it("should return metadata from json call", async () => {
Expand Down Expand Up @@ -196,7 +196,7 @@ describe("MetadataService", () => {
await subject.getMetadata();

// assert
expect(getJsonMock).toHaveBeenCalledWith("http://sts/metadata", { credentials: "include" });
expect(getJsonMock).toHaveBeenCalledWith("http://sts/metadata", { credentials: "include", timeoutInSeconds: undefined });
});
});

Expand Down Expand Up @@ -535,7 +535,7 @@ describe("MetadataService", () => {
await subject.getSigningKeys();

// assert
expect(getJsonMock).toHaveBeenCalledWith("http://sts/metadata/keys");
expect(getJsonMock).toHaveBeenCalledWith("http://sts/metadata/keys", { "timeoutInSeconds": undefined });
});

it("should return keys from jwks_uri", async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/MetadataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class MetadataService {
}

logger.debug("getting metadata from", this._metadataUrl);
const metadata = await this._jsonService.getJson(this._metadataUrl, { credentials: this._fetchRequestCredentials });
const metadata = await this._jsonService.getJson(this._metadataUrl, { credentials: this._fetchRequestCredentials, timeoutInSeconds: this._settings.requestTimeoutInSeconds });

logger.debug("merging remote JSON with seed metadata");
this._metadata = Object.assign({}, this._settings.metadataSeed, metadata);
Expand Down Expand Up @@ -133,7 +133,7 @@ export class MetadataService {
const jwks_uri = await this.getKeysEndpoint(false);
logger.debug("got jwks_uri", jwks_uri);

const keySet = await this._jsonService.getJson(jwks_uri);
const keySet = await this._jsonService.getJson(jwks_uri, { timeoutInSeconds: this._settings.requestTimeoutInSeconds });
logger.debug("got key set", keySet);

if (!Array.isArray(keySet.keys)) {
Expand Down
8 changes: 8 additions & 0 deletions src/OidcClientSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ export interface OidcClientSettings {
* Only scopes in this list will be passed in the token refresh request.
*/
refreshTokenAllowedScope?: string | undefined;

/**
* Defines request timeouts globally across all requests made to the authorisation server
*/
requestTimeoutInSeconds?: number | undefined;
}

/**
Expand Down Expand Up @@ -188,6 +193,7 @@ export class OidcClientSettingsStore {
public readonly fetchRequestCredentials: RequestCredentials;
public readonly refreshTokenAllowedScope: string | undefined;
public readonly disablePKCE: boolean;
public readonly requestTimeoutInSeconds: number | undefined;

public constructor({
// metadata related
Expand All @@ -201,6 +207,7 @@ export class OidcClientSettingsStore {
// behavior flags
filterProtocolClaims = true,
loadUserInfo = false,
requestTimeoutInSeconds ,
dbfr3qs marked this conversation as resolved.
Show resolved Hide resolved
staleStateAgeInSeconds = DefaultStaleStateAgeInSeconds,
mergeClaimsStrategy = { array: "replace" },
disablePKCE = false,
Expand Down Expand Up @@ -257,6 +264,7 @@ export class OidcClientSettingsStore {
this.revokeTokenAdditionalContentTypes = revokeTokenAdditionalContentTypes;

this.fetchRequestCredentials = fetchRequestCredentials ? fetchRequestCredentials : "same-origin";
this.requestTimeoutInSeconds = requestTimeoutInSeconds;

if (stateStore) {
this.stateStore = stateStore;
Expand Down
11 changes: 8 additions & 3 deletions src/TokenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,12 @@ export class TokenClient {
const url = await this._metadataService.getTokenEndpoint(false);
logger.debug("got token endpoint");

const response = await this._jsonService.postForm(url, { body: params, basicAuth, initCredentials: this._settings.fetchRequestCredentials });
const response = await this._jsonService.postForm(url, {
body: params,
basicAuth,
timeoutInSeconds: this._settings.requestTimeoutInSeconds,
initCredentials: this._settings.fetchRequestCredentials,
});
logger.debug("got response");

return response;
Expand Down Expand Up @@ -175,7 +180,7 @@ export class TokenClient {
const url = await this._metadataService.getTokenEndpoint(false);
logger.debug("got token endpoint");

const response = await this._jsonService.postForm(url, { body: params, basicAuth, initCredentials: this._settings.fetchRequestCredentials });
const response = await this._jsonService.postForm(url, { body: params, basicAuth, timeoutInSeconds: this._settings.requestTimeoutInSeconds, initCredentials: this._settings.fetchRequestCredentials });
logger.debug("got response");

return response;
Expand Down Expand Up @@ -262,7 +267,7 @@ export class TokenClient {
params.set("client_secret", this._settings.client_secret);
}

await this._jsonService.postForm(url, { body: params });
await this._jsonService.postForm(url, { body: params, timeoutInSeconds: this._settings.requestTimeoutInSeconds });
logger.debug("got response");
}
}
1 change: 1 addition & 0 deletions src/UserInfoService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class UserInfoService {
const claims = await this._jsonService.getJson(url, {
token,
credentials: this._settings.fetchRequestCredentials,
timeoutInSeconds: this._settings.requestTimeoutInSeconds,
});
logger.debug("got claims", claims);

Expand Down
4 changes: 2 additions & 2 deletions src/UserManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ export class UserManager {
protected async _useRefreshToken(args: UseRefreshTokenArgs): Promise<User> {
const response = await this._client.useRefreshToken({
...args,
dbfr3qs marked this conversation as resolved.
Show resolved Hide resolved
timeoutInSeconds: this.settings.silentRequestTimeoutInSeconds,
timeoutInSeconds: this.settings.requestTimeoutInSeconds ?? this.settings.silentRequestTimeoutInSeconds,
dbfr3qs marked this conversation as resolved.
Show resolved Hide resolved
});
const user = new User({ ...args.state, ...response });

Expand All @@ -344,7 +344,7 @@ export class UserManager {
/**
*
* Notify the parent window of response (callback) from the authorization endpoint.
* It is recommend to use {@link UserManager.signinCallback} instead.
* It is recommended to use {@link UserManager.signinCallback} instead.
*
* @returns A promise
*
Expand Down
Loading