Skip to content

Commit 0d9ee2a

Browse files
blorkossus-lib
authored andcommitted
Add support for Authentication Session (#305)
* Add support for authorising with ASWebAuthenticationSession * Add fallback support for SFAuthenticationSession on iOS 11
1 parent 0083bc1 commit 0d9ee2a

File tree

2 files changed

+73
-17
lines changed

2 files changed

+73
-17
lines changed

Sources/Base/OAuth2AuthConfig.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public struct OAuth2AuthConfig {
4343
/// Starting with iOS 9, `SFSafariViewController` will be used for embedded authorization instead of our custom class. You can turn this off here.
4444
public var useSafariView = true
4545

46+
/// Starting with iOS 12, `ASWebAuthenticationSession` can be used for embedded authorization instead of our custom class. You can turn this on here.
47+
public var useAuthenticationSession = false
48+
4649
#if os(iOS)
4750
/// By assigning your own style you can configure how the embedded authorization is presented.
4851
public var modalPresentationStyle = UIModalPresentationStyle.fullScreen
@@ -60,7 +63,7 @@ public struct OAuth2AuthConfig {
6063

6164
/// Whether to automatically dismiss the auto-presented authorization screen.
6265
public var authorizeEmbeddedAutoDismiss = true
63-
66+
6467
/// Context information for the authorization flow:
6568
/// - iOS: The parent view controller to present from
6669
/// - macOS: An NSWindow from which to present a modal sheet _or_ `nil` to present in a new window

Sources/iOS/OAuth2Authorizer+iOS.swift

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import UIKit
2323
import SafariServices
24+
import AuthenticationServices
2425
#if !NO_MODULE_IMPORT
2526
import 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

Comments
 (0)