Skip to content

Add foundation for presenting authorization flow without a window or view controller #743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
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
Prev Previous commit
Next Next commit
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
  • Loading branch information
mdmathias committed Jan 13, 2023
commit 2af25fd461650c564854f1eb09d630cc5decbc7f
21 changes: 15 additions & 6 deletions Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h
Original file line number Diff line number Diff line change
@@ -32,18 +32,27 @@ NS_ASSUME_NONNULL_BEGIN
API_AVAILABLE(macCatalyst(13)) API_UNAVAILABLE(ios)
@interface OIDExternalUserAgentCatalyst : NSObject<OIDExternalUserAgent>

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

/*! @brief The designated initializer.
/*! @brief Create an external user-agent which optionally uses a private authentication session.
@param prefersEphemeralSession Whether the caller prefers to use a private authentication
session. See @c ASWebAuthenticationSession.prefersEphemeralWebBrowserSession for more.
*/
- (nullable instancetype)initWithPrefersEphemeralSession:(BOOL)prefersEphemeralSession;

/*! @brief Create an external user-agent with a presenting view controller.
@param presentingViewController The view controller from which to present the
\SFSafariViewController.
*/
- (nullable instancetype)initWithPresentingViewController:
(UIViewController *)presentingViewController
NS_DESIGNATED_INITIALIZER;
(UIViewController *)presentingViewController;

/*! @brief Create an external user-agent which optionally uses a private authentication session.
@param presentingViewController The view controller from which to present the browser.
47 changes: 46 additions & 1 deletion Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.m
Original file line number Diff line number Diff line change
@@ -45,9 +45,22 @@ @implementation OIDExternalUserAgentCatalyst {
ASWebAuthenticationSession *_webAuthenticationVC;
}

- (nonnull instancetype)init {
self = [super init];
return self;
}

- (nullable instancetype)initWithPrefersEphemeralSession:(BOOL)prefersEphemeralSession {
self = [self init];
if (self) {
_prefersEphemeralSession = prefersEphemeralSession;
}
return self;
}

- (nullable instancetype)initWithPresentingViewController:
(UIViewController *)presentingViewController {
self = [super init];
self = [self init];
if (self) {
_presentingViewController = presentingViewController;
}
@@ -71,6 +84,38 @@ - (BOOL)presentExternalUserAgentRequest:(id<OIDExternalUserAgentRequest>)request
return NO;
}

if (!_presentingViewController) {
// Find presentingViewController
if (@available(iOS 13.0, *)) {
NSSet<UIWindowScene *> *scenes =
(NSSet<UIWindowScene *> *)[UIApplication sharedApplication].connectedScenes;

NSMutableArray<UIWindow *> *windows = [NSMutableArray array];

for (UIWindowScene *scene in scenes) {
[windows addObjectsFromArray:scene.windows];
}

for (UIWindow *window in windows) {
if (window.isKeyWindow) { // False if calling before window appears
UIWindow *keyWindow = window;
_presentingViewController = keyWindow.rootViewController;
break;
}
}
} else {
// ≤ iOS 12.X
NSArray<UIWindow *> *windows = UIApplication.sharedApplication.windows;
NSPredicate *keyWindowPredicate = [NSPredicate predicateWithFormat:@"keyWindow == YES"];
UIWindow *keyWindow = [windows filteredArrayUsingPredicate:keyWindowPredicate].firstObject;
_presentingViewController = keyWindow.rootViewController;
}
if (!_presentingViewController) {
// Unable to find a presentingViewController; perhaps because no window is key and visible
return NO;
}
}

_externalUserAgentFlowInProgress = YES;
_session = session;
BOOL openedUserAgent = NO;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*! @file OIDExternalUserAgentIOSTests.m
/*! @file OIDExternalUserAgentTests.m
@brief AppAuth iOS SDK
@copyright
Copyright 2023 The AppAuth Authors. All Rights Reserved.
@@ -16,25 +16,35 @@
limitations under the License.
*/

#import <TargetConditionals.h>

#import <XCTest/XCTest.h>

#if SWIFT_PACKAGE
@import AppAuth;
@import TestHelpers;
#else
#import "Source/AppAuth/iOS/OIDExternalUserAgentCatalyst.h"
#import "Source/AppAuth/iOS/OIDExternalUserAgentIOS.h"
#import "Source/AppAuthCore/OIDError.h"
#import "UnitTests/TestHelpers/OIDAuthorizationRequest+TestHelper.h"
#endif

@interface OIDExternalUserAgentIOSTests : XCTestCase
@interface OIDExternalUserAgentTests : XCTestCase

@end

@implementation OIDExternalUserAgentIOSTests
@implementation OIDExternalUserAgentTests

- (void)testThatPresentExternalUserAgentRequestReturnsNoWhenMissingPresentingViewController {
OIDExternalUserAgentIOS *userAgent = [[OIDExternalUserAgentIOS alloc] init];
id<OIDExternalUserAgent> userAgent;

#if TARGET_OS_MACCATALYST
userAgent = [[OIDExternalUserAgentCatalyst alloc] init];
#elif TARGET_OS_IOS
userAgent = [[OIDExternalUserAgentIOS alloc] init];
#endif

OIDAuthorizationRequest *authRequest = [OIDAuthorizationRequest testInstance];
[OIDAuthorizationService
presentAuthorizationRequest:authRequest