Skip to content

Commit 58e4bae

Browse files
ivo liondovivo liondov
authored andcommitted
Recover early iOS sessions with shared service lookup
1 parent eda97f6 commit 58e4bae

File tree

5 files changed

+83
-10
lines changed

5 files changed

+83
-10
lines changed

ios/ApproovPinningDelegate.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ NS_ASSUME_NONNULL_BEGIN
4040
/// @param approovService is the ApproovService that will provide the pinning
4141
/// information
4242
+ (instancetype)createWithDelegate:(id<NSURLSessionDataDelegate> _Nullable)delegate
43-
approovService:(ApproovService *)approovService;
43+
approovService:(ApproovService *_Nullable)approovService;
4444

4545
/// Initializes a pinning URL session delegate.
4646
///
4747
/// @param delegate is the original delgate
4848
/// @param approovService is the ApproovService that will provide the pinning
4949
/// information
5050
- (instancetype)initWithDelegate:(id<NSURLSessionDataDelegate> _Nullable)delegate
51-
approovService:(ApproovService *)approovService;
51+
approovService:(ApproovService *_Nullable)approovService;
5252

5353
@end
5454

ios/ApproovPinningDelegate.m

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ @interface PinningURLSessionDelegate ()
3838

3939
// the ApproovService that is able to interpret the trust decisions against the
4040
// dynamic pins
41-
@property ApproovService *approovService;
41+
@property(nonatomic, strong, nullable) ApproovService *approovService;
4242

4343
// the original delegate to which non-authentication calls are passed
4444
@property(nullable) id<NSURLSessionDataDelegate> originalDelegate;
@@ -54,7 +54,7 @@ @implementation PinningURLSessionDelegate
5454
* information
5555
*/
5656
+ (instancetype)createWithDelegate:(id<NSURLSessionDataDelegate> _Nullable)delegate
57-
approovService:(ApproovService *)approovService {
57+
approovService:(ApproovService *_Nullable)approovService {
5858
return [[self alloc] initWithDelegate:delegate approovService:approovService];
5959
}
6060

@@ -66,7 +66,7 @@ + (instancetype)createWithDelegate:(id<NSURLSessionDataDelegate> _Nullable)deleg
6666
* information
6767
*/
6868
- (instancetype)initWithDelegate:(id<NSURLSessionDataDelegate> _Nullable)delegate
69-
approovService:(ApproovService *)approovService {
69+
approovService:(ApproovService *_Nullable)approovService {
7070
self = [super init];
7171
if (self) {
7272
_approovService = approovService;
@@ -77,6 +77,19 @@ - (instancetype)initWithDelegate:(id<NSURLSessionDataDelegate> _Nullable)delegat
7777
return self;
7878
}
7979

80+
/**
81+
* Resolves the current ApproovService. Delegates created during early
82+
* swizzling may be initialized before the native module exists, so pinning
83+
* must recover the live service at challenge time.
84+
*/
85+
- (ApproovService *_Nullable)currentApproovService {
86+
ApproovService *service = _approovService ?: [ApproovService sharedService];
87+
if (_approovService == nil && service != nil) {
88+
_approovService = service;
89+
}
90+
return service;
91+
}
92+
8093
/**
8194
* Forwards authentication challenges to the wrapped delegate when available.
8295
* Task-level callback is preferred when a task is available, then
@@ -156,9 +169,23 @@ - (void)URLSession:(NSURLSession *)session
156169
host);
157170
if ([challenge.protectionSpace.authenticationMethod
158171
isEqualToString:NSURLAuthenticationMethodServerTrust]) {
172+
ApproovService *service = [self currentApproovService];
173+
if (service == nil) {
174+
ApproovLogW(@"ApproovService unavailable for task server-trust "
175+
@"challenge on %@, forwarding without pin verification",
176+
host);
177+
[self forwardChallengeToOriginalDelegateForSession:session
178+
task:dataTask
179+
challenge:challenge
180+
completionHandler:completionHandler
181+
challengeType:@"server-trust "
182+
@"(service "
183+
@"unavailable)"];
184+
return;
185+
}
186+
159187
ApproovTrustDecision trustDecision =
160-
[_approovService verifyPins:challenge.protectionSpace.serverTrust
161-
forHost:host];
188+
[service verifyPins:challenge.protectionSpace.serverTrust forHost:host];
162189

163190
// Notify interceptor that pinning was invoked
164191
if (self.authChallengeCallback) {
@@ -212,9 +239,23 @@ - (void)URLSession:(NSURLSession *)session
212239
authMethod, host);
213240
if ([challenge.protectionSpace.authenticationMethod
214241
isEqualToString:NSURLAuthenticationMethodServerTrust]) {
242+
ApproovService *service = [self currentApproovService];
243+
if (service == nil) {
244+
ApproovLogW(@"ApproovService unavailable for session server-trust "
245+
@"challenge on %@, forwarding without pin verification",
246+
host);
247+
[self forwardChallengeToOriginalDelegateForSession:session
248+
task:nil
249+
challenge:challenge
250+
completionHandler:completionHandler
251+
challengeType:@"server-trust "
252+
@"(service "
253+
@"unavailable)"];
254+
return;
255+
}
256+
215257
ApproovTrustDecision trustDecision =
216-
[_approovService verifyPins:challenge.protectionSpace.serverTrust
217-
forHost:host];
258+
[service verifyPins:challenge.protectionSpace.serverTrust forHost:host];
218259

219260
// Notify interceptor that pinning was invoked
220261
if (self.authChallengeCallback) {

ios/ApproovRCTInterceptor.m

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,25 @@ - (void)logSessionMethodResolution:(NSURLSession *)session
345345
NSString *tokenHeader = [ApproovService sharedTokenHeader];
346346
NSString *traceIDHeader = [ApproovService sharedTraceIDHeader];
347347
NSString *tokenBefore = [request valueForHTTPHeaderField:tokenHeader];
348+
ApproovService *service = self.approovService ?: [ApproovService sharedService];
349+
350+
if (service == nil) {
351+
ApproovLogW(@"skipping %@ interception for %@ on session %p because "
352+
@"ApproovService is not yet available",
353+
taskType, request.URL, session);
354+
return [ApproovInterceptorResult createWithRequest:request
355+
withAction:
356+
ApproovInterceptorActionProceed
357+
withMessage:@"ApproovService not "
358+
@"ready"];
359+
}
360+
361+
if (self.approovService == nil) {
362+
self.approovService = service;
363+
}
348364

349365
ApproovInterceptorResult *result =
350-
[self.approovService interceptRequest:request];
366+
[service interceptRequest:request];
351367
NSString *tokenAfterIntercept =
352368
[result.request valueForHTTPHeaderField:tokenHeader];
353369
NSString *traceAfterIntercept =

ios/ApproovService.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ typedef NS_ENUM(NSUInteger, ApproovTrustDecision) {
7474
/// interceptors and bridges calls from Javascript
7575
@interface ApproovService : NSObject <RCTBridgeModule>
7676

77+
/// Returns the current shared service instance when available.
78+
+ (nullable ApproovService *)sharedService;
79+
7780
/// Intercepts a request and updates it to potentially add an Approov token
7881
/// and/or perform substitutions on headers and query parameters.
7982
///

ios/ApproovService.m

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ @implementation ApproovService
109109
// Approov itself can be initialized - or 0.0 they may proceed immediately
110110
NSTimeInterval earliestNetworkRequestTime = 0.0;
111111

112+
// the current shared ApproovService instance
113+
static ApproovService *sharedApproovService = nil;
114+
112115
// original config string used during initialization
113116
NSString *initialConfigString = nil;
114117

@@ -209,6 +212,10 @@ - (instancetype)init {
209212
format:@"Approov native module failed to initialize"];
210213
}
211214

215+
@synchronized([ApproovService class]) {
216+
sharedApproovService = self;
217+
}
218+
212219
// setup the state for the ApproovService
213220
substitutionHeaders = [[NSMutableDictionary alloc] init];
214221
substitutionQueryParams = [[NSMutableSet alloc] init];
@@ -1701,6 +1708,12 @@ + (BOOL)sharedUseApproovStatusIfNoToken {
17011708
return useApproovStatusIfNoToken;
17021709
}
17031710

1711+
+ (ApproovService *)sharedService {
1712+
@synchronized([ApproovService class]) {
1713+
return sharedApproovService;
1714+
}
1715+
}
1716+
17041717
+ (NSString *)sharedTokenHeader {
17051718
@synchronized(approovTokenHeader) {
17061719
return approovTokenHeader;

0 commit comments

Comments
 (0)