2121
2222import UIKit
2323import SafariServices
24+ import AuthenticationServices
2425#if !NO_MODULE_IMPORT
2526import Base
2627#endif
@@ -39,6 +40,8 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI {
3940 /// Used to store the `SFSafariViewControllerDelegate`.
4041 private var safariViewDelegate : AnyObject ?
4142
43+ /// Used to store the authentication session.
44+ private var authenticationSession : AnyObject ?
4245
4346 public init ( oauth2: OAuth2 ) {
4447 self . oauth2 = oauth2
@@ -72,23 +75,31 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI {
7275 - parameter at: The authorize URL to open
7376 */
7477 public func authorizeEmbedded( with config: OAuth2AuthConfig , at url: URL ) throws {
75- guard let controller = config. authorizeContext as? UIViewController else {
76- throw ( nil == config. authorizeContext) ? OAuth2Error . noAuthorizationContext : OAuth2Error . invalidAuthorizationContext
77- }
78-
79- if #available( iOS 9 , * ) , config. ui. useSafariView {
80- let web = try authorizeSafariEmbedded ( from: controller, at: url)
81- if config. authorizeEmbeddedAutoDismiss {
82- oauth2. internalAfterAuthorizeOrFail = { wasFailure, error in
83- web. dismiss ( animated: true )
78+ if #available( iOS 11 , * ) , config. ui. useAuthenticationSession {
79+ guard let redirect = oauth2. redirect else {
80+ throw OAuth2Error . noRedirectURL
81+ }
82+
83+ authenticationSessionEmbedded ( at: url, withRedirect: redirect)
84+ } else {
85+ guard let controller = config. authorizeContext as? UIViewController else {
86+ throw ( nil == config. authorizeContext) ? OAuth2Error . noAuthorizationContext : OAuth2Error . invalidAuthorizationContext
87+ }
88+
89+ if #available( iOS 9 , * ) , config. ui. useSafariView {
90+ let web = try authorizeSafariEmbedded ( from: controller, at: url)
91+ if config. authorizeEmbeddedAutoDismiss {
92+ oauth2. internalAfterAuthorizeOrFail = { wasFailure, error in
93+ web. dismiss ( animated: true )
94+ }
8495 }
8596 }
86- }
87- else {
88- let web = try authorizeEmbedded ( from : controller , at : url )
89- if config . authorizeEmbeddedAutoDismiss {
90- oauth2 . internalAfterAuthorizeOrFail = { wasFailure , error in
91- web . dismiss ( animated : true )
97+ else {
98+ let web = try authorizeEmbedded ( from : controller , at : url )
99+ if config . authorizeEmbeddedAutoDismiss {
100+ oauth2 . internalAfterAuthorizeOrFail = { wasFailure , error in
101+ web . dismiss ( animated : true )
102+ }
92103 }
93104 }
94105 }
@@ -104,6 +115,47 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI {
104115 open func willPresent( viewController: UIViewController , in naviController: UINavigationController ? ) {
105116 }
106117
118+ // MARK: - SFAuthenticationSession / ASWebAuthenticationSession
119+
120+ /**
121+ Use SFAuthenticationSession or ASWebAuthenticationSession to manage authorisation.
122+
123+ On iOS 11, use SFAuthenticationSession. On iOS 12+, use ASWebAuthenticationSession.
124+
125+ The mechanism works just like when you're using Safari itself to log the user in, hence you **need to implement**
126+ `application(application:openURL:sourceApplication:annotation:)` in your application delegate.
127+
128+ This method dismisses the view controller automatically - this cannot be disabled.
129+
130+ - parameter at: The authorize URL to open
131+ - returns: A Boolean value indicating whether the web authentication session starts successfully.
132+ */
133+ @available ( iOS 11 . 0 , * )
134+ @discardableResult
135+ public func authenticationSessionEmbedded( at url: URL , withRedirect redirect: String ) -> Bool {
136+ let completionHandler : ( URL ? , Error ? ) -> Void = { url, error in
137+ if let url = url {
138+ do {
139+ try self . oauth2. handleRedirectURL ( url as URL )
140+ }
141+ catch let err {
142+ self . oauth2. logger? . warn ( " OAuth2 " , msg: " Cannot intercept redirect URL: \( err) " )
143+ }
144+ } else {
145+ self . oauth2. didFail ( with: nil )
146+ }
147+ self . authenticationSession = nil
148+ }
149+
150+ if #available( iOS 12 , * ) {
151+ authenticationSession = ASWebAuthenticationSession ( url: url, callbackURLScheme: redirect, completionHandler: completionHandler)
152+ return ( authenticationSession as! ASWebAuthenticationSession ) . start ( )
153+ } else {
154+ authenticationSession = SFAuthenticationSession ( url: url, callbackURLScheme: redirect, completionHandler: completionHandler)
155+ return ( authenticationSession as! SFAuthenticationSession ) . start ( )
156+ }
157+ }
158+
107159
108160 // MARK: - Safari Web View Controller
109161
@@ -135,13 +187,14 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI {
135187 web. preferredControlTintColor = tint
136188 }
137189 web. modalPresentationStyle = oauth2. authConfig. ui. modalPresentationStyle
138-
190+
139191 willPresent ( viewController: web, in: nil )
140192 controller. present ( web, animated: true , completion: nil )
141193
142194 return web
143195 }
144196
197+
145198 /**
146199 Called from our delegate, which reacts to users pressing "Done". We can assume this is always a cancel as nomally the Safari view
147200 controller is dismissed automatically.
0 commit comments