From 75ab2d7fdac2b7e44541d80ea7e6bf4faf5cca55 Mon Sep 17 00:00:00 2001 From: Oskar Eichler <62393985+OskarEichler@users.noreply.github.com> Date: Fri, 28 Aug 2026 04:18:15 +0300 Subject: [PATCH] fix: reject malformed headers and non-finite timeouts --- .changeset/appauth-option-validation.md | 5 +++++ docs/docs/usage/config.md | 2 +- docs/docs/usage/register.md | 2 +- packages/react-native-app-auth/index.js | 14 +++++++++----- 4 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 .changeset/appauth-option-validation.md diff --git a/.changeset/appauth-option-validation.md b/.changeset/appauth-option-validation.md new file mode 100644 index 000000000..8562b193f --- /dev/null +++ b/.changeset/appauth-option-validation.md @@ -0,0 +1,5 @@ +--- +"react-native-app-auth": patch +--- + +Reject null/array custom header groups and array additionalHeaders with the normal configuration error. Validate finite non-negative timeouts before native conversion. Use every() instead of allocating filtered arrays for header value checks. diff --git a/docs/docs/usage/config.md b/docs/docs/usage/config.md index f1b9d5a0c..45a20a79b 100644 --- a/docs/docs/usage/config.md +++ b/docs/docs/usage/config.md @@ -51,4 +51,4 @@ See specific example [configurations for your provider](/docs/category/providers - **androidPrefersEphemeralSession** - (`boolean`) (default: `false`) _ANDROID_ indicates whether the session should ask the browser for a private authentication session when supported. - **androidAllowCustomBrowsers** - (`string[]`) (default: undefined) _ANDROID_ override the used browser for authorization. If no value is provided, all browsers are allowed. - **androidTrustedWebActivity** - (`boolean`) (default: `false`) _ANDROID_ Use [`EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY`](https://developer.chrome.com/docs/android/trusted-web-activity/) when opening web view. -- **connectionTimeoutSeconds** - (`number`) configure the request timeout interval in seconds. This must be a positive number. The default values are 60 seconds on iOS and 15 seconds on Android. +- **connectionTimeoutSeconds** - (`number`) configure the request timeout interval in seconds. Use a finite, non-negative number; zero preserves the native platform's timeout behavior. The default values are 60 seconds on iOS and 15 seconds on Android. diff --git a/docs/docs/usage/register.md b/docs/docs/usage/register.md index 236ab130b..0dd364d8d 100644 --- a/docs/docs/usage/register.md +++ b/docs/docs/usage/register.md @@ -32,7 +32,7 @@ const registerResult = await register(registerConfig); `hello=world&foo=bar` to the authorization request. - **dangerouslyAllowInsecureHttpRequests** - (`boolean`) _ANDROID_ same as in authorization config - **customHeaders** - (`object`) _ANDROID_ same as in authorization config -- **connectionTimeoutSeconds** - (`number`) configure the request timeout interval in seconds. This must be a positive number. The default values are 60 seconds on iOS and 15 seconds on Android. +- **connectionTimeoutSeconds** - (`number`) same timeout rules as in [Configuration](/docs/usage/config). #### registerResult diff --git a/packages/react-native-app-auth/index.js b/packages/react-native-app-auth/index.js index a49c3c58c..2e8a9ff23 100644 --- a/packages/react-native-app-auth/index.js +++ b/packages/react-native-app-auth/index.js @@ -71,9 +71,9 @@ const validateHeaders = headers => { ); Object.values(headers).forEach(value => { - invariant(typeof value === 'object', customHeaderTypeErrorMessage); + invariant(value && typeof value === 'object' && !Array.isArray(value), customHeaderTypeErrorMessage); invariant( - Object.values(value).filter(key => typeof key !== 'string').length === 0, + Object.values(value).every(header => typeof header === 'string'), customHeaderTypeErrorMessage ); }); @@ -86,19 +86,23 @@ const validateAdditionalHeaders = headers => { const errorMessage = 'Config error: additionalHeaders must be { [key: string]: string }'; - invariant(typeof headers === 'object', errorMessage); + invariant(typeof headers === 'object' && !Array.isArray(headers), errorMessage); invariant( - Object.values(headers).filter(key => typeof key !== 'string').length === 0, + Object.values(headers).every(header => typeof header === 'string'), errorMessage ); }; const validateConnectionTimeoutSeconds = timeout => { - if (!timeout) { + if (timeout === undefined) { return; } invariant(typeof timeout === 'number', 'Config error: connectionTimeoutSeconds must be a number'); + invariant( + Number.isFinite(timeout) && timeout >= 0, + 'Config error: connectionTimeoutSeconds must be a finite, non-negative number' + ); }; export const SECOND_IN_MS = 1000;