Skip to content

Commit 1fc06a4

Browse files
committed
Add no presenting vc auth flow support to mac catalyst external user agent
Also updates the presentExternUserAgentRequest test to work for both iOS and catalyst
1 parent ac2babc commit 1fc06a4

File tree

3 files changed

+75
-11
lines changed

3 files changed

+75
-11
lines changed

Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,27 @@ NS_ASSUME_NONNULL_BEGIN
3232
API_AVAILABLE(macCatalyst(13)) API_UNAVAILABLE(ios)
3333
@interface OIDExternalUserAgentCatalyst : NSObject<OIDExternalUserAgent>
3434

35-
/*! @internal
36-
@brief Unavailable. Please use @c initWithPresentingViewController:
35+
/*! @brief Create an external user-agent.
36+
@discussion The specific authentication UI used depends on the iOS version and accessibility
37+
options. iOS 8 uses the system browser, iOS 9-10 use @c SFSafariViewController, iOS 11 uses
38+
@c SFAuthenticationSession (unless Guided Access is on which does not work) or uses
39+
@c SFSafariViewController, and iOS 12+ uses @c ASWebAuthenticationSession (unless Guided
40+
Access is on).
3741
*/
38-
- (nonnull instancetype)init NS_UNAVAILABLE;
42+
- (nonnull instancetype)init;
3943

40-
/*! @brief The designated initializer.
44+
/*! @brief Create an external user-agent which optionally uses a private authentication session.
45+
@param prefersEphemeralSession Whether the caller prefers to use a private authentication
46+
session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more.
47+
*/
48+
- (nullable instancetype)initWithPrefersEphemeralSession:(BOOL)prefersEphemeralSession;
49+
50+
/*! @brief Create an external user-agent with a presenting view controller.
4151
@param presentingViewController The view controller from which to present the
4252
\SFSafariViewController.
4353
*/
4454
- (nullable instancetype)initWithPresentingViewController:
45-
(UIViewController *)presentingViewController
46-
NS_DESIGNATED_INITIALIZER;
55+
(UIViewController *)presentingViewController;
4756

4857
/*! @brief Create an external user-agent which optionally uses a private authentication session.
4958
@param presentingViewController The view controller from which to present the browser.

Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,22 @@ @implementation OIDExternalUserAgentCatalyst {
4545
ASWebAuthenticationSession *_webAuthenticationVC;
4646
}
4747

48+
- (nonnull instancetype)init {
49+
self = [super init];
50+
return self;
51+
}
52+
53+
- (nullable instancetype)initWithPrefersEphemeralSession:(BOOL)prefersEphemeralSession {
54+
self = [self init];
55+
if (self) {
56+
_prefersEphemeralSession = prefersEphemeralSession;
57+
}
58+
return self;
59+
}
60+
4861
- (nullable instancetype)initWithPresentingViewController:
4962
(UIViewController *)presentingViewController {
50-
self = [super init];
63+
self = [self init];
5164
if (self) {
5265
_presentingViewController = presentingViewController;
5366
}
@@ -71,6 +84,38 @@ - (BOOL)presentExternalUserAgentRequest:(id<OIDExternalUserAgentRequest>)request
7184
return NO;
7285
}
7386

87+
if (!_presentingViewController) {
88+
// Find presentingViewController
89+
if (@available(iOS 13.0, *)) {
90+
NSSet<UIWindowScene *> *scenes =
91+
(NSSet<UIWindowScene *> *)[UIApplication sharedApplication].connectedScenes;
92+
93+
NSMutableArray<UIWindow *> *windows = [NSMutableArray array];
94+
95+
for (UIWindowScene *scene in scenes) {
96+
[windows addObjectsFromArray:scene.windows];
97+
}
98+
99+
for (UIWindow *window in windows) {
100+
if (window.isKeyWindow) { // False if calling before window appears
101+
UIWindow *keyWindow = window;
102+
_presentingViewController = keyWindow.rootViewController;
103+
break;
104+
}
105+
}
106+
} else {
107+
// ≤ iOS 12.X
108+
NSArray<UIWindow *> *windows = UIApplication.sharedApplication.windows;
109+
NSPredicate *keyWindowPredicate = [NSPredicate predicateWithFormat:@"keyWindow == YES"];
110+
UIWindow *keyWindow = [windows filteredArrayUsingPredicate:keyWindowPredicate].firstObject;
111+
_presentingViewController = keyWindow.rootViewController;
112+
}
113+
if (!_presentingViewController) {
114+
// Unable to find a presentingViewController; perhaps because no window is key and visible
115+
return NO;
116+
}
117+
}
118+
74119
_externalUserAgentFlowInProgress = YES;
75120
_session = session;
76121
BOOL openedUserAgent = NO;

UnitTests/AppAuth/OIDExternUserAgentIOSTests.m renamed to UnitTests/AppAuth/OIDExternUserAgentTests.m

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*! @file OIDExternalUserAgentIOSTests.m
1+
/*! @file OIDExternalUserAgentTests.m
22
@brief AppAuth iOS SDK
33
@copyright
44
Copyright 2023 The AppAuth Authors. All Rights Reserved.
@@ -16,25 +16,35 @@
1616
limitations under the License.
1717
*/
1818

19+
#import <TargetConditionals.h>
20+
1921
#import <XCTest/XCTest.h>
2022

2123
#if SWIFT_PACKAGE
2224
@import AppAuth;
2325
@import TestHelpers;
2426
#else
27+
#import "Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h"
2528
#import "Source/AppAuth/iOS/OIDExternalUserAgentIOS.h"
2629
#import "Source/AppAuthCore/OIDError.h"
2730
#import "UnitTests/TestHelpers/OIDAuthorizationRequest+TestHelper.h"
2831
#endif
2932

30-
@interface OIDExternalUserAgentIOSTests : XCTestCase
33+
@interface OIDExternalUserAgentTests : XCTestCase
3134

3235
@end
3336

34-
@implementation OIDExternalUserAgentIOSTests
37+
@implementation OIDExternalUserAgentTests
3538

3639
- (void)testThatPresentExternalUserAgentRequestReturnsNoWhenMissingPresentingViewController {
37-
OIDExternalUserAgentIOS *userAgent = [[OIDExternalUserAgentIOS alloc] init];
40+
id<OIDExternalUserAgent> userAgent;
41+
42+
#if TARGET_OS_MACCATALYST
43+
userAgent = [[OIDExternalUserAgentCatalyst alloc] init];
44+
#elif TARGET_OS_IOS
45+
userAgent = [[OIDExternalUserAgentIOS alloc] init];
46+
#endif
47+
3848
OIDAuthorizationRequest *authRequest = [OIDAuthorizationRequest testInstance];
3949
[OIDAuthorizationService
4050
presentAuthorizationRequest:authRequest

0 commit comments

Comments
 (0)