diff --git a/Sources/AppAuthCore/OIDAuthState.h b/Sources/AppAuthCore/OIDAuthState.h index 46c78a831..7d58d5bcd 100644 --- a/Sources/AppAuthCore/OIDAuthState.h +++ b/Sources/AppAuthCore/OIDAuthState.h @@ -237,8 +237,7 @@ static NSString *const kRefreshTokenRequestException = /*! @brief Calls the block with a valid access token (refreshing it first, if needed), or if a refresh was needed and failed, with the error that caused it to fail. - @param action The block to execute with a fresh token. This block will be executed on the main - thread. + @param action The block to execute with a fresh token. @param additionalParameters Additional parameters for the token request if token is refreshed. @param dispatchQueue The dispatchQueue on which to dispatch the action block. @@ -253,6 +252,17 @@ static NSString *const kRefreshTokenRequestException = */ - (void)setNeedsTokenRefresh; +/*! @brief The shared queue used for token refresh operations and delegate callbacks. + @discussion By default, all token refresh operations and delegate callbacks occur on + the main queue. Set this to a background queue if you need to call + @c OIDAuthState.performActionWithFreshTokens: synchronously without risking deadlock. + When using a custom queue, @c OIDAuthState should only be used from that queue, because it is not thread-safe. + Delelegate calls are dispatched from this queue as well. + This is a class-level property that affects all @c OIDAuthState instances. + The getter returns the main queue if no custom queue has been set. + */ +@property(class, nonatomic, strong, nullable) dispatch_queue_t sharedDelegateQueue; + /*! @brief Creates a token request suitable for refreshing an access token. @return A @c OIDTokenRequest suitable for using a refresh token to obtain a new access token. @discussion After performing the refresh, call @c OIDAuthState.updateWithTokenResponse:error: diff --git a/Sources/AppAuthCore/OIDAuthState.m b/Sources/AppAuthCore/OIDAuthState.m index cb5a22a1e..258837d15 100644 --- a/Sources/AppAuthCore/OIDAuthState.m +++ b/Sources/AppAuthCore/OIDAuthState.m @@ -102,6 +102,7 @@ - (void)didChangeState; @end +static dispatch_queue_t _sharedDelegateQueue = nil; @implementation OIDAuthState { /*! @brief Array of pending actions (use @c _pendingActionsSyncObject to synchronize access). @@ -490,6 +491,14 @@ - (void)setNeedsTokenRefresh { _needsTokenRefresh = YES; } ++ (void)setSharedDelegateQueue:(dispatch_queue_t)queue { + _sharedDelegateQueue = queue; +} + ++ (dispatch_queue_t)sharedDelegateQueue { + return _sharedDelegateQueue ?: dispatch_get_main_queue(); +} + - (void)performActionWithFreshTokens:(OIDAuthStateAction)action { [self performActionWithFreshTokens:action additionalRefreshParameters:nil]; } @@ -547,6 +556,7 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action [self tokenRefreshRequestWithAdditionalParameters:additionalParameters]; [OIDAuthorizationService performTokenRequest:tokenRefreshRequest originalAuthorizationResponse:_lastAuthorizationResponse + callbackDispatchQueue:[OIDAuthState sharedDelegateQueue] callback:^(OIDTokenResponse *_Nullable response, NSError *_Nullable error) { // update OIDAuthState based on response diff --git a/Sources/AppAuthCore/OIDAuthorizationService.h b/Sources/AppAuthCore/OIDAuthorizationService.h index c8fee5358..ad3229fbe 100644 --- a/Sources/AppAuthCore/OIDAuthorizationService.h +++ b/Sources/AppAuthCore/OIDAuthorizationService.h @@ -152,8 +152,14 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg /*! @brief Performs a token request. @param request The token request. @param authorizationResponse The original authorization response related to this token request. + @param callbackDispatchQueue The dispatch queue on which to call the callback. @param callback The method called when the request has completed or failed. */ ++ (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + callbackDispatchQueue:(dispatch_queue_t)callbackDispatchQueue + callback:(OIDTokenCallback)callback; + + (void)performTokenRequest:(OIDTokenRequest *)request originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse callback:(OIDTokenCallback)callback; diff --git a/Sources/AppAuthCore/OIDAuthorizationService.m b/Sources/AppAuthCore/OIDAuthorizationService.m index cc749a3f9..7891a8b7d 100644 --- a/Sources/AppAuthCore/OIDAuthorizationService.m +++ b/Sources/AppAuthCore/OIDAuthorizationService.m @@ -423,11 +423,21 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL + (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { [[self class] performTokenRequest:request originalAuthorizationResponse:nil + callbackDispatchQueue:dispatch_get_main_queue() callback:callback]; } ++ (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + callback:(OIDTokenCallback)callback { + [self performTokenRequest:request +originalAuthorizationResponse:authorizationResponse + callbackDispatchQueue:dispatch_get_main_queue() + callback:callback]; +} + (void)performTokenRequest:(OIDTokenRequest *)request originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + callbackDispatchQueue:(dispatch_queue_t)callbackDispatchQueue callback:(OIDTokenCallback)callback { NSURLRequest *URLRequest = [request URLRequest]; @@ -453,7 +463,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError underlyingError:error description:errorDescription]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -482,7 +492,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities OAuthErrorWithDomain:OIDOAuthTokenErrorDomain OAuthResponse:json underlyingError:serverError]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, oauthError); }); return; @@ -498,7 +508,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeServerError underlyingError:serverError description:errorDescription]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -516,7 +526,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeJSONDeserializationError underlyingError:jsonDeserializationError description:errorDescription]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -530,7 +540,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeTokenResponseConstructionError underlyingError:jsonDeserializationError description:@"Token response invalid."]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -551,7 +561,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenParsingError underlyingError:nil description:@"ID Token parsing failed"]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -568,7 +578,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"Issuer mismatch"]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -584,7 +594,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"Audience mismatch"]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -610,7 +620,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"ID Token expired"]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -628,7 +638,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:message]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -644,7 +654,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"Nonce mismatch"]; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -659,7 +669,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request } // Success - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(tokenResponse, nil); }); }] resume];