From f13c1e6a2fa84e7fca5362f8e9e260dce7533d3c Mon Sep 17 00:00:00 2001 From: Julian Asamer Date: Wed, 29 Oct 2025 17:25:45 +0100 Subject: [PATCH 1/2] Use background queue for performTokenRequest --- Sources/AppAuthCore/OIDAuthState.m | 9 +++++ Sources/AppAuthCore/OIDAuthorizationService.h | 5 +++ Sources/AppAuthCore/OIDAuthorizationService.m | 34 ++++++++++++------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Sources/AppAuthCore/OIDAuthState.m b/Sources/AppAuthCore/OIDAuthState.m index cb5a22a1e..255f9e6c7 100644 --- a/Sources/AppAuthCore/OIDAuthState.m +++ b/Sources/AppAuthCore/OIDAuthState.m @@ -102,6 +102,14 @@ - (void)didChangeState; @end +static dispatch_queue_t dispatch_get_oidAuthStateQueue(void) { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("OIDAuthStateQueue", NULL); + }); + return queue; +} @implementation OIDAuthState { /*! @brief Array of pending actions (use @c _pendingActionsSyncObject to synchronize access). @@ -547,6 +555,7 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action [self tokenRefreshRequestWithAdditionalParameters:additionalParameters]; [OIDAuthorizationService performTokenRequest:tokenRefreshRequest originalAuthorizationResponse:_lastAuthorizationResponse + dispatchQueue: dispatch_get_oidAuthStateQueue() 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..42e1cb773 100644 --- a/Sources/AppAuthCore/OIDAuthorizationService.h +++ b/Sources/AppAuthCore/OIDAuthorizationService.h @@ -154,6 +154,11 @@ typedef void (^OIDRegistrationCompletion)(OIDRegistrationResponse *_Nullable reg @param authorizationResponse The original authorization response related to this token request. @param callback The method called when the request has completed or failed. */ ++ (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + dispatchQueue:(dispatch_queue_t)dispatchQueue + 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..724adc4fd 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 + dispatchQueue:dispatch_get_main_queue() callback:callback]; } ++ (void)performTokenRequest:(OIDTokenRequest *)request + originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + callback:(OIDTokenCallback)callback { + [self performTokenRequest:request +originalAuthorizationResponse:authorizationResponse + dispatchQueue:dispatch_get_main_queue() + callback:callback]; +} + (void)performTokenRequest:(OIDTokenRequest *)request originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse + dispatchQueue:(dispatch_queue_t)dispatchQueue 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ 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(dispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -659,7 +669,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request } // Success - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(dispatchQueue, ^{ callback(tokenResponse, nil); }); }] resume]; From acc573c7c2d617edb74b06335d90f3b01eb07e39 Mon Sep 17 00:00:00 2001 From: Julian Asamer Date: Thu, 30 Oct 2025 16:41:43 +0100 Subject: [PATCH 2/2] Make the dispatch queue used by OIDAuthState configurable --- Sources/AppAuthCore/OIDAuthState.h | 14 +++++++-- Sources/AppAuthCore/OIDAuthState.m | 19 ++++++------ Sources/AppAuthCore/OIDAuthorizationService.h | 3 +- Sources/AppAuthCore/OIDAuthorizationService.m | 30 +++++++++---------- 4 files changed, 39 insertions(+), 27 deletions(-) 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 255f9e6c7..258837d15 100644 --- a/Sources/AppAuthCore/OIDAuthState.m +++ b/Sources/AppAuthCore/OIDAuthState.m @@ -102,14 +102,7 @@ - (void)didChangeState; @end -static dispatch_queue_t dispatch_get_oidAuthStateQueue(void) { - static dispatch_queue_t queue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("OIDAuthStateQueue", NULL); - }); - return queue; -} +static dispatch_queue_t _sharedDelegateQueue = nil; @implementation OIDAuthState { /*! @brief Array of pending actions (use @c _pendingActionsSyncObject to synchronize access). @@ -498,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]; } @@ -555,7 +556,7 @@ - (void)performActionWithFreshTokens:(OIDAuthStateAction)action [self tokenRefreshRequestWithAdditionalParameters:additionalParameters]; [OIDAuthorizationService performTokenRequest:tokenRefreshRequest originalAuthorizationResponse:_lastAuthorizationResponse - dispatchQueue: dispatch_get_oidAuthStateQueue() + 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 42e1cb773..ad3229fbe 100644 --- a/Sources/AppAuthCore/OIDAuthorizationService.h +++ b/Sources/AppAuthCore/OIDAuthorizationService.h @@ -152,11 +152,12 @@ 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 - dispatchQueue:(dispatch_queue_t)dispatchQueue + callbackDispatchQueue:(dispatch_queue_t)callbackDispatchQueue callback:(OIDTokenCallback)callback; + (void)performTokenRequest:(OIDTokenRequest *)request diff --git a/Sources/AppAuthCore/OIDAuthorizationService.m b/Sources/AppAuthCore/OIDAuthorizationService.m index 724adc4fd..7891a8b7d 100644 --- a/Sources/AppAuthCore/OIDAuthorizationService.m +++ b/Sources/AppAuthCore/OIDAuthorizationService.m @@ -423,7 +423,7 @@ + (void)discoverServiceConfigurationForDiscoveryURL:(NSURL *)discoveryURL + (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { [[self class] performTokenRequest:request originalAuthorizationResponse:nil - dispatchQueue:dispatch_get_main_queue() + callbackDispatchQueue:dispatch_get_main_queue() callback:callback]; } + (void)performTokenRequest:(OIDTokenRequest *)request @@ -431,13 +431,13 @@ + (void)performTokenRequest:(OIDTokenRequest *)request callback:(OIDTokenCallback)callback { [self performTokenRequest:request originalAuthorizationResponse:authorizationResponse - dispatchQueue:dispatch_get_main_queue() + callbackDispatchQueue:dispatch_get_main_queue() callback:callback]; } + (void)performTokenRequest:(OIDTokenRequest *)request originalAuthorizationResponse:(OIDAuthorizationResponse *_Nullable)authorizationResponse - dispatchQueue:(dispatch_queue_t)dispatchQueue + callbackDispatchQueue:(dispatch_queue_t)callbackDispatchQueue callback:(OIDTokenCallback)callback { NSURLRequest *URLRequest = [request URLRequest]; @@ -463,7 +463,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeNetworkError underlyingError:error description:errorDescription]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -492,7 +492,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities OAuthErrorWithDomain:OIDOAuthTokenErrorDomain OAuthResponse:json underlyingError:serverError]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, oauthError); }); return; @@ -508,7 +508,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeServerError underlyingError:serverError description:errorDescription]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -526,7 +526,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeJSONDeserializationError underlyingError:jsonDeserializationError description:errorDescription]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -540,7 +540,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeTokenResponseConstructionError underlyingError:jsonDeserializationError description:@"Token response invalid."]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, returnedError); }); return; @@ -561,7 +561,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenParsingError underlyingError:nil description:@"ID Token parsing failed"]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -578,7 +578,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"Issuer mismatch"]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -594,7 +594,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"Audience mismatch"]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -620,7 +620,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"ID Token expired"]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -638,7 +638,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:message]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -654,7 +654,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request [OIDErrorUtilities errorWithCode:OIDErrorCodeIDTokenFailedValidationError underlyingError:nil description:@"Nonce mismatch"]; - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(nil, invalidIDToken); }); return; @@ -669,7 +669,7 @@ + (void)performTokenRequest:(OIDTokenRequest *)request } // Success - dispatch_async(dispatchQueue, ^{ + dispatch_async(callbackDispatchQueue, ^{ callback(tokenResponse, nil); }); }] resume];