Skip to content

Commit b1a9e5f

Browse files
fflorentatropos112
andauthored
OIDC: allow configuring the request timeout (#1177)
Add IdP timeout, controlled by env var GRIST_OIDC_SP_HTTP_TIMEOUT --------- Co-authored-by: atropos <[email protected]>
1 parent 16d2460 commit b1a9e5f

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

app/server/lib/OIDCConfig.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
* A JSON object with extra client metadata to pass to openid-client. Optional.
4848
* Be aware that setting this object may override any other values passed to the openid client.
4949
* More info: https://github.com/panva/node-openid-client/tree/main/docs#new-clientmetadata-jwks-options
50-
*
50+
* env GRIST_OIDC_SP_HTTP_TIMEOUT
51+
* The timeout in milliseconds for HTTP requests to the IdP. The default value is set to 3500 by the
52+
* openid-client library. See: https://github.com/panva/node-openid-client/blob/main/docs/README.md#customizing-http-requests
5153
*
5254
* This version of OIDCConfig has been tested with Keycloak OIDC IdP following the instructions
5355
* at:
@@ -66,7 +68,7 @@
6668
import * as express from 'express';
6769
import { GristLoginSystem, GristServer } from './GristServer';
6870
import {
69-
Client, ClientMetadata, Issuer, errors as OIDCError, TokenSet, UserinfoResponse
71+
Client, ClientMetadata, custom, Issuer, errors as OIDCError, TokenSet, UserinfoResponse
7072
} from 'openid-client';
7173
import { Sessions } from './Sessions';
7274
import log from 'app/server/lib/log';
@@ -137,6 +139,9 @@ export class OIDCConfig {
137139
envVar: 'GRIST_OIDC_IDP_CLIENT_SECRET',
138140
censor: true,
139141
});
142+
const httpTimeout = section.flag('httpTimeout').readInt({
143+
envVar: 'GRIST_OIDC_SP_HTTP_TIMEOUT',
144+
});
140145
this._namePropertyKey = section.flag('namePropertyKey').readString({
141146
envVar: 'GRIST_OIDC_SP_PROFILE_NAME_ATTR',
142147
});
@@ -173,6 +178,9 @@ export class OIDCConfig {
173178
this._protectionManager = new ProtectionsManager(enabledProtections);
174179

175180
this._redirectUrl = new URL(CALLBACK_URL, spHost).href;
181+
custom.setHttpOptionsDefaults({
182+
...(httpTimeout !== undefined ? {timeout: httpTimeout} : {}),
183+
});
176184
await this._initClient({ issuerUrl, clientId, clientSecret, extraMetadata });
177185

178186
if (this._client.issuer.metadata.end_session_endpoint === undefined &&

test/server/lib/OIDCConfig.ts

+50-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {Sessions} from "app/server/lib/Sessions";
55
import log from "app/server/lib/log";
66
import {assert} from "chai";
77
import Sinon from "sinon";
8-
import {Client, generators, errors as OIDCError} from "openid-client";
8+
import {Client, custom, generators, errors as OIDCError} from "openid-client";
99
import express from "express";
1010
import _ from "lodash";
1111
import {RequestWithLogin} from "app/server/lib/Authorizer";
@@ -192,6 +192,55 @@ describe('OIDCConfig', () => {
192192
});
193193
});
194194
});
195+
196+
describe('GRIST_OIDC_SP_HTTP_TIMEOUT', function () {
197+
[
198+
{
199+
itMsg: 'when omitted should not override openid-client default value',
200+
expectedUserDefinedHttpOptions: {}
201+
},
202+
{
203+
itMsg: 'should reject when the provided value is not a number',
204+
env: {
205+
GRIST_OIDC_SP_HTTP_TIMEOUT: '__NOT_A_NUMBER__',
206+
},
207+
expectedErrorMsg: /__NOT_A_NUMBER__ does not look like a number/,
208+
},
209+
{
210+
itMsg: 'should override openid-client timeout accordingly to the provided value',
211+
env: {
212+
GRIST_OIDC_SP_HTTP_TIMEOUT: '10000',
213+
},
214+
shouldSetTimeout: true,
215+
expectedUserDefinedHttpOptions: {
216+
timeout: 10000
217+
}
218+
},
219+
{
220+
itMsg: 'should allow disabling the timeout by having its value set to 0',
221+
env: {
222+
GRIST_OIDC_SP_HTTP_TIMEOUT: '0',
223+
},
224+
expectedUserDefinedHttpOptions: {
225+
timeout: 0
226+
}
227+
}
228+
].forEach(ctx => {
229+
it(ctx.itMsg, async () => {
230+
const setHttpOptionsDefaultsStub = sandbox.stub(custom, 'setHttpOptionsDefaults');
231+
setEnvVars();
232+
Object.assign(process.env, ctx.env);
233+
const promise = OIDCConfigStubbed.buildWithStub();
234+
if (ctx.expectedErrorMsg) {
235+
await assert.isRejected(promise, ctx.expectedErrorMsg);
236+
} else {
237+
await assert.isFulfilled(promise, 'initOIDC should have been fulfilled');
238+
assert.isTrue(setHttpOptionsDefaultsStub.calledOnce, 'Should have called custom.setHttpOptionsDefaults');
239+
assert.deepEqual(setHttpOptionsDefaultsStub.firstCall.args[0], ctx.expectedUserDefinedHttpOptions);
240+
}
241+
});
242+
});
243+
});
195244
});
196245

197246
describe('GRIST_OIDC_IDP_ENABLED_PROTECTIONS', () => {

0 commit comments

Comments
 (0)