Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/calm-geckos-isolate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-native-app-auth": patch
---

Keep each iOS request on its originating URL session so concurrent OAuth calls cannot mix additional headers or timeout settings.
3 changes: 3 additions & 0 deletions docs/docs/usage/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ See specific example [configurations for your provider](/docs/category/providers
- **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.

On iOS, additional headers and timeout settings are scoped to the call that supplied them. Concurrent
authorize, refresh, and register requests do not inherit another call's URL session configuration.
103 changes: 67 additions & 36 deletions packages/react-native-app-auth/ios/RNAppAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ - (dispatch_queue_t)methodQueue
resolve: (RCTPromiseResolveBlock) resolve
reject: (RCTPromiseRejectBlock) reject)
{
[self configureUrlSession:additionalHeaders sessionTimeout:connectionTimeoutSeconds];
NSURLSession *urlSession = [self createUrlSession:additionalHeaders sessionTimeout:connectionTimeoutSeconds];
[OIDURLSessionProvider setSession:urlSession];

// if we have manually provided configuration, we can use it and skip the OIDC well-known discovery endpoint call
if (serviceConfiguration) {
Expand All @@ -64,6 +65,7 @@ - (dispatch_queue_t)methodQueue
subjectType: subjectType
tokenEndpointAuthMethod: tokenEndpointAuthMethod
additionalParameters: additionalParameters
urlSession: urlSession
resolve: resolve
reject: reject];
} else {
Expand All @@ -80,6 +82,7 @@ - (dispatch_queue_t)methodQueue
subjectType: subjectType
tokenEndpointAuthMethod: tokenEndpointAuthMethod
additionalParameters: additionalParameters
urlSession: urlSession
resolve: resolve
reject: reject];
}];
Expand All @@ -104,7 +107,8 @@ - (dispatch_queue_t)methodQueue
resolve: (RCTPromiseResolveBlock) resolve
reject: (RCTPromiseRejectBlock) reject)
{
[self configureUrlSession:additionalHeaders sessionTimeout:connectionTimeoutSeconds];
NSURLSession *urlSession = [self createUrlSession:additionalHeaders sessionTimeout:connectionTimeoutSeconds];
[OIDURLSessionProvider setSession:urlSession];

// if we have manually provided configuration, we can use it and skip the OIDC well-known discovery endpoint call
if (serviceConfiguration) {
Expand All @@ -120,6 +124,7 @@ - (dispatch_queue_t)methodQueue
skipCodeExchange: skipCodeExchange
iosCustomBrowser: iosCustomBrowser
prefersEphemeralSession: prefersEphemeralSession
urlSession: urlSession
resolve: resolve
reject: reject];
} else {
Expand All @@ -141,6 +146,7 @@ - (dispatch_queue_t)methodQueue
skipCodeExchange: skipCodeExchange
iosCustomBrowser: iosCustomBrowser
prefersEphemeralSession: prefersEphemeralSession
urlSession: urlSession
resolve: resolve
reject: reject];
}];
Expand All @@ -162,7 +168,8 @@ - (dispatch_queue_t)methodQueue
resolve:(RCTPromiseResolveBlock) resolve
reject: (RCTPromiseRejectBlock) reject)
{
[self configureUrlSession:additionalHeaders sessionTimeout:connectionTimeoutSeconds];
NSURLSession *urlSession = [self createUrlSession:additionalHeaders sessionTimeout:connectionTimeoutSeconds];
[OIDURLSessionProvider setSession:urlSession];

// if we have manually provided configuration, we can use it and skip the OIDC well-known discovery endpoint call
if (serviceConfiguration) {
Expand All @@ -174,6 +181,7 @@ - (dispatch_queue_t)methodQueue
refreshToken: refreshToken
scopes: scopes
additionalParameters: additionalParameters
urlSession: urlSession
resolve: resolve
reject: reject];
} else {
Expand All @@ -191,6 +199,7 @@ - (dispatch_queue_t)methodQueue
refreshToken: refreshToken
scopes: scopes
additionalParameters: additionalParameters
urlSession: urlSession
resolve: resolve
reject: reject];
}];
Expand All @@ -208,6 +217,8 @@ - (dispatch_queue_t)methodQueue
resolve:(RCTPromiseResolveBlock) resolve
reject: (RCTPromiseRejectBlock) reject)
{
[OIDURLSessionProvider setSession:[self createUrlSession:nil sessionTimeout:60]];

if (serviceConfiguration) {
OIDServiceConfiguration *configuration = [self createServiceConfiguration:serviceConfiguration];
[self endSessionWithConfiguration: configuration
Expand Down Expand Up @@ -288,6 +299,7 @@ - (void)registerWithConfiguration: (OIDServiceConfiguration *) configuration
subjectType: (NSString *) subjectType
tokenEndpointAuthMethod: (NSString *) tokenEndpointAuthMethod
additionalParameters: (NSDictionary *_Nullable) additionalParameters
urlSession: (NSURLSession *) urlSession
resolve: (RCTPromiseResolveBlock) resolve
reject: (RCTPromiseRejectBlock) reject
{
Expand All @@ -305,6 +317,7 @@ - (void)registerWithConfiguration: (OIDServiceConfiguration *) configuration
tokenEndpointAuthMethod:tokenEndpointAuthMethod
additionalParameters:additionalParameters];

[OIDURLSessionProvider setSession:urlSession];
[OIDAuthorizationService performRegistrationRequest:request
completion:^(OIDRegistrationResponse *_Nullable response,
NSError *_Nullable error) {
Expand Down Expand Up @@ -332,6 +345,7 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration
skipCodeExchange: (BOOL) skipCodeExchange
iosCustomBrowser: (NSString *) iosCustomBrowser
prefersEphemeralSession: (BOOL) prefersEphemeralSession
urlSession: (NSURLSession *) urlSession
resolve: (RCTPromiseResolveBlock) resolve
reject: (RCTPromiseRejectBlock) reject
{
Expand Down Expand Up @@ -415,40 +429,56 @@ - (void)authorizeWithConfiguration: (OIDServiceConfiguration *) configuration
}
}
} else {
OIDAuthStateAuthorizationCallback callback = ^(
OIDAuthState *_Nullable authState,
NSError *_Nullable error
) {
typeof(self) strongSelf = weakSelf;
strongSelf->_currentSession = nil;
[UIApplication.sharedApplication endBackgroundTask:rnAppAuthTaskId];
rnAppAuthTaskId = UIBackgroundTaskInvalid;
if (authState) {
resolve([self formatResponse:authState.lastTokenResponse
withAuthResponse:authState.lastAuthorizationResponse]);
} else {
[self rejectPromise:reject
defaultCode:@"authentication_failed"
error:error];
}
};

OIDAuthorizationCallback tokenExchangeCallback = ^(
OIDAuthorizationResponse *_Nullable authorizationResponse,
NSError *_Nullable error
) {
if (!authorizationResponse) {
typeof(self) strongSelf = weakSelf;
strongSelf->_currentSession = nil;
[UIApplication.sharedApplication endBackgroundTask:rnAppAuthTaskId];
rnAppAuthTaskId = UIBackgroundTaskInvalid;
[self rejectPromise:reject
defaultCode:@"authentication_failed"
error:error];
return;
}

OIDTokenRequest *tokenRequest = [authorizationResponse tokenExchangeRequest];
[OIDURLSessionProvider setSession:urlSession];
[OIDAuthorizationService performTokenRequest:tokenRequest
originalAuthorizationResponse:authorizationResponse
callback:^(OIDTokenResponse *_Nullable tokenResponse,
NSError *_Nullable tokenError) {
typeof(self) strongSelf = weakSelf;
strongSelf->_currentSession = nil;
[UIApplication.sharedApplication endBackgroundTask:rnAppAuthTaskId];
rnAppAuthTaskId = UIBackgroundTaskInvalid;
if (tokenResponse) {
resolve([self formatResponse:tokenResponse
withAuthResponse:authorizationResponse]);
} else {
[self rejectPromise:reject
defaultCode:@"authentication_failed"
error:tokenError];
}
}];
};

if(externalUserAgent != nil) {
_currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request
externalUserAgent:externalUserAgent
callback:callback];
_currentSession = [OIDAuthorizationService presentAuthorizationRequest:request
externalUserAgent:externalUserAgent
callback:tokenExchangeCallback];
} else {


if (@available(iOS 13, *)) {
_currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request
presentingViewController:presentingViewController
prefersEphemeralSession:prefersEphemeralSession
callback:callback];
_currentSession = [OIDAuthorizationService presentAuthorizationRequest:request
presentingViewController:presentingViewController
prefersEphemeralSession:prefersEphemeralSession
callback:tokenExchangeCallback];
} else {
_currentSession = [OIDAuthState authStateByPresentingAuthorizationRequest:request
presentingViewController:presentingViewController
callback:callback];
_currentSession = [OIDAuthorizationService presentAuthorizationRequest:request
presentingViewController:presentingViewController
callback:tokenExchangeCallback];
}
}
}
Expand All @@ -464,6 +494,7 @@ - (void)refreshWithConfiguration: (OIDServiceConfiguration *)configuration
refreshToken: (NSString *) refreshToken
scopes: (NSArray *) scopes
additionalParameters: (NSDictionary *_Nullable) additionalParameters
urlSession: (NSURLSession *) urlSession
resolve:(RCTPromiseResolveBlock) resolve
reject: (RCTPromiseRejectBlock) reject {

Expand All @@ -479,6 +510,7 @@ - (void)refreshWithConfiguration: (OIDServiceConfiguration *)configuration
codeVerifier:nil
additionalParameters:additionalParameters];

[OIDURLSessionProvider setSession:urlSession];
[OIDAuthorizationService performTokenRequest:tokenRefreshRequest
callback:^(OIDTokenResponse *_Nullable response,
NSError *_Nullable error) {
Expand Down Expand Up @@ -546,16 +578,15 @@ - (void)endSessionWithConfiguration: (OIDServiceConfiguration *) configuration
}];
}

- (void)configureUrlSession: (NSDictionary*) headers sessionTimeout: (double) sessionTimeout{
- (NSURLSession *)createUrlSession: (NSDictionary*) headers sessionTimeout: (double) sessionTimeout{
NSURLSessionConfiguration* configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
if (headers != nil) {
configuration.HTTPAdditionalHeaders = headers;
}

configuration.timeoutIntervalForRequest = sessionTimeout;

NSURLSession* session = [NSURLSession sessionWithConfiguration:configuration];
[OIDURLSessionProvider setSession:session];
return [NSURLSession sessionWithConfiguration:configuration];
}

/*
Expand Down