Skip to content

Instantly share code, notes, and snippets.

@yesleon
Last active July 13, 2020 03:51
Show Gist options
  • Save yesleon/904d8fe418a17467aada5b8661dba665 to your computer and use it in GitHub Desktop.
Save yesleon/904d8fe418a17467aada5b8661dba665 to your computer and use it in GitHub Desktop.
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