Skip to content

Commit

Permalink
add configurable timeout fn param (connectionTimeoutSeconds/connectio…
Browse files Browse the repository at this point in the history
…nTimeoutMillis) (#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
  • Loading branch information
Austin McBee authored Feb 2, 2022
1 parent 6bb6710 commit 580372d
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 59 deletions.
8 changes: 6 additions & 2 deletions Example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,19 @@ const App = () => {
React.useEffect(() => {
prefetchConfiguration({
warmAndPrefetchChrome: true,
...configs.identityserver
connectionTimeoutSeconds: 5,
...configs.identityserver,
});
}, []);

const handleAuthorize = useCallback(
async provider => {
try {
const config = configs[provider];
const newAuthState = await authorize(config);
const newAuthState = await authorize({
...config,
connectionTimeoutSeconds: 5,
});

setAuthState({
hasLoggedInOnce: true,
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ This is the result from the auth server:
- **scopes** - ([`string`]) the scopes the user has agreed to be granted
- **authorizationCode** - (`string`) the authorization code (only if `skipCodeExchange=true`)
- **codeVerifier** - (`string`) the codeVerifier value used for the PKCE exchange (only if both `skipCodeExchange=true` and `usePKCE=true`)
- **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.

### `refresh`

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

const config = {
issuer: '<YOUR_ISSUER_URL>'
issuer: '<YOUR_ISSUER_URL>',
};

const result = await logout(config, {
idToken: '<ID_TOKEN>',
postLogoutRedirectUrl: '<POST_LOGOUT_URL>'
postLogoutRedirectUrl: '<POST_LOGOUT_URL>',
});
```

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

AppAuth supports three options for dependency management.

1. **CocoaPods**
1. **CocoaPods**

```sh
cd ios
pod install
```

2. **Carthage**
2. **Carthage**

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

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

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.

3. **Static Library**
3. **Static Library**

You can also use [AppAuth-iOS](https://github.com/openid/AppAuth-iOS) as a static library. This
requires linking the library and your project and including the headers. Suggested configuration:
Expand Down
35 changes: 30 additions & 5 deletions android/src/main/java/com/rnappauth/RNAppAuthModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,15 @@ public void prefetchConfiguration(
final ReadableMap serviceConfiguration,
final boolean dangerouslyAllowInsecureHttpRequests,
final ReadableMap headers,
final Double connectionTimeoutMillis,
final Promise promise
) {
if (warmAndPrefetchChrome) {
warmChromeCustomTab(reactContext, issuer);
}

this.parseHeaderMap(headers);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders, connectionTimeoutMillis);
final CountDownLatch fetchConfigurationLatch = new CountDownLatch(1);

if(!isPrefetched) {
Expand Down Expand Up @@ -156,12 +157,13 @@ public void register(
final String tokenEndpointAuthMethod,
final ReadableMap additionalParameters,
final ReadableMap serviceConfiguration,
final Double connectionTimeoutMillis,
final boolean dangerouslyAllowInsecureHttpRequests,
final ReadableMap headers,
final Promise promise
) {
this.parseHeaderMap(headers);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.registrationRequestHeaders);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.registrationRequestHeaders, connectionTimeoutMillis);
final AppAuthConfiguration appAuthConfiguration = this.createAppAuthConfiguration(builder, dangerouslyAllowInsecureHttpRequests);
final HashMap<String, String> additionalParametersMap = MapUtil.readableMapToHashMap(additionalParameters);

Expand Down Expand Up @@ -225,6 +227,7 @@ public void authorize(
final ReadableMap additionalParameters,
final ReadableMap serviceConfiguration,
final Boolean skipCodeExchange,
final Double connectionTimeoutMillis,
final Boolean useNonce,
final Boolean usePKCE,
final String clientAuthMethod,
Expand All @@ -233,7 +236,7 @@ public void authorize(
final Promise promise
) {
this.parseHeaderMap(headers);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.authorizationRequestHeaders, connectionTimeoutMillis);
final AppAuthConfiguration appAuthConfiguration = this.createAppAuthConfiguration(builder, dangerouslyAllowInsecureHttpRequests);
final HashMap<String, String> additionalParametersMap = MapUtil.readableMapToHashMap(additionalParameters);

Expand Down Expand Up @@ -318,13 +321,14 @@ public void refresh(
final ReadableArray scopes,
final ReadableMap additionalParameters,
final ReadableMap serviceConfiguration,
final Double connectionTimeoutMillis,
final String clientAuthMethod,
final boolean dangerouslyAllowInsecureHttpRequests,
final ReadableMap headers,
final Promise promise
) {
this.parseHeaderMap(headers);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.tokenRequestHeaders);
final ConnectionBuilder builder = createConnectionBuilder(dangerouslyAllowInsecureHttpRequests, this.tokenRequestHeaders, connectionTimeoutMillis);
final AppAuthConfiguration appAuthConfiguration = createAppAuthConfiguration(builder, dangerouslyAllowInsecureHttpRequests);
final HashMap<String, String> additionalParametersMap = MapUtil.readableMapToHashMap(additionalParameters);

Expand Down Expand Up @@ -884,7 +888,7 @@ private AppAuthConfiguration createAppAuthConfiguration(
/*
* Create appropriate connection builder based on provided settings
*/
private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnections, Map<String, String> headers) {
private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnections, Map<String, String> headers, Double connectionTimeoutMillis) {
ConnectionBuilder proxiedBuilder;

if (allowInsecureConnections) {
Expand All @@ -894,6 +898,27 @@ private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnectio
}

CustomConnectionBuilder customConnection = new CustomConnectionBuilder(proxiedBuilder);

if (headers != null) {
customConnection.setHeaders(headers);
}

customConnection.setConnectionTimeout(connectionTimeoutMillis.intValue());

return customConnection;
}

private ConnectionBuilder createConnectionBuilder(boolean allowInsecureConnections, Map<String, String> headers) {
ConnectionBuilder proxiedBuilder;

if (allowInsecureConnections) {
proxiedBuilder = UnsafeConnectionBuilder.INSTANCE;
} else {
proxiedBuilder = DefaultConnectionBuilder.INSTANCE;
}

CustomConnectionBuilder customConnection = new CustomConnectionBuilder(proxiedBuilder);

if (headers != null) {
customConnection.setHeaders(headers);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.concurrent.TimeUnit;
import java.util.Map;


Expand All @@ -33,6 +34,9 @@
public final class CustomConnectionBuilder implements ConnectionBuilder {

private Map<String, String> headers = null;

private int connectionTimeoutMs = (int) TimeUnit.SECONDS.toMillis(15);
private int readTimeoutMs = (int) TimeUnit.SECONDS.toMillis(10);
private ConnectionBuilder connectionBuilder;

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

public void setConnectionTimeout (int timeout) {
connectionTimeoutMs = timeout;
readTimeoutMs = timeout;
}

@NonNull
@Override
public HttpURLConnection openConnection(@NonNull Uri uri) throws IOException {
HttpURLConnection conn = connectionBuilder.openConnection(uri);

if (headers != null) {
for (Map.Entry<String, String> header: headers.entrySet()) {
conn.setRequestProperty(header.getKey(), header.getValue());
}
}

conn.setConnectTimeout(connectionTimeoutMs);
conn.setReadTimeout(readTimeoutMs);

return conn;
}
}
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export type AuthConfiguration = BaseAuthConfiguration & {
dangerouslyAllowInsecureHttpRequests?: boolean;
customHeaders?: CustomHeaders;
additionalHeaders?: AdditionalHeaders;
connectionTimeoutSeconds?: number;
useNonce?: boolean;
usePKCE?: boolean;
warmAndPrefetchChrome?: boolean;
Expand Down
33 changes: 31 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,40 @@ const validateAdditionalHeaders = headers => {
);
};

const validateConnectionTimeoutSeconds = timeout => {
if (!timeout) {
return;
}

invariant(typeof timeout === 'number', 'Config error: connectionTimeoutSeconds must be a number');
};

export const SECOND_IN_MS = 1000;
export const DEFAULT_TIMEOUT_IOS = 60;
export const DEFAULT_TIMEOUT_ANDROID = 15;

const convertTimeoutForPlatform = (
platform,
connectionTimeout = Platform.OS === 'ios' ? DEFAULT_TIMEOUT_IOS : DEFAULT_TIMEOUT_ANDROID
) => (platform === 'android' ? connectionTimeout * SECOND_IN_MS : connectionTimeout);

export const prefetchConfiguration = async ({
warmAndPrefetchChrome,
warmAndPrefetchChrome = false,
issuer,
redirectUrl,
clientId,
scopes,
serviceConfiguration,
dangerouslyAllowInsecureHttpRequests = false,
customHeaders,
connectionTimeoutSeconds,
}) => {
if (Platform.OS === 'android') {
validateIssuerOrServiceConfigurationEndpoints(issuer, serviceConfiguration);
validateClientId(clientId);
validateRedirectUrl(redirectUrl);
validateHeaders(customHeaders);
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);

const nativeMethodArguments = [
warmAndPrefetchChrome,
Expand All @@ -100,6 +119,7 @@ export const prefetchConfiguration = async ({
serviceConfiguration,
dangerouslyAllowInsecureHttpRequests,
customHeaders,
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
];

RNAppAuth.prefetchConfiguration(...nativeMethodArguments);
Expand All @@ -118,10 +138,12 @@ export const register = ({
dangerouslyAllowInsecureHttpRequests = false,
customHeaders,
additionalHeaders,
connectionTimeoutSeconds,
}) => {
validateIssuerOrServiceConfigurationRegistrationEndpoint(issuer, serviceConfiguration);
validateHeaders(customHeaders);
validateAdditionalHeaders(additionalHeaders);
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);

invariant(
Array.isArray(redirectUrls) && redirectUrls.every(url => typeof url === 'string'),
Expand Down Expand Up @@ -155,6 +177,7 @@ export const register = ({
tokenEndpointAuthMethod,
additionalParameters,
serviceConfiguration,
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
];

if (Platform.OS === 'android') {
Expand Down Expand Up @@ -184,12 +207,14 @@ export const authorize = ({
customHeaders,
additionalHeaders,
skipCodeExchange = false,
connectionTimeoutSeconds,
}) => {
validateIssuerOrServiceConfigurationEndpoints(issuer, serviceConfiguration);
validateClientId(clientId);
validateRedirectUrl(redirectUrl);
validateHeaders(customHeaders);
validateAdditionalHeaders(additionalHeaders);
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);
// TODO: validateAdditionalParameters

const nativeMethodArguments = [
Expand All @@ -201,6 +226,7 @@ export const authorize = ({
additionalParameters,
serviceConfiguration,
skipCodeExchange,
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
];

if (Platform.OS === 'android') {
Expand All @@ -227,12 +253,13 @@ export const refresh = (
clientId,
clientSecret,
scopes,
additionalParameters,
additionalParameters = {},
serviceConfiguration,
clientAuthMethod = 'basic',
dangerouslyAllowInsecureHttpRequests = false,
customHeaders,
additionalHeaders,
connectionTimeoutSeconds,
},
{ refreshToken }
) => {
Expand All @@ -241,6 +268,7 @@ export const refresh = (
validateRedirectUrl(redirectUrl);
validateHeaders(customHeaders);
validateAdditionalHeaders(additionalHeaders);
validateConnectionTimeoutSeconds(connectionTimeoutSeconds);
invariant(refreshToken, 'Please pass in a refresh token');
// TODO: validateAdditionalParameters

Expand All @@ -253,6 +281,7 @@ export const refresh = (
scopes,
additionalParameters,
serviceConfiguration,
convertTimeoutForPlatform(Platform.OS, connectionTimeoutSeconds),
];

if (Platform.OS === 'android') {
Expand Down
Loading

0 comments on commit 580372d

Please sign in to comment.