Forked from WilliamDenniss/OIDExternalUserAgentIOSSafariViewController.h
Last active
November 11, 2019 21:55
-
-
Save mattio/f2bf65e2cfbe385774a9bc49086366ba to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Original source from AppAuth: https://gist.github.com/WilliamDenniss/18f3779b4a310361bb955cf4e534f29c | |
// NOTE: If you use this for LOGIN, you probably want to use it for LOGOUT, too. | |
#import <Foundation/Foundation.h> | |
#import <UIKit/UIKit.h> | |
#import <SafariServices/SafariServices.h> | |
#import "OIDExternalUserAgent.h" | |
NS_ASSUME_NONNULL_BEGIN | |
/*! @brief Allows library consumers to bootstrap an @c SFSafariViewController as they see fit. | |
@remarks Useful for customizing tint colors and presentation styles. | |
*/ | |
@protocol OIDSafariViewControllerFactoryExternal | |
/*! @brief Creates and returns a new @c SFSafariViewController. | |
@param URL The URL which the @c SFSafariViewController should load initially. | |
*/ | |
- (SFSafariViewController *)safariViewControllerWithURL:(NSURL *)URL; | |
@end | |
/*! @brief A special-case iOS external user-agent that always uses \SFSafariViewController. Most applications should use the more generic @c OIDExternalUserAgentIOS to get the default AppAuth user-agent handling with the benefits of Single Sign-on (SSO) for all supported versions of iOS. | |
*/ | |
@interface OIDExternalUserAgentIOSSafariViewController : NSObject<OIDExternalUserAgent> | |
/*! @brief Allows library consumers to change the @c OIDSafariViewControllerFactory used to create new instances of @c SFSafariViewController. | |
@remarks Useful for customizing tint colors and presentation styles. | |
@param factory The @c OIDSafariViewControllerFactory to use for creating new instances of @c SFSafariViewController. | |
*/ | |
+ (void)setSafariViewControllerFactory:(id<OIDSafariViewControllerFactoryExternal>)factory; | |
/*! @internal | |
@brief Unavailable. Please use @c initWithPresentingViewController: | |
*/ | |
- (nonnull instancetype)init NS_UNAVAILABLE; | |
/*! @brief The designated initializer. | |
@param presentingViewController The view controller from which to present the \SFSafariViewController. | |
*/ | |
- (nullable instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController NS_DESIGNATED_INITIALIZER; | |
@end | |
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Original source from AppAuth: https://gist.github.com/WilliamDenniss/18f3779b4a310361bb955cf4e534f29c | |
// NOTE: If you use this for LOGIN, you probably want to use it for LOGOUT, too. | |
#import "OIDExternalUserAgentIOSSafariViewController.h" | |
#import "OIDErrorUtilities.h" | |
#import "OIDExternalUserAgentSession.h" | |
#import "OIDExternalUserAgentRequest.h" | |
NS_ASSUME_NONNULL_BEGIN | |
/** @brief The global/shared Safari view controller factory. Responsible for creating all new instances of @c SFSafariViewController. | |
*/ | |
static id<OIDSafariViewControllerFactoryExternal> __nullable gSafariViewControllerFactoryExternal; | |
/** @brief The default @c OIDSafariViewControllerFactory which creates new instances of @c SFSafariViewController using known best practices. | |
*/ | |
@interface MSKDefaultSafariViewControllerFactory : NSObject<OIDSafariViewControllerFactoryExternal> | |
@end | |
@interface OIDExternalUserAgentIOSSafariViewController() <SFSafariViewControllerDelegate> | |
@end | |
@implementation OIDExternalUserAgentIOSSafariViewController { | |
UIViewController *_presentingViewController; | |
BOOL _externalUserAgentFlowInProgress; | |
__weak id<OIDExternalUserAgentSession> _session; | |
__weak SFSafariViewController *_safariVC; | |
} | |
/** @brief Obtains the current @c OIDSafariViewControllerFactory; creating a new default instance if required. | |
*/ | |
+ (id<OIDSafariViewControllerFactoryExternal>)safariViewControllerFactory { | |
if (!gSafariViewControllerFactoryExternal) { | |
gSafariViewControllerFactoryExternal = [[MSKDefaultSafariViewControllerFactory alloc] init]; | |
} | |
return gSafariViewControllerFactoryExternal; | |
} | |
+ (void)setSafariViewControllerFactory:(id<OIDSafariViewControllerFactoryExternal>)factory { | |
NSAssert(factory, @"Parameter: |factory| must be non-nil."); | |
gSafariViewControllerFactoryExternal = factory; | |
} | |
- (nullable instancetype)initWithPresentingViewController: (UIViewController *)presentingViewController { | |
self = [super init]; | |
if (self) { | |
_presentingViewController = presentingViewController; | |
} | |
return self; | |
} | |
- (BOOL)presentExternalUserAgentRequest:(id<OIDExternalUserAgentRequest>)request | |
session:(id<OIDExternalUserAgentSession>)session { | |
if (_externalUserAgentFlowInProgress) { | |
// TODO: Handle errors as authorization is already in progress. | |
return NO; | |
} | |
_externalUserAgentFlowInProgress = YES; | |
_session = session; | |
BOOL openedSafari = NO; | |
NSURL *requestURL = [request externalUserAgentRequestURL]; | |
SFSafariViewController *safariVC = [[[self class] safariViewControllerFactory] safariViewControllerWithURL:requestURL]; | |
safariVC.delegate = self; | |
_safariVC = safariVC; | |
[_presentingViewController presentViewController:safariVC animated:YES completion:nil]; | |
openedSafari = YES; | |
if (!openedSafari) { | |
[self cleanUp]; | |
NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError | |
underlyingError:nil | |
description:@"Unable to open Safari."]; | |
[session failExternalUserAgentFlowWithError:safariError]; | |
} | |
return openedSafari; | |
} | |
- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(void (^)(void))completion { | |
if (!_externalUserAgentFlowInProgress) { | |
// Ignore this call if there is no authorization flow in progress. | |
return; | |
} | |
SFSafariViewController *safariVC = _safariVC; | |
[self cleanUp]; | |
if (safariVC) { | |
[safariVC dismissViewControllerAnimated:YES completion:completion]; | |
} else { | |
if (completion) completion(); | |
} | |
} | |
- (void)cleanUp { | |
// The weak references to |_safariVC| and |_session| are set to nil to avoid accidentally using them while not in an authorization flow. | |
_safariVC = nil; | |
_session = nil; | |
_externalUserAgentFlowInProgress = NO; | |
} | |
#pragma mark - SFSafariViewControllerDelegate | |
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller { | |
if (controller != _safariVC) { | |
// Ignore this call if the safari view controller do not match. | |
return; | |
} | |
if (!_externalUserAgentFlowInProgress) { | |
// Ignore this call if there is no authorization flow in progress. | |
return; | |
} | |
id<OIDExternalUserAgentSession> session = _session; | |
[self cleanUp]; | |
NSError *error = [OIDErrorUtilities errorWithCode:OIDErrorCodeProgramCanceledAuthorizationFlow | |
underlyingError:nil | |
description:nil]; | |
[session failExternalUserAgentFlowWithError:error]; | |
} | |
@end | |
@implementation MSKDefaultSafariViewControllerFactory | |
- (SFSafariViewController *)safariViewControllerWithURL:(NSURL *)URL { | |
SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:URL | |
entersReaderIfAvailable:NO]; | |
return safariViewController; | |
} | |
@end | |
NS_ASSUME_NONNULL_END |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment