Skip to content

Commit 580372d

Browse files
author
Austin McBee
authored
add configurable timeout fn param (connectionTimeoutSeconds/connectionTimeoutMillis) (FormidableLabs#701)
* add connectionTimeoutSeconds fn param to JS/iOS methods and connectionTimeoutMillis to java methods * update readme * typo * fix type def * overload createConnectionBuilder method and add default timeout values * update readme * remove 0 check * remove 0 check in obj-c configureUrlSession method
1 parent 6bb6710 commit 580372d

File tree

8 files changed

+342
-59
lines changed

8 files changed

+342
-59
lines changed

Example/App.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,19 @@ const App = () => {
4747
React.useEffect(() => {
4848
prefetchConfiguration({
4949
warmAndPrefetchChrome: true,
50-
...configs.identityserver
50+
connectionTimeoutSeconds: 5,
51+
...configs.identityserver,
5152
});
5253
}, []);
5354

5455
const handleAuthorize = useCallback(
5556
async provider => {
5657
try {
5758
const config = configs[provider];
58-
const newAuthState = await authorize(config);
59+
const newAuthState = await authorize({
60+
...config,
61+
connectionTimeoutSeconds: 5,
62+
});
5963

6064
setAuthState({
6165
hasLoggedInOnce: true,

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ This is the result from the auth server:
151151
- **scopes** - ([`string`]) the scopes the user has agreed to be granted
152152
- **authorizationCode** - (`string`) the authorization code (only if `skipCodeExchange=true`)
153153
- **codeVerifier** - (`string`) the codeVerifier value used for the PKCE exchange (only if both `skipCodeExchange=true` and `usePKCE=true`)
154+
- **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.
154155

155156
### `refresh`
156157

@@ -201,12 +202,12 @@ This method will logout a user, as per the [OpenID Connect RP Initiated Logout](
201202
import { logout } from 'react-native-app-auth';
202203

203204
const config = {
204-
issuer: '<YOUR_ISSUER_URL>'
205+
issuer: '<YOUR_ISSUER_URL>',
205206
};
206207

207208
const result = await logout(config, {
208209
idToken: '<ID_TOKEN>',
209-
postLogoutRedirectUrl: '<POST_LOGOUT_URL>'
210+
postLogoutRedirectUrl: '<POST_LOGOUT_URL>',
210211
});
211212
```
212213

@@ -276,14 +277,14 @@ are not distributed as part of the bridge.
276277

277278
AppAuth supports three options for dependency management.
278279

279-
1. **CocoaPods**
280+
1. **CocoaPods**
280281

281282
```sh
282283
cd ios
283284
pod install
284285
```
285286

286-
2. **Carthage**
287+
2. **Carthage**
287288

288289
With [Carthage](https://github.com/Carthage/Carthage), add the following line to your `Cartfile`:
289290

@@ -295,7 +296,7 @@ AppAuth supports three options for dependency management.
295296

296297
Add a copy files build step for `AppAuth.framework`: open Build Phases on Xcode, add a new "Copy Files" phase, choose "Frameworks" as destination, add `AppAuth.framework` and ensure "Code Sign on Copy" is checked.
297298

298-
3. **Static Library**
299+
3. **Static Library**
299300

300301
You can also use [AppAuth-iOS](https://github.com/openid/AppAuth-iOS) as a static library. This
301302
requires linking the library and your project and including the headers. Suggested configuration:

android/src/main/java/com/rnappauth/RNAppAuthModule.java

+30-5
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,15 @@ public void prefetchConfiguration(
9595
final ReadableMap serviceConfiguration,
9696
final boolean dangerouslyAllowInsecureHttpRequests,
9797
final ReadableMap headers,
98+
final Double connectionTimeoutMillis,
9899
final Promise promise
99100
) {
100101
if (warmAndPrefetchChrome) {
101102
warmChromeCustomTab(reactContext, issuer);
102103
}
103104

104105
this.parseHeaderMap(headers);
105-
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders);
106+
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders, connectionTimeoutMillis);
106107
final CountDownLatch fetchConfigurationLatch = new CountDownLatch(1);
107108

108109
if(!isPrefetched) {
@@ -156,12 +157,13 @@ public void register(
156157
final String tokenEndpointAuthMethod,
157158
final ReadableMap additionalParameters,
158159
final ReadableMap serviceConfiguration,
160+
final Double connectionTimeoutMillis,
159161
final boolean dangerouslyAllowInsecureHttpRequests,
160162
final ReadableMap headers,
161163
final Promise promise
162164
) {
163165
this.parseHeaderMap(headers);
164-
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.registrationRequestHeaders);
166+
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.registrationRequestHeaders, connectionTimeoutMillis);
165167
final AppAuthConfiguration appAuthConfiguration = this.createAppAuthConfiguration(builder, dangerouslyAllowInsecureHttpRequests);
166168
final HashMap<String, String> additionalParametersMap = MapUtil.readableMapToHashMap(additionalParameters);
167169

@@ -225,6 +227,7 @@ public void authorize(
225227
final ReadableMap additionalParameters,
226228
final ReadableMap serviceConfiguration,
227229
final Boolean skipCodeExchange,
230+
final Double connectionTimeoutMillis,
228231
final Boolean useNonce,
229232
final Boolean usePKCE,
230233
final String clientAuthMethod,
@@ -233,7 +236,7 @@ public void authorize(
233236
final Promise promise
234237
) {
235238
this.parseHeaderMap(headers);
236-
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders);
239+
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders, connectionTimeoutMillis);
237240
final AppAuthConfiguration appAuthConfiguration = this.createAppAuthConfiguration(builder, dangerouslyAllowInsecureHttpRequests);
238241
final HashMap<String, String> additionalParametersMap = MapUtil.readableMapToHashMap(additionalParameters);
239242

@@ -318,13 +321,14 @@ public void refresh(
318321
final ReadableArray scopes,
319322
final ReadableMap additionalParameters,
320323
final ReadableMap serviceConfiguration,
324+
final Double connectionTimeoutMillis,
321325
final String clientAuthMethod,
322326
final boolean dangerouslyAllowInsecureHttpRequests,
323327
final ReadableMap headers,
324328
final Promise promise
325329
) {
326330
this.parseHeaderMap(headers);
327-
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.tokenRequestHeaders);
331+
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.tokenRequestHeaders, connectionTimeoutMillis);
328332
final AppAuthConfiguration appAuthConfiguration = createAppAuthConfiguration(builder, dangerouslyAllowInsecureHttpRequests);
329333
final HashMap<String, String> additionalParametersMap = MapUtil.readableMapToHashMap(additionalParameters);
330334

@@ -884,7 +888,7 @@ private AppAuthConfiguration createAppAuthConfiguration(
884888
/*
885889
* Create appropriate connection builder based on provided settings
886890
*/
887-
private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnections, Map<String, String> headers) {
891+
private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnections, Map<String, String> headers, Double connectionTimeoutMillis) {
888892
ConnectionBuilder proxiedBuilder;
889893

890894
if (allowInsecureConnections) {
@@ -894,6 +898,27 @@ private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnectio
894898
}
895899

896900
CustomConnectionBuilder customConnection = new CustomConnectionBuilder(proxiedBuilder);
901+
902+
if (headers != null) {
903+
customConnection.setHeaders(headers);
904+
}
905+
906+
customConnection.setConnectionTimeout(connectionTimeoutMillis.intValue());
907+
908+
return customConnection;
909+
}
910+
911+
private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnections, Map<String, String> headers) {
912+
ConnectionBuilder proxiedBuilder;
913+
914+
if (allowInsecureConnections) {
915+
proxiedBuilder = UnsafeConnectionBuilder.INSTANCE;
916+
} else {
917+
proxiedBuilder = DefaultConnectionBuilder.INSTANCE;
918+
}
919+
920+
CustomConnectionBuilder customConnection = new CustomConnectionBuilder(proxiedBuilder);
921+
897922
if (headers != null) {
898923
customConnection.setHeaders(headers);
899924
}

android/src/main/java/com/rnappauth/utils/CustomConnectionBuilder.java

+13
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import java.io.IOException;
2424
import java.net.HttpURLConnection;
25+
import java.util.concurrent.TimeUnit;
2526
import java.util.Map;
2627

2728

@@ -33,6 +34,9 @@
3334
public final class CustomConnectionBuilder implements ConnectionBuilder {
3435

3536
private Map<String, String> headers = null;
37+
38+
private int connectionTimeoutMs = (int) TimeUnit.SECONDS.toMillis(15);
39+
private int readTimeoutMs = (int) TimeUnit.SECONDS.toMillis(10);
3640
private ConnectionBuilder connectionBuilder;
3741

3842
public CustomConnectionBuilder(ConnectionBuilder connectionBuilderToUse) {
@@ -43,16 +47,25 @@ public void setHeaders (Map<String, String> headersToSet) {
4347
headers = headersToSet;
4448
}
4549

50+
public void setConnectionTimeout (int timeout) {
51+
connectionTimeoutMs = timeout;
52+
readTimeoutMs = timeout;
53+
}
54+
4655
@NonNull
4756
@Override
4857
public HttpURLConnection openConnection(@NonNull Uri uri) throws IOException {
4958
HttpURLConnection conn = connectionBuilder.openConnection(uri);
59+
5060
if (headers != null) {
5161
for (Map.Entry<String, String> header: headers.entrySet()) {
5262
conn.setRequestProperty(header.getKey(), header.getValue());
5363
}
5464
}
5565

66+
conn.setConnectTimeout(connectionTimeoutMs);
67+
conn.setReadTimeout(readTimeoutMs);
68+
5669
return conn;
5770
}
5871
}

index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export type AuthConfiguration = BaseAuthConfiguration & {
7474
dangerouslyAllowInsecureHttpRequests?: boolean;
7575
customHeaders?: CustomHeaders;
7676
additionalHeaders?: AdditionalHeaders;
77+
connectionTimeoutSeconds?: number;
7778
useNonce?: boolean;
7879
usePKCE?: boolean;
7980
warmAndPrefetchChrome?: boolean;

index.js

+31-2
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,40 @@ const validateAdditionalHeaders = headers => {
7575
);
7676
};
7777

78+
const validateConnectionTimeoutSeconds = timeout => {
79+
if (!timeout) {
80+
return;
81+
}
82+
83+
invariant(typeof timeout === 'number', 'Config error: connectionTimeoutSeconds must be a number');
84+
};
85+
86+
export const SECOND_IN_MS = 1000;
87+
export const DEFAULT_TIMEOUT_IOS = 60;
88+
export const DEFAULT_TIMEOUT_ANDROID = 15;
89+
90+
const convertTimeoutForPlatform = (
91+
platform,
92+
connectionTimeout = Platform.OS === 'ios' ? DEFAULT_TIMEOUT_IOS : DEFAULT_TIMEOUT_ANDROID
93+
) => (platform === 'android' ? connectionTimeout * SECOND_IN_MS : connectionTimeout);
94+
7895
export const prefetchConfiguration = async ({
79-
warmAndPrefetchChrome,
96+
warmAndPrefetchChrome = false,
8097
issuer,
8198
redirectUrl,
8299
clientId,
83100
scopes,
84101
serviceConfiguration,
85102
dangerouslyAllowInsecureHttpRequests = false,
86103
customHeaders,
104+
connectionTimeoutSeconds,
87105
}) => {
88106
if (Platform.OS === 'android') {
89107
validateIssuerOrServiceConfigurationEndpoints(issuer, serviceConfiguration);
90108
validateClientId(clientId);
91109
validateRedirectUrl(redirectUrl);
92110
validateHeaders(customHeaders);
111+
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);
93112

94113
const nativeMethodArguments = [
95114
warmAndPrefetchChrome,
@@ -100,6 +119,7 @@ export const prefetchConfiguration = async ({
100119
serviceConfiguration,
101120
dangerouslyAllowInsecureHttpRequests,
102121
customHeaders,
122+
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
103123
];
104124

105125
RNAppAuth.prefetchConfiguration(...nativeMethodArguments);
@@ -118,10 +138,12 @@ export const register = ({
118138
dangerouslyAllowInsecureHttpRequests = false,
119139
customHeaders,
120140
additionalHeaders,
141+
connectionTimeoutSeconds,
121142
}) => {
122143
validateIssuerOrServiceConfigurationRegistrationEndpoint(issuer, serviceConfiguration);
123144
validateHeaders(customHeaders);
124145
validateAdditionalHeaders(additionalHeaders);
146+
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);
125147

126148
invariant(
127149
Array.isArray(redirectUrls) && redirectUrls.every(url => typeof url === 'string'),
@@ -155,6 +177,7 @@ export const register = ({
155177
tokenEndpointAuthMethod,
156178
additionalParameters,
157179
serviceConfiguration,
180+
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
158181
];
159182

160183
if (Platform.OS === 'android') {
@@ -184,12 +207,14 @@ export const authorize = ({
184207
customHeaders,
185208
additionalHeaders,
186209
skipCodeExchange = false,
210+
connectionTimeoutSeconds,
187211
}) => {
188212
validateIssuerOrServiceConfigurationEndpoints(issuer, serviceConfiguration);
189213
validateClientId(clientId);
190214
validateRedirectUrl(redirectUrl);
191215
validateHeaders(customHeaders);
192216
validateAdditionalHeaders(additionalHeaders);
217+
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);
193218
// TODO: validateAdditionalParameters
194219

195220
const nativeMethodArguments = [
@@ -201,6 +226,7 @@ export const authorize = ({
201226
additionalParameters,
202227
serviceConfiguration,
203228
skipCodeExchange,
229+
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
204230
];
205231

206232
if (Platform.OS === 'android') {
@@ -227,12 +253,13 @@ export const refresh = (
227253
clientId,
228254
clientSecret,
229255
scopes,
230-
additionalParameters,
256+
additionalParameters = {},
231257
serviceConfiguration,
232258
clientAuthMethod = 'basic',
233259
dangerouslyAllowInsecureHttpRequests = false,
234260
customHeaders,
235261
additionalHeaders,
262+
connectionTimeoutSeconds,
236263
},
237264
{ refreshToken }
238265
) => {
@@ -241,6 +268,7 @@ export const refresh = (
241268
validateRedirectUrl(redirectUrl);
242269
validateHeaders(customHeaders);
243270
validateAdditionalHeaders(additionalHeaders);
271+
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);
244272
invariant(refreshToken, 'Please pass in a refresh token');
245273
// TODO: validateAdditionalParameters
246274

@@ -253,6 +281,7 @@ export const refresh = (
253281
scopes,
254282
additionalParameters,
255283
serviceConfiguration,
284+
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
256285
];
257286

258287
if (Platform.OS === 'android') {

0 commit comments

Comments
 (0)