Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift 4.1 + iOS 11 (SFAuthenticationSession) #41

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions Simplicity.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,12 @@
DF74EC271CE2A8BB008F16BF /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = Stormpath;
TargetAttributes = {
DF74EC2F1CE2A8BB008F16BF = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0800;
LastSwiftMigration = 0930;
};
};
};
Expand All @@ -165,6 +165,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = DF74EC261CE2A8BB008F16BF;
Expand Down Expand Up @@ -218,14 +219,22 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand Down Expand Up @@ -270,14 +279,22 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand Down Expand Up @@ -324,7 +341,8 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
Expand All @@ -344,7 +362,8 @@
PRODUCT_BUNDLE_IDENTIFIER = com.stormpath.Simplicity;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
name = Release;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0930"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
12 changes: 9 additions & 3 deletions Simplicity/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@ class Helpers {
- returns: A list of URL Schemes that match the filter closure.
*/
static func registeredURLSchemes(filter closure: (String) -> Bool) -> [String] {
// Retrieve url types from the main bundle
guard let urlTypes = Bundle.main.infoDictionary?["CFBundleURLTypes"] as? [[String: AnyObject]] else {
return [String]()
}

// Convert the complex dictionary into an array of URL schemes
let urlSchemes = urlTypes.flatMap({($0["CFBundleURLSchemes"] as? [String])?.first })
let urlSchemes: [String] = urlTypes.reduce(into: []) { (result, component) in
if let schemes = component["CFBundleURLSchemes"] as? [String] {
result.append(contentsOf: schemes)
}
}

return urlSchemes.flatMap({closure($0) ? $0 : nil})
// Filter schemes with parameter block
return urlSchemes.compactMap({closure($0) ? $0 : nil})
}

/**
Expand All @@ -36,7 +42,7 @@ class Helpers {
- returns: A query string
*/
static func queryString(_ parts: [String: String?]) -> String? {
return parts.flatMap { key, value -> String? in
return parts.compactMap { key, value -> String? in
if let value = value {
return key + "=" + value
} else {
Expand Down
7 changes: 6 additions & 1 deletion Simplicity/LoginProviders/Facebook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ public class Facebook: OAuth2 {
/// An array with query string parameters for the authorization URL.
override public var authorizationURLParameters: [String : String?] {
var result = super.authorizationURLParameters
// Required parameters to have the button to connect via the native fb app
result["auth_type"] = authType.rawValue
result["display"] = "touch"
result["sdk"] = "ios"
result["fbapp_pres"] = "1"
result["sdk_version"] = "4.31.1"
return result
}

Expand All @@ -53,7 +58,7 @@ public class Facebook: OAuth2 {
let range = urlScheme.range(of: "\\d+", options: .regularExpression) else {
preconditionFailure("You must configure your Facebook URL Scheme to use Facebook login.")
}
let clientId = urlScheme.substring(with: range)
let clientId = String(urlScheme[range.lowerBound..<range.upperBound])
let authorizationEndpoint = URL(string: "https://www.facebook.com/dialog/oauth")!
let redirectEndpoint = URL(string: urlScheme + "://authorize")!

Expand Down
2 changes: 1 addition & 1 deletion Simplicity/LoginProviders/VKontakte.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class VKontakte: OAuth2 {
let range = urlScheme.range(of: "\\d+", options: .regularExpression) else {
preconditionFailure("You must configure your VK URL Scheme to use VK login.")
}
let clientId = urlScheme.substring(with: range)
let clientId = String(urlScheme[range.lowerBound..<range.upperBound])
let authorizationEndpoint = URL(string: "https://oauth.vk.com/authorize")!
let redirectEndpoint = URL(string: urlScheme + "://authorize")!

Expand Down
2 changes: 1 addition & 1 deletion Simplicity/OAuth2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class OAuth2: LoginProvider {

var url = URLComponents(url: authorizationEndpoint, resolvingAgainstBaseURL: false)!

url.queryItems = authorizationURLParameters.flatMap({key, value -> URLQueryItem? in
url.queryItems = authorizationURLParameters.compactMap({key, value -> URLQueryItem? in
return value != nil ? URLQueryItem(name: key, value: value) : nil
})

Expand Down
54 changes: 43 additions & 11 deletions Simplicity/Simplicity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,61 @@ import SafariServices
/// Callback handler after an external login completes.
public typealias ExternalLoginCallback = (String?, NSError?) -> Void

/**
/**
Simplicity is a framework for authenticating with external providers on iOS.
*/
public final class Simplicity {
private static var currentLoginProvider: LoginProvider?
private static var callback: ExternalLoginCallback?
private static var safari: UIViewController?
@available(iOS 11.0, *)
private static var authSession: SFAuthenticationSession?

/**
Begin the login flow by redirecting to the LoginProvider's website.

- parameters:
- loginProvider: The login provider object configured to be used.
- callback: A callback with the access token, or a SimplicityError.
- loginProvider: The login provider object configured to be used.
- callback: A callback with the access token, or a SimplicityError.
*/
public static func login(_ loginProvider: LoginProvider, callback: @escaping ExternalLoginCallback) {
self.currentLoginProvider = loginProvider
self.callback = callback

presentSafariView(loginProvider.authorizationURL)
if #available(iOS 11, *) {
if self.presentAuthentificationSession(url: loginProvider.authorizationURL, callbackURL: loginProvider.urlScheme) == false {
self.presentSafariView(loginProvider.authorizationURL)
}
} else {
self.presentSafariView(loginProvider.authorizationURL)
}
}

/// Deep link handler (iOS9)
public static func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any]) -> Bool {
safari?.dismiss(animated: true, completion: nil)
guard let callback = callback, url.scheme == currentLoginProvider?.urlScheme else {
return false
public static func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
if let safari = self.safari {
safari.dismiss(animated: true, completion: nil)
self.safari = nil
}
currentLoginProvider?.linkHandler(url, callback: callback)
currentLoginProvider = nil
if #available(iOS 11.0, *) {
if let auth = self.authSession {
auth.cancel()
self.authSession = nil
}
}
guard let callback = self.callback,
let currentLoginProvider = self.currentLoginProvider,
url.scheme == currentLoginProvider.urlScheme
else { return false }
currentLoginProvider.linkHandler(url, callback: callback)
self.currentLoginProvider = nil

return true
}

/// Deep link handler (<iOS9)
public static func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
return self.application(application, open: url, options: [UIApplicationOpenURLOptionsKey: Any]())
return self.application(application, open: url, options: [UIApplication.OpenURLOptionsKey: Any]())
}

private static func presentSafariView(_ url: URL) {
Expand All @@ -63,4 +81,18 @@ public final class Simplicity {
UIApplication.shared.openURL(url)
}
}

@available(iOS 11.0, *)
private static func presentAuthentificationSession(url: URL, callbackURL: String) -> Bool {

let session = SFAuthenticationSession(url: url, callbackURLScheme: nil) { (url, error) in
self.authSession = nil
if let url = url {
_ = self.application(UIApplication.shared, open: url, options: [:])
}
}
self.authSession = session
return session.start()

}
}