A wrapper around ASWebAuthenticationSession and SFAuthenticationSession.
// | |
// Usage: | |
// | |
// Facebook: | |
// | |
// 1. Follow this guide (WITHOUT setting up a URL scheme in Xcode): | |
// https://github.com/fullstackreact/react-native-oauth/issues/76#issuecomment-335902057 | |
// | |
// 2. call startFacebookAuthenticationSession(appID:completionHandler:) on a window: | |
// | |
// view.window?.startFacebookAuthenticationSession(appID: "{app-id}") { | |
// switch $0 { | |
// case .success(let token): | |
// print(token) | |
// case .failure(let error): | |
// print(error) | |
// } | |
// } | |
// | |
// | |
// Dropbox: | |
// | |
// view.window?.startDropboxAuthenticationSession( | |
// appKey: "{app-id}", | |
// callbackURI: "{redirect-uri}" | |
// ) { | |
// switch $0 { | |
// case .success(let token): | |
// print(token) | |
// case .failure(let error): | |
// print(error) | |
// } | |
// } | |
import UIKit | |
import AuthenticationServices | |
import SafariServices | |
@available(iOS 11.0, *) | |
extension UIWindow: ASWebAuthenticationPresentationContextProviding { | |
// MARK: - Core methods | |
@available(iOS 13.0, *) | |
public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { | |
self | |
} | |
// Unified interface for ASWebAuthenticationSession and SFAuthenticationSession. | |
func startAuthenticationSession( | |
url: URL, | |
callbackURLScheme: String?, | |
completionHandler: @escaping (URL?, Error?) -> Void | |
) { | |
if #available(iOS 12.0, *) { | |
var session: ASWebAuthenticationSession? | |
session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) { url, error in | |
completionHandler(url, error) | |
session = nil | |
} | |
if #available(iOS 13.0, *) { | |
session?.presentationContextProvider = self | |
} | |
session?.start() | |
} else { | |
var session: SFAuthenticationSession? | |
session = SFAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) { url, error in | |
completionHandler(url, error) | |
session = nil | |
} | |
session?.start() | |
} | |
} | |
func startImplicitGrantFlowAuthenticationSession( | |
endpoint: URL, | |
clientID: String, | |
callbackURI: String, | |
completionHandler: @escaping (Result<String, Error>) -> Void | |
) { | |
let state = UUID().uuidString | |
var urlComponents = URLComponents(url: endpoint, resolvingAgainstBaseURL: true)! | |
urlComponents.queryItems = [ | |
.init(name: "client_id", value: clientID), | |
.init(name: "response_type", value: "token"), | |
.init(name: "redirect_uri", value: callbackURI), | |
.init(name: "state", value: state) | |
] | |
let callbackURLScheme = String(callbackURI.prefix(while: { $0 != ":" })) | |
startAuthenticationSession(url: urlComponents.url!, callbackURLScheme: callbackURLScheme) { url, error in | |
if let error = error { | |
completionHandler(.failure(error)) | |
} else if let url = url { | |
var urlComponents = URLComponents(string: url.absoluteString) | |
let query = urlComponents?.fragment | |
urlComponents?.query = query | |
let queryItems = urlComponents?.queryItems | |
guard queryItems?.first(where: { $0.name == "state" })?.value == state else { fatalError() } | |
let token = queryItems?.first(where: { $0.name == "access_token" })?.value | |
completionHandler(.success(token!)) | |
} | |
} | |
} | |
// MARK: - Convenience methods | |
func startDropboxAuthenticationSession( | |
appKey: String, | |
callbackURI: String, | |
completionHandler: @escaping (Result<String, Error>) -> Void | |
) { | |
startImplicitGrantFlowAuthenticationSession( | |
endpoint: URL(string: "https://www.dropbox.com/oauth2/authorize")!, | |
clientID: appKey, | |
callbackURI: callbackURI, | |
completionHandler: completionHandler | |
) | |
} | |
func startFacebookAuthenticationSession( | |
appID: String, | |
completionHandler: @escaping (Result<String, Error>) -> Void | |
) { | |
startImplicitGrantFlowAuthenticationSession( | |
endpoint: URL(string: "https://www.facebook.com/v7.0/dialog/oauth")!, | |
clientID: appID, | |
callbackURI: "fb\(appID)://authorize", | |
completionHandler: completionHandler | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment