From fb4d26ea63e5cd80ef767646de9d3f5ea2518c91 Mon Sep 17 00:00:00 2001 From: Maxime Le Moine Date: Tue, 28 Aug 2018 16:48:26 +0200 Subject: [PATCH 01/16] Add compiler flag to disable UIApplication calls (#280) Specifying `P2_APP_EXTENSION` ensures the framework can be compiled for an app extension. --- CONTRIBUTORS.md | 1 + Sources/iOS/OAuth2Authorizer+iOS.swift | 14 +++++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3531902a..7a65758e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -3,6 +3,7 @@ Contributors Contributors to the codebase, in reverse chronological order: +- Maxime Le Moine, @MaximeLM - Seb Skuse, @sebskuse - David Hardiman, @dhardiman - Amaury David, @amaurydavid diff --git a/Sources/iOS/OAuth2Authorizer+iOS.swift b/Sources/iOS/OAuth2Authorizer+iOS.swift index 6fe1705b..e53a3ddc 100644 --- a/Sources/iOS/OAuth2Authorizer+iOS.swift +++ b/Sources/iOS/OAuth2Authorizer+iOS.swift @@ -55,17 +55,13 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { */ public func openAuthorizeURLInBrowser(_ url: URL) throws { - // By asking for the shared instance method by using the "value for key" method on UIApplication, we are able to - // bypass the Swift compilation restriction that blocks the library from being compiled for an extension when - // directly referencing it. We do it as an optional so in the advent of this method being called, like in an - // extension, we handle it as though its not supported. - guard let application = UIApplication.value(forKey: "sharedApplication") as? UIApplication else { - throw OAuth2Error.unableToOpenAuthorizeURL - } - - if !application.openURL(url) { + #if !P2_APP_EXTENSIONS + if !UIApplication.shared.openURL(url) { throw OAuth2Error.unableToOpenAuthorizeURL } + #else + throw OAuth2Error.unableToOpenAuthorizeURL + #endif } /** From 726462dc44f15a12ceea5d5684ce5c48f572b8f1 Mon Sep 17 00:00:00 2001 From: David Jennes Date: Thu, 10 Jan 2019 14:47:11 +0100 Subject: [PATCH 02/16] Set podspec version to 4.2.0 --- CHANGELOG.md | 2 +- p2.OAuth2.podspec | 50 +++++++++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27154b27..38a41abe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog Version numbering represents the Swift version, plus a running number representing updates, fixes and new features at the same time. You can also refer to commit logs to get details on what was implemented, fixed and improved. -### Master +### 4.2.0 - Swift 4.2 support. [djbe](https://github.com/djbe) diff --git a/p2.OAuth2.podspec b/p2.OAuth2.podspec index 538b6705..fb296bc6 100644 --- a/p2.OAuth2.podspec +++ b/p2.OAuth2.podspec @@ -6,9 +6,9 @@ # Pod::Spec.new do |s| - s.name = "p2.OAuth2" - s.version = "4.0.1" - s.summary = "OAuth2 framework for macOS, iOS and tvOS, written in Swift." + s.name = 'p2.OAuth2' + s.version = '4.2.0' + s.summary = 'OAuth2 framework for macOS, iOS and tvOS, written in Swift.' s.description = <<-DESC OAuth2 frameworks for macOS, iOS and tvOS written in Swift. @@ -19,21 +19,37 @@ Pod::Spec.new do |s| Start with `import p2_OAuth2` in your source files. Code documentation is available from within Xcode (ALT + click on symbols) and on [p2.github.io/OAuth2/](http://p2.github.io/OAuth2/). DESC - s.homepage = "https://github.com/p2/OAuth2" - s.documentation_url = "http://p2.github.io/OAuth2/" - s.license = "Apache 2" - s.author = { "Pascal Pfiffner" => "phase.of.matter@gmail.com" } - s.source = { :git => "https://github.com/p2/OAuth2.git", :tag => "#{s.version}", :submodules => true } + s.homepage = 'https://github.com/p2/OAuth2' + s.documentation_url = 'http://p2.github.io/OAuth2/' + s.license = 'Apache 2' + s.author = { + 'Pascal Pfiffner' => 'phase.of.matter@gmail.com' + } - s.ios.deployment_target = "8.0" - s.osx.deployment_target = "10.10" - s.tvos.deployment_target = "9.0" - s.pod_target_xcconfig = { "OTHER_SWIFT_FLAGS" => "-DNO_MODULE_IMPORT -DNO_KEYCHAIN_IMPORT" } + s.source = { + :git => 'https://github.com/p2/OAuth2.git', + :tag => s.version.to_s, + :submodules => true + } + s.swift_version = '4.2' + s.cocoapods_version = '>= 1.4.0' - s.source_files = "SwiftKeychain/Keychain/*.swift", "Sources/Base/*.swift", "Sources/Flows/*.swift", "Sources/DataLoader/*.swift" - s.ios.source_files = "Sources/iOS/*.swift" - s.osx.source_files = "Sources/macOS/*.swift" - s.tvos.source_files = "Sources/tvOS/*.swift" + s.ios.deployment_target = '8.0' + s.osx.deployment_target = '10.10' + s.tvos.deployment_target = '9.0' + s.pod_target_xcconfig = { + 'OTHER_SWIFT_FLAGS' => '-DNO_MODULE_IMPORT -DNO_KEYCHAIN_IMPORT' + } - s.ios.framework = "SafariServices" + s.source_files = [ + 'SwiftKeychain/Keychain/*.swift', + 'Sources/Base/*.swift', + 'Sources/Flows/*.swift', + 'Sources/DataLoader/*.swift' + ] + s.ios.source_files = 'Sources/iOS/*.swift' + s.osx.source_files = 'Sources/macOS/*.swift' + s.tvos.source_files = 'Sources/tvOS/*.swift' + + s.ios.framework = 'SafariServices' end From 77641bc5088f465493d620dd083695741102c74d Mon Sep 17 00:00:00 2001 From: David Jennes Date: Thu, 10 Jan 2019 14:48:04 +0100 Subject: [PATCH 03/16] Use bundler for generating docs --- Gemfile | 17 ++++++++ Gemfile.lock | 103 +++++++++++++++++++++++++++++++++++++++++++++++ generate-docs.sh | 6 +-- 3 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 Gemfile create mode 100644 Gemfile.lock diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..f2530a31 --- /dev/null +++ b/Gemfile @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +# The bare minimum for building, e.g. in Homebrew +group :build do +end + +# In addition to :build, for contributing +group :development do +end + +# For releasing to GitHub +group :release do + gem 'cocoapods', '~> 1.6.0.beta.2' + gem 'jazzy' +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..218b4b5e --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,103 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.0) + activesupport (4.2.11) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + atomos (0.1.3) + claide (1.0.2) + cocoapods (1.6.0.beta.2) + activesupport (>= 4.0.2, < 5) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.6.0.beta.2) + cocoapods-deintegrate (>= 1.0.2, < 2.0) + cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-stats (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.3.1, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (~> 2.0.1) + gh_inspector (~> 1.0) + molinillo (~> 0.6.6) + nap (~> 1.0) + ruby-macho (~> 1.3, >= 1.3.1) + xcodeproj (>= 1.7.0, < 2.0) + cocoapods-core (1.6.0.beta.2) + activesupport (>= 4.0.2, < 6) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + cocoapods-deintegrate (1.0.2) + cocoapods-downloader (1.2.2) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.0) + cocoapods-stats (1.0.0) + cocoapods-trunk (1.3.1) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.1.0) + colored2 (3.1.2) + concurrent-ruby (1.1.4) + escape (0.0.4) + ffi (1.10.0) + fourflusher (2.0.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jazzy (0.9.4) + cocoapods (~> 1.0) + mustache (~> 0.99) + open4 + redcarpet (~> 3.2) + rouge (>= 2.0.6, < 4.0) + sass (~> 3.4) + sqlite3 (~> 1.3) + xcinvoke (~> 0.3.0) + liferaft (0.0.6) + minitest (5.11.3) + molinillo (0.6.6) + mustache (0.99.8) + nanaimo (0.2.6) + nap (1.1.0) + netrc (0.11.0) + open4 (1.3.4) + rb-fsevent (0.10.3) + rb-inotify (0.10.0) + ffi (~> 1.0) + redcarpet (3.4.0) + rouge (3.3.0) + ruby-macho (1.3.1) + sass (3.7.3) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sqlite3 (1.3.13) + thread_safe (0.3.6) + tzinfo (1.2.5) + thread_safe (~> 0.1) + xcinvoke (0.3.0) + liferaft (~> 0.0.6) + xcodeproj (1.7.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods (~> 1.6.0.beta.2) + jazzy + +BUNDLED WITH + 1.17.1 diff --git a/generate-docs.sh b/generate-docs.sh index b5ead1e1..8c3a846c 100755 --- a/generate-docs.sh +++ b/generate-docs.sh @@ -1,12 +1,12 @@ #!/bin/bash # # Build documentation using jazzy: -# [sudo] gem install jazzy +# [sudo] bundle install -jazzy \ +bundle exec jazzy \ -o "docs" \ --min-acl "internal" \ - --module-version "3.0.3" + --module-version "4.2.0" mkdir docs/assets 2>/dev/null cp assets/* docs/assets/ From 9bb789a3f8c12ea768c0f5b3d6827c0216383ab9 Mon Sep 17 00:00:00 2001 From: wtimme Date: Mon, 14 Jan 2019 17:29:40 +0100 Subject: [PATCH 04/16] Update version in sample configurations of the README to 4.2 This makes sure that users who are new to the project start using the new version. Otherwise, if they use Swift 4.2, they will not be able to successfully build their project. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 57bfa060..ea928e06 100644 --- a/README.md +++ b/README.md @@ -477,7 +477,7 @@ The preferred way is to use _git_ directly or _Carthage_. Installation via Carthage is easy enough: ```ruby -github "p2/OAuth2" ~> 3.0 +github "p2/OAuth2" ~> 4.2 ``` #### git @@ -514,7 +514,7 @@ If you're unfamiliar with CocoaPods, read [using CocoaPods](http://guides.cocoap platform :ios, '8.0' # or platform :osx, '10.9' use_frameworks! target `YourApp` do - pod 'p2.OAuth2', '~> 3.0' + pod 'p2.OAuth2', '~> 4.2' end ``` From 0d9ee2a6326731053f9810cc2454249e48e1adce Mon Sep 17 00:00:00 2001 From: Sam Oakley Date: Sat, 16 Feb 2019 22:40:47 +0000 Subject: [PATCH 05/16] Add support for Authentication Session (#305) * Add support for authorising with ASWebAuthenticationSession * Add fallback support for SFAuthenticationSession on iOS 11 --- Sources/Base/OAuth2AuthConfig.swift | 5 +- Sources/iOS/OAuth2Authorizer+iOS.swift | 85 +++++++++++++++++++++----- 2 files changed, 73 insertions(+), 17 deletions(-) diff --git a/Sources/Base/OAuth2AuthConfig.swift b/Sources/Base/OAuth2AuthConfig.swift index f9d5b59b..078050b9 100644 --- a/Sources/Base/OAuth2AuthConfig.swift +++ b/Sources/Base/OAuth2AuthConfig.swift @@ -43,6 +43,9 @@ public struct OAuth2AuthConfig { /// Starting with iOS 9, `SFSafariViewController` will be used for embedded authorization instead of our custom class. You can turn this off here. public var useSafariView = true + /// Starting with iOS 12, `ASWebAuthenticationSession` can be used for embedded authorization instead of our custom class. You can turn this on here. + public var useAuthenticationSession = false + #if os(iOS) /// By assigning your own style you can configure how the embedded authorization is presented. public var modalPresentationStyle = UIModalPresentationStyle.fullScreen @@ -60,7 +63,7 @@ public struct OAuth2AuthConfig { /// Whether to automatically dismiss the auto-presented authorization screen. public var authorizeEmbeddedAutoDismiss = true - + /// Context information for the authorization flow: /// - iOS: The parent view controller to present from /// - macOS: An NSWindow from which to present a modal sheet _or_ `nil` to present in a new window diff --git a/Sources/iOS/OAuth2Authorizer+iOS.swift b/Sources/iOS/OAuth2Authorizer+iOS.swift index e53a3ddc..20477e06 100644 --- a/Sources/iOS/OAuth2Authorizer+iOS.swift +++ b/Sources/iOS/OAuth2Authorizer+iOS.swift @@ -21,6 +21,7 @@ import UIKit import SafariServices +import AuthenticationServices #if !NO_MODULE_IMPORT import Base #endif @@ -39,6 +40,8 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { /// Used to store the `SFSafariViewControllerDelegate`. private var safariViewDelegate: AnyObject? + /// Used to store the authentication session. + private var authenticationSession: AnyObject? public init(oauth2: OAuth2) { self.oauth2 = oauth2 @@ -72,23 +75,31 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { - parameter at: The authorize URL to open */ public func authorizeEmbedded(with config: OAuth2AuthConfig, at url: URL) throws { - guard let controller = config.authorizeContext as? UIViewController else { - throw (nil == config.authorizeContext) ? OAuth2Error.noAuthorizationContext : OAuth2Error.invalidAuthorizationContext - } - - if #available(iOS 9, *), config.ui.useSafariView { - let web = try authorizeSafariEmbedded(from: controller, at: url) - if config.authorizeEmbeddedAutoDismiss { - oauth2.internalAfterAuthorizeOrFail = { wasFailure, error in - web.dismiss(animated: true) + if #available(iOS 11, *), config.ui.useAuthenticationSession { + guard let redirect = oauth2.redirect else { + throw OAuth2Error.noRedirectURL + } + + authenticationSessionEmbedded(at: url, withRedirect: redirect) + } else { + guard let controller = config.authorizeContext as? UIViewController else { + throw (nil == config.authorizeContext) ? OAuth2Error.noAuthorizationContext : OAuth2Error.invalidAuthorizationContext + } + + if #available(iOS 9, *), config.ui.useSafariView { + let web = try authorizeSafariEmbedded(from: controller, at: url) + if config.authorizeEmbeddedAutoDismiss { + oauth2.internalAfterAuthorizeOrFail = { wasFailure, error in + web.dismiss(animated: true) + } } } - } - else { - let web = try authorizeEmbedded(from: controller, at: url) - if config.authorizeEmbeddedAutoDismiss { - oauth2.internalAfterAuthorizeOrFail = { wasFailure, error in - web.dismiss(animated: true) + else { + let web = try authorizeEmbedded(from: controller, at: url) + if config.authorizeEmbeddedAutoDismiss { + oauth2.internalAfterAuthorizeOrFail = { wasFailure, error in + web.dismiss(animated: true) + } } } } @@ -104,6 +115,47 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { open func willPresent(viewController: UIViewController, in naviController: UINavigationController?) { } + // MARK: - SFAuthenticationSession / ASWebAuthenticationSession + + /** + Use SFAuthenticationSession or ASWebAuthenticationSession to manage authorisation. + + On iOS 11, use SFAuthenticationSession. On iOS 12+, use ASWebAuthenticationSession. + + The mechanism works just like when you're using Safari itself to log the user in, hence you **need to implement** + `application(application:openURL:sourceApplication:annotation:)` in your application delegate. + + This method dismisses the view controller automatically - this cannot be disabled. + + - parameter at: The authorize URL to open + - returns: A Boolean value indicating whether the web authentication session starts successfully. + */ + @available(iOS 11.0, *) + @discardableResult + public func authenticationSessionEmbedded(at url: URL, withRedirect redirect: String) -> Bool { + let completionHandler: (URL?, Error?) -> Void = { url, error in + if let url = url { + do { + try self.oauth2.handleRedirectURL(url as URL) + } + catch let err { + self.oauth2.logger?.warn("OAuth2", msg: "Cannot intercept redirect URL: \(err)") + } + } else { + self.oauth2.didFail(with: nil) + } + self.authenticationSession = nil + } + + if #available(iOS 12, *) { + authenticationSession = ASWebAuthenticationSession(url: url, callbackURLScheme: redirect, completionHandler: completionHandler) + return (authenticationSession as! ASWebAuthenticationSession).start() + } else { + authenticationSession = SFAuthenticationSession(url: url, callbackURLScheme: redirect, completionHandler: completionHandler) + return (authenticationSession as! SFAuthenticationSession).start() + } + } + // MARK: - Safari Web View Controller @@ -135,13 +187,14 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { web.preferredControlTintColor = tint } web.modalPresentationStyle = oauth2.authConfig.ui.modalPresentationStyle - + willPresent(viewController: web, in: nil) controller.present(web, animated: true, completion: nil) return web } + /** Called from our delegate, which reacts to users pressing "Done". We can assume this is always a cancel as nomally the Safari view controller is dismissed automatically. From db241a13bdd04b7f333decd2ca964f506616433c Mon Sep 17 00:00:00 2001 From: Ossus Library Date: Sat, 16 Feb 2019 14:44:23 -0800 Subject: [PATCH 06/16] Update README.md Mention `oauth2.authConfig.ui.useAuthenticationSession` --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ea928e06..e337cdee 100644 --- a/README.md +++ b/README.md @@ -368,7 +368,7 @@ The main configuration you'll use with `oauth2.authConfig` is whether or not to oauth2.authConfig.authorizeEmbedded = true -Similarly, if you want to take care of dismissing the login screen yourself: +Similarly, if you want to take care of dismissing the login screen yourself (not possible with the newer authorization sessions mentioned below): oauth2.authConfig.authorizeEmbeddedAutoDismiss = false @@ -394,6 +394,10 @@ Similar is how you specify custom HTTP headers: "headers": ["Accept": "application/json, text/plain"] Starting with version 2.0.1 on iOS 9, `SFSafariViewController` will be used for embedded authorization. +Starting after version 4.2, on iOS 11 (`SFAuthenticationSession`) and iOS 12 (`ASWebAuthenticationSession`), you can opt-in to these newer authorization session view controllers: + + oauth2.authConfig.ui.useAuthenticationSession = true + To revert to the old custom `OAuth2WebViewController`: oauth2.authConfig.ui.useSafariView = false From 7466c21cefdd96220152128b34d231ea3e49079d Mon Sep 17 00:00:00 2001 From: Dave Carlson Date: Fri, 31 May 2019 00:21:04 -0600 Subject: [PATCH 07/16] Fix compile errors with Swift 5 (#313) Thanks! This also fixes #311 --- OAuth2.xcodeproj/project.pbxproj | 5 +++-- README.md | 2 +- Sources/Base/OAuth2Error.swift | 2 +- Sources/Base/OAuth2Logger.swift | 6 +++--- p2.OAuth2.podspec | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/OAuth2.xcodeproj/project.pbxproj b/OAuth2.xcodeproj/project.pbxproj index 36f54c31..27b0b05b 100644 --- a/OAuth2.xcodeproj/project.pbxproj +++ b/OAuth2.xcodeproj/project.pbxproj @@ -590,6 +590,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -910,7 +911,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.11; METAL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -961,7 +962,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 10.11; METAL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_VERSION = 4.0; diff --git a/README.md b/README.md index e337cdee..2561ed06 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ OAuth2 frameworks for **macOS**, **iOS** and **tvOS** written in Swift 4.2. - [๐Ÿ“ฑ Sample iOS app](https://github.com/p2/OAuth2PodApp) (using CocoaPods) - [๐Ÿ“– Technical Documentation](https://p2.github.io/OAuth2) -OAuth2 requires Xcode 8, the built framework can be used on **OS X 10.9** or **iOS 8** and later. +OAuth2 requires Xcode 8, the built framework can be used on **OS X 10.11** or **iOS 8** and later. Happy to accept pull requests, please see [CONTRIBUTING.md](./CONTRIBUTING.md) ### Swift Version diff --git a/Sources/Base/OAuth2Error.swift b/Sources/Base/OAuth2Error.swift index 50bed17f..161eeb49 100644 --- a/Sources/Base/OAuth2Error.swift +++ b/Sources/Base/OAuth2Error.swift @@ -338,7 +338,7 @@ public extension Error { /** Convenience getter to easily retrieve an OAuth2Error from any Error. */ - public var asOAuth2Error: OAuth2Error { + var asOAuth2Error: OAuth2Error { if let oaerror = self as? OAuth2Error { return oaerror } diff --git a/Sources/Base/OAuth2Logger.swift b/Sources/Base/OAuth2Logger.swift index 5383ffe4..c9ad436c 100644 --- a/Sources/Base/OAuth2Logger.swift +++ b/Sources/Base/OAuth2Logger.swift @@ -99,17 +99,17 @@ extension OAuth2Logger { /** Log a message at the trace level. */ public func trace(_ module: String? = "OAuth2", filename: String? = #file, line: Int? = #line, function: String? = #function, msg: @autoclosure() -> String) { - log(.trace, module: module, filename: filename, line: line, function: function, msg: msg) + log(.trace, module: module, filename: filename, line: line, function: function, msg: msg()) } /** Standard debug logging. */ public func debug(_ module: String? = "OAuth2", filename: String? = #file, line: Int? = #line, function: String? = #function, msg: @autoclosure() -> String) { - log(.debug, module: module, filename: filename, line: line, function: function, msg: msg) + log(.debug, module: module, filename: filename, line: line, function: function, msg: msg()) } /** Log warning messages. */ public func warn(_ module: String? = "OAuth2", filename: String? = #file, line: Int? = #line, function: String? = #function, msg: @autoclosure() -> String) { - log(.warn, module: module, filename: filename, line: line, function: function, msg: msg) + log(.warn, module: module, filename: filename, line: line, function: function, msg: msg()) } } diff --git a/p2.OAuth2.podspec b/p2.OAuth2.podspec index fb296bc6..f9b0dd12 100644 --- a/p2.OAuth2.podspec +++ b/p2.OAuth2.podspec @@ -35,7 +35,7 @@ Pod::Spec.new do |s| s.cocoapods_version = '>= 1.4.0' s.ios.deployment_target = '8.0' - s.osx.deployment_target = '10.10' + s.osx.deployment_target = '10.11' s.tvos.deployment_target = '9.0' s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS' => '-DNO_MODULE_IMPORT -DNO_KEYCHAIN_IMPORT' From 7e00f2d995554b4462962e9bbc8ff91ef7ac9671 Mon Sep 17 00:00:00 2001 From: Ossus Date: Sun, 30 Jun 2019 12:37:27 +0200 Subject: [PATCH 08/16] Update to Swift 5 --- .../contents.xcworkspacedata | 7 ++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 +++++ CHANGELOG.md | 7 ++++ CONTRIBUTORS.md | 2 ++ OAuth2.xcodeproj/project.pbxproj | 27 ++++++++------ .../xcshareddata/xcschemes/OAuth2iOS.xcscheme | 26 ++++++-------- .../xcschemes/OAuth2macOS.xcscheme | 26 ++++++-------- .../xcschemes/OAuth2tvOS.xcscheme | 26 ++++++-------- Package.swift | 24 +++++++++---- README.md | 4 +-- Sources/macOS/OAuth2WebViewController.swift | 36 ++++++++++--------- generate-docs.sh | 2 +- p2.OAuth2.podspec | 4 +-- 13 files changed, 111 insertions(+), 88 deletions(-) create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata create mode 100644 .swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a41abe..975c4364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ Changelog Version numbering represents the Swift version, plus a running number representing updates, fixes and new features at the same time. You can also refer to commit logs to get details on what was implemented, fixed and improved. +### 5.0.0 + +- Swift 5.0 support. +- Add support for Authentication Session. + [blork](https://github.com/blork) + [#305](https://github.com/p2/OAuth2/pull/305) + ### 4.2.0 - Swift 4.2 support. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8cf6a2cf..c207b177 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -3,6 +3,8 @@ Contributors Contributors to the codebase, in reverse chronological order: +- Dave Carlson, @drdavec +- Sam Oakley, @blork - David Jennes, @davidjennes - Tim Schmitz, @tschmitz - Maxime Le Moine, @MaximeLM diff --git a/OAuth2.xcodeproj/project.pbxproj b/OAuth2.xcodeproj/project.pbxproj index 27b0b05b..5559aeb1 100644 --- a/OAuth2.xcodeproj/project.pbxproj +++ b/OAuth2.xcodeproj/project.pbxproj @@ -565,7 +565,7 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1100; ORGANIZATIONNAME = "Pascal Pfiffner"; TargetAttributes = { 659854451C5B3BEA00237D39 = { @@ -576,21 +576,20 @@ }; EEE209461942772800736F1A = { CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 1100; }; EEE209A119427DFE00736F1A = { CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0900; + LastSwiftMigration = 1100; TestTargetID = EEE209461942772800736F1A; }; }; }; buildConfigurationList = EEDB861E193FAAE500C4EEA1 /* Build configuration list for PBXProject "OAuth2" */; compatibilityVersion = "Xcode 6.3"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, ); @@ -869,6 +868,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -877,12 +877,14 @@ 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; @@ -927,6 +929,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -935,12 +938,14 @@ 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; @@ -977,7 +982,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -999,7 +1004,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1035,7 +1040,7 @@ PRODUCT_NAME = OAuth2; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1059,7 +1064,7 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -1078,7 +1083,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.github.p2.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1099,7 +1104,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/OAuth2.xcodeproj/xcshareddata/xcschemes/OAuth2iOS.xcscheme b/OAuth2.xcodeproj/xcshareddata/xcschemes/OAuth2iOS.xcscheme index a3685ef9..b6233a37 100644 --- a/OAuth2.xcodeproj/xcshareddata/xcschemes/OAuth2iOS.xcscheme +++ b/OAuth2.xcodeproj/xcshareddata/xcschemes/OAuth2iOS.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -40,23 +48,11 @@ - - - - - - - - + + + + @@ -54,23 +62,11 @@ - - - - - - - - + + + + @@ -40,23 +48,11 @@ - - - - - - - - /dev/null cp assets/* docs/assets/ diff --git a/p2.OAuth2.podspec b/p2.OAuth2.podspec index f9b0dd12..ce7a8549 100644 --- a/p2.OAuth2.podspec +++ b/p2.OAuth2.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| s.name = 'p2.OAuth2' - s.version = '4.2.0' + s.version = '5.0.0' s.summary = 'OAuth2 framework for macOS, iOS and tvOS, written in Swift.' s.description = <<-DESC OAuth2 frameworks for macOS, iOS and tvOS written in Swift. @@ -31,7 +31,7 @@ Pod::Spec.new do |s| :tag => s.version.to_s, :submodules => true } - s.swift_version = '4.2' + s.swift_version = '5.0' s.cocoapods_version = '>= 1.4.0' s.ios.deployment_target = '8.0' From e4deb6cf83c9f43a74cd2deb757af0a381af9635 Mon Sep 17 00:00:00 2001 From: Dave Carlson Date: Wed, 11 Sep 2019 00:28:22 -0600 Subject: [PATCH 09/16] Update Swift package manager configuration (#322) * Update Swift package configuration * Bump version to 5.1.0 --- CHANGELOG.md | 4 +++ Info.plist | 2 +- OAuth2.xcodeproj/project.pbxproj | 26 +++++++++++++------ Package.swift | 9 +++++-- Sources/Flows/OAuth2.swift | 2 +- Sources/OAuth2/OAuth2Module.swift | 13 ++++++++++ Sources/iOS/OAuth2Authorizer+iOS.swift | 2 +- ...wift => OAuth2WebViewController+iOS.swift} | 2 +- ...ft => OAuth2WebViewController+macOS.swift} | 0 p2.OAuth2.podspec | 2 +- 10 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 Sources/OAuth2/OAuth2Module.swift rename Sources/iOS/{OAuth2WebViewController.swift => OAuth2WebViewController+iOS.swift} (99%) rename Sources/macOS/{OAuth2WebViewController.swift => OAuth2WebViewController+macOS.swift} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 975c4364..0e4c5c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Changelog Version numbering represents the Swift version, plus a running number representing updates, fixes and new features at the same time. You can also refer to commit logs to get details on what was implemented, fixed and improved. +### 5.1.0 + +- Update Swift package configuration for use with XCode 11. + ### 5.0.0 - Swift 5.0 support. diff --git a/Info.plist b/Info.plist index a02ba6c2..4b5fa2ec 100644 --- a/Info.plist +++ b/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.0.3 + 5.1.0 CFBundleSignature ???? CFBundleVersion diff --git a/OAuth2.xcodeproj/project.pbxproj b/OAuth2.xcodeproj/project.pbxproj index 5559aeb1..b7538d87 100644 --- a/OAuth2.xcodeproj/project.pbxproj +++ b/OAuth2.xcodeproj/project.pbxproj @@ -31,7 +31,7 @@ 65EC05E11C9050CB00DE9186 /* OAuth2KeychainAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EC05DF1C9050CB00DE9186 /* OAuth2KeychainAccount.swift */; }; 65EC05E21C9050CB00DE9186 /* OAuth2KeychainAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EC05DF1C9050CB00DE9186 /* OAuth2KeychainAccount.swift */; }; CCCE40D6B4EAD9BF05C92ACE /* OAuth2CustomAuthorizer+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCE4C8DC3CB7713E59BC1EE /* OAuth2CustomAuthorizer+iOS.swift */; }; - DD0CCBAD1C4DC83A0044C4E3 /* OAuth2WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController.swift */; }; + DD0CCBAD1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift */; }; EA9758181B222CEA007744B1 /* OAuth2PasswordGrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9758171B222CEA007744B1 /* OAuth2PasswordGrant.swift */; }; EA97581E1B2242F9007744B1 /* OAuth2PasswordGrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9758171B222CEA007744B1 /* OAuth2PasswordGrant.swift */; }; EE1070341E5C7A4200250586 /* OAuth2CustomAuthorizerUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE1070331E5C7A4200250586 /* OAuth2CustomAuthorizerUI.swift */; }; @@ -42,7 +42,7 @@ EE20118C1E44D0BD00913FA7 /* OAuth2DataLoaderSessionTaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE20118B1E44D0BD00913FA7 /* OAuth2DataLoaderSessionTaskDelegate.swift */; }; EE20118D1E44D0BD00913FA7 /* OAuth2DataLoaderSessionTaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE20118B1E44D0BD00913FA7 /* OAuth2DataLoaderSessionTaskDelegate.swift */; }; EE20118E1E44D0BD00913FA7 /* OAuth2DataLoaderSessionTaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE20118B1E44D0BD00913FA7 /* OAuth2DataLoaderSessionTaskDelegate.swift */; }; - EE24862A1AC85DD4002B31AF /* OAuth2WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2486291AC85DD4002B31AF /* OAuth2WebViewController.swift */; }; + EE24862A1AC85DD4002B31AF /* OAuth2WebViewController+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2486291AC85DD4002B31AF /* OAuth2WebViewController+iOS.swift */; }; EE2983701D40B83600933CDD /* OAuth2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE29836F1D40B83600933CDD /* OAuth2.swift */; }; EE2983711D40B83600933CDD /* OAuth2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE29836F1D40B83600933CDD /* OAuth2.swift */; }; EE2983721D40B83600933CDD /* OAuth2.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE29836F1D40B83600933CDD /* OAuth2.swift */; }; @@ -168,14 +168,15 @@ 6598543F1C5B3B4000237D39 /* OAuth2Authorizer+tvOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "OAuth2Authorizer+tvOS.swift"; path = "Sources/tvOS/OAuth2Authorizer+tvOS.swift"; sourceTree = SOURCE_ROOT; }; 659854461C5B3BEA00237D39 /* OAuth2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OAuth2.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 65EC05DF1C9050CB00DE9186 /* OAuth2KeychainAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2KeychainAccount.swift; sourceTree = ""; }; + B4EE6D0922FF07D6004BC0D4 /* OAuth2Module.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2Module.swift; sourceTree = ""; }; CCCE4C8DC3CB7713E59BC1EE /* OAuth2CustomAuthorizer+iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OAuth2CustomAuthorizer+iOS.swift"; sourceTree = ""; }; - DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2WebViewController.swift; sourceTree = ""; }; + DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OAuth2WebViewController+macOS.swift"; sourceTree = ""; }; EA9758171B222CEA007744B1 /* OAuth2PasswordGrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = OAuth2PasswordGrant.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; EE01F96E1C58D5D6003AEA7E /* generate-docs.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "generate-docs.sh"; sourceTree = ""; }; EE1070331E5C7A4200250586 /* OAuth2CustomAuthorizerUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2CustomAuthorizerUI.swift; sourceTree = ""; }; EE1391D91AC5B41A002C7B18 /* OAuth2CodeGrantBasicAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = OAuth2CodeGrantBasicAuth.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; EE20118B1E44D0BD00913FA7 /* OAuth2DataLoaderSessionTaskDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2DataLoaderSessionTaskDelegate.swift; sourceTree = ""; }; - EE2486291AC85DD4002B31AF /* OAuth2WebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2WebViewController.swift; sourceTree = ""; }; + EE2486291AC85DD4002B31AF /* OAuth2WebViewController+iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OAuth2WebViewController+iOS.swift"; sourceTree = ""; }; EE29836A1D3FC28000933CDD /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; EE29836F1D40B83600933CDD /* OAuth2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2.swift; sourceTree = ""; }; EE2983741D40BE7600933CDD /* OAuth2AuthorizerUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth2AuthorizerUI.swift; sourceTree = ""; }; @@ -268,12 +269,20 @@ path = tvOS; sourceTree = ""; }; + B4EE6D0822FF07D6004BC0D4 /* OAuth */ = { + isa = PBXGroup; + children = ( + B4EE6D0922FF07D6004BC0D4 /* OAuth2Module.swift */, + ); + path = OAuth; + sourceTree = ""; + }; EE2486281AC85DD4002B31AF /* iOS */ = { isa = PBXGroup; children = ( EEC7A8C81AE47111008C30E7 /* OAuth2Authorizer+iOS.swift */, CCCE4C8DC3CB7713E59BC1EE /* OAuth2CustomAuthorizer+iOS.swift */, - EE2486291AC85DD4002B31AF /* OAuth2WebViewController.swift */, + EE2486291AC85DD4002B31AF /* OAuth2WebViewController+iOS.swift */, ); path = iOS; sourceTree = ""; @@ -367,7 +376,7 @@ children = ( EEC7A8C61AE46C33008C30E7 /* OAuth2Authorizer+macOS.swift */, 19C919DC1E51CC8000BFC834 /* OAuth2CustomAuthorizer+macOS.swift */, - DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController.swift */, + DD0CCBAC1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift */, ); path = macOS; sourceTree = ""; @@ -411,6 +420,7 @@ EEDB8626193FAAE500C4EEA1 /* OAuth2 */ = { isa = PBXGroup; children = ( + B4EE6D0822FF07D6004BC0D4 /* OAuth */, EE2983731D40BC8900933CDD /* Base */, EE79F65C1BFBDFFF00746243 /* Flows */, EE9EBF111D775A21003263FC /* DataLoader */, @@ -710,7 +720,7 @@ EEACE1E01A7E8FC5009BF3A7 /* OAuth2CodeGrantFacebook.swift in Sources */, EE1391DB1AC5B41A002C7B18 /* OAuth2CodeGrantBasicAuth.swift in Sources */, EE507A391B1E15E000AE02E9 /* OAuth2DynReg.swift in Sources */, - EE24862A1AC85DD4002B31AF /* OAuth2WebViewController.swift in Sources */, + EE24862A1AC85DD4002B31AF /* OAuth2WebViewController+iOS.swift in Sources */, EE79F6581BFA945C00746243 /* OAuth2ClientConfig.swift in Sources */, EE79F65B1BFAA36900746243 /* OAuth2Error.swift in Sources */, CCCE40D6B4EAD9BF05C92ACE /* OAuth2CustomAuthorizer+iOS.swift in Sources */, @@ -726,7 +736,7 @@ EEAEF10B1CDBCF28001A1C6F /* OAuth2Logger.swift in Sources */, 65EC05E01C9050CB00DE9186 /* OAuth2KeychainAccount.swift in Sources */, 0C2F5E5B1DE2DB8500F621E0 /* OAuth2CodeGrantAzure.swift in Sources */, - DD0CCBAD1C4DC83A0044C4E3 /* OAuth2WebViewController.swift in Sources */, + DD0CCBAD1C4DC83A0044C4E3 /* OAuth2WebViewController+macOS.swift in Sources */, EE9EBF1B1D775F74003263FC /* OAuth2Securable.swift in Sources */, EE79F65A1BFAA36900746243 /* OAuth2Error.swift in Sources */, EEC49F311C9BF22400989A18 /* OAuth2AuthRequest.swift in Sources */, diff --git a/Package.swift b/Package.swift index 6c36199d..8e915ea2 100644 --- a/Package.swift +++ b/Package.swift @@ -27,17 +27,22 @@ let package = Package( .macOS(.v10_11), .iOS(.v8), .tvOS(.v9), .watchOS(.v3) ], products: [ - .library(name: "OAuth2", targets: ["Base", "Flows", "DataLoader"]), + .library(name: "OAuth2", targets: ["OAuth2"]), ], dependencies: [ // SwiftKeychain is not yet available as a Package, so we symlink to /Sources and make it a Target //.package(url: "https://github.com/yankodimitrov/SwiftKeychain.git", majorVersion: 1), ], targets: [ + .target(name: "OAuth2", + dependencies: ["Base", "Flows", "DataLoader"]), .target(name: "SwiftKeychain"), .target(name: "Base", dependencies: [.target(name: "SwiftKeychain")]), .target(name: "macOS", dependencies: [.target(name: "Base")]), - .target(name: "Flows", dependencies: [.target(name: "macOS")]), + .target(name: "iOS", dependencies: [.target(name: "Base")]), + .target(name: "tvOS", dependencies: [.target(name: "Base")]), + .target(name: "Flows", dependencies: [ + .target(name: "macOS"), .target(name: "iOS"), .target(name: "tvOS")]), .target(name: "DataLoader", dependencies: [.target(name: "Flows")]), .testTarget(name: "BaseTests", dependencies: [.target(name: "Base"), .target(name: "Flows")]), .testTarget(name: "FlowTests", dependencies: [.target(name: "Flows")]), diff --git a/Sources/Flows/OAuth2.swift b/Sources/Flows/OAuth2.swift index 70702664..5c9013c7 100644 --- a/Sources/Flows/OAuth2.swift +++ b/Sources/Flows/OAuth2.swift @@ -397,7 +397,7 @@ open class OAuth2: OAuth2Base { - parameter callback: The callback to call on the main thread; if both json and error is nil no registration was attempted; error is nil on success */ - func registerClientIfNeeded(callback: @escaping ((OAuth2JSON?, OAuth2Error?) -> Void)) { + public func registerClientIfNeeded(callback: @escaping ((OAuth2JSON?, OAuth2Error?) -> Void)) { if nil != clientId || !type(of: self).clientIdMandatory { callOnMainThread() { callback(nil, nil) diff --git a/Sources/OAuth2/OAuth2Module.swift b/Sources/OAuth2/OAuth2Module.swift new file mode 100644 index 00000000..2c465b8f --- /dev/null +++ b/Sources/OAuth2/OAuth2Module.swift @@ -0,0 +1,13 @@ +// +// OAuth2.swift +// OAuth2 +// +// Created by Dave Carlson on 8/9/19. +// + +@_exported import Base +@_exported import macOS +@_exported import iOS +@_exported import tvOS +@_exported import Flows +@_exported import DataLoader diff --git a/Sources/iOS/OAuth2Authorizer+iOS.swift b/Sources/iOS/OAuth2Authorizer+iOS.swift index 20477e06..b1b1451e 100644 --- a/Sources/iOS/OAuth2Authorizer+iOS.swift +++ b/Sources/iOS/OAuth2Authorizer+iOS.swift @@ -43,7 +43,7 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { /// Used to store the authentication session. private var authenticationSession: AnyObject? - public init(oauth2: OAuth2) { + public init(oauth2: OAuth2Base) { self.oauth2 = oauth2 } diff --git a/Sources/iOS/OAuth2WebViewController.swift b/Sources/iOS/OAuth2WebViewController+iOS.swift similarity index 99% rename from Sources/iOS/OAuth2WebViewController.swift rename to Sources/iOS/OAuth2WebViewController+iOS.swift index 0867c04c..ee8eb12c 100644 --- a/Sources/iOS/OAuth2WebViewController.swift +++ b/Sources/iOS/OAuth2WebViewController+iOS.swift @@ -32,7 +32,7 @@ A simple iOS web view controller that allows you to display the login/authorizat open class OAuth2WebViewController: UIViewController, WKNavigationDelegate { /// Handle to the OAuth2 instance in play, only used for debug lugging at this time. - var oauth: OAuth2? + var oauth: OAuth2Base? /// The URL to load on first show. open var startURL: URL? { diff --git a/Sources/macOS/OAuth2WebViewController.swift b/Sources/macOS/OAuth2WebViewController+macOS.swift similarity index 100% rename from Sources/macOS/OAuth2WebViewController.swift rename to Sources/macOS/OAuth2WebViewController+macOS.swift diff --git a/p2.OAuth2.podspec b/p2.OAuth2.podspec index ce7a8549..307ddac7 100644 --- a/p2.OAuth2.podspec +++ b/p2.OAuth2.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| s.name = 'p2.OAuth2' - s.version = '5.0.0' + s.version = '5.1.0' s.summary = 'OAuth2 framework for macOS, iOS and tvOS, written in Swift.' s.description = <<-DESC OAuth2 frameworks for macOS, iOS and tvOS written in Swift. From 36b154d5aae2d7ae121b8bdafbf86324ac5dc499 Mon Sep 17 00:00:00 2001 From: Larry Brunet <55544254+larrybrunet@users.noreply.github.com> Date: Fri, 20 Sep 2019 12:49:40 -0400 Subject: [PATCH 10/16] Add PKCE support (#324) * Add optional PKCE support * PKCE documentation * PKCE unit tests * Updated contributors * Closes #290 --- CONTRIBUTORS.md | 1 + OAuth2.xcodeproj/project.pbxproj | 1 + README.md | 6 ++ Sources/Base/OAuth2Base.swift | 38 +++++++++++++ Sources/Base/OAuth2ClientConfig.swift | 11 ++++ Sources/Flows/OAuth2.swift | 5 ++ Sources/Flows/OAuth2CodeGrant.swift | 4 +- Tests/FlowTests/OAuth2CodeGrantTests.swift | 65 +++++++++++++++++++++- 8 files changed, 129 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c207b177..f527d018 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -3,6 +3,7 @@ Contributors Contributors to the codebase, in reverse chronological order: +- Larry Brunet, @larrybrunet - Dave Carlson, @drdavec - Sam Oakley, @blork - David Jennes, @davidjennes diff --git a/OAuth2.xcodeproj/project.pbxproj b/OAuth2.xcodeproj/project.pbxproj index b7538d87..a032a8d4 100644 --- a/OAuth2.xcodeproj/project.pbxproj +++ b/OAuth2.xcodeproj/project.pbxproj @@ -404,6 +404,7 @@ EEDB8625193FAAE500C4EEA1 /* Products */, ); sourceTree = ""; + usesTabs = 1; }; EEDB8625193FAAE500C4EEA1 /* Products */ = { isa = PBXGroup; diff --git a/README.md b/README.md index da16495d..f9406d07 100644 --- a/README.md +++ b/README.md @@ -452,6 +452,12 @@ dynreg.register(client: oauth2) { params, error in } ``` +PKCE +---- + +PKCE support is controlled by the `useProofKeyForCodeExchange` property, and the "use_pkce" setting. +It is disabled by default. When enabled, a new code verifier string is generated for every authorization request. + Keychain -------- diff --git a/Sources/Base/OAuth2Base.swift b/Sources/Base/OAuth2Base.swift index 54308523..098d65b3 100644 --- a/Sources/Base/OAuth2Base.swift +++ b/Sources/Base/OAuth2Base.swift @@ -19,6 +19,7 @@ // import Foundation +import CommonCrypto /** @@ -451,6 +452,7 @@ open class OAuth2Base: OAuth2Securable { */ open func assureRefreshTokenParamsAreValid(_ params: OAuth2JSON) throws { } + } @@ -462,6 +464,10 @@ open class OAuth2ContextStore { /// Currently used redirect_url. open var redirectURL: String? + /// Current code verifier used for PKCE + public internal(set) var codeVerifier: String? + public let codeChallengeMethod = "S256" + /// The current state. internal var _state = "" @@ -497,5 +503,37 @@ open class OAuth2ContextStore { func resetState() { _state = "" } + + // MARK: - PKCE + + /** + Generates a new code verifier string + */ + func generateCodeVerifier() { + var buffer = [UInt8](repeating: 0, count: 32) + _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) + codeVerifier = Data(bytes: buffer).base64EncodedString() + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + .trimmingCharacters(in: .whitespaces) + } + + + func codeChallenge() -> String? { + guard let verifier = codeVerifier, let data = verifier.data(using: .utf8) else { return nil } + var buffer = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) + data.withUnsafeBytes { + _ = CC_SHA256($0, CC_LONG(data.count), &buffer) + } + let hash = Data(bytes: buffer) + let challenge = hash.base64EncodedString() + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + .trimmingCharacters(in: .whitespaces) + return challenge + } + } diff --git a/Sources/Base/OAuth2ClientConfig.swift b/Sources/Base/OAuth2ClientConfig.swift index 2a128875..65338e83 100644 --- a/Sources/Base/OAuth2ClientConfig.swift +++ b/Sources/Base/OAuth2ClientConfig.swift @@ -87,6 +87,12 @@ open class OAuth2ClientConfig { /// url. open var safariCancelWorkaround = false + /// Use Proof Key for Code Exchange (PKCE) + /// + /// See https://tools.ietf.org/html/rfc7636 + /// + open var useProofKeyForCodeExchange = false + /** Initializer to initialize properties from a settings dictionary. */ @@ -139,6 +145,11 @@ open class OAuth2ClientConfig { if let assume = settings["token_assume_unexpired"] as? Bool { accessTokenAssumeUnexpired = assume } + + if let usePKCE = settings["use_pkce"] as? Bool { + useProofKeyForCodeExchange = usePKCE + } + } diff --git a/Sources/Flows/OAuth2.swift b/Sources/Flows/OAuth2.swift index 5c9013c7..e0a3e65e 100644 --- a/Sources/Flows/OAuth2.swift +++ b/Sources/Flows/OAuth2.swift @@ -285,6 +285,11 @@ open class OAuth2: OAuth2Base { if clientConfig.safariCancelWorkaround { req.params["swa"] = "\(Date.timeIntervalSinceReferenceDate)" // Safari issue workaround } + if clientConfig.useProofKeyForCodeExchange { + context.generateCodeVerifier() + req.params["code_challenge"] = context.codeChallenge() + req.params["code_challenge_method"] = context.codeChallengeMethod + } req.add(params: params) return req diff --git a/Sources/Flows/OAuth2CodeGrant.swift b/Sources/Flows/OAuth2CodeGrant.swift index 7896d389..f97a40a3 100644 --- a/Sources/Flows/OAuth2CodeGrant.swift +++ b/Sources/Flows/OAuth2CodeGrant.swift @@ -67,7 +67,9 @@ open class OAuth2CodeGrant: OAuth2 { req.params["grant_type"] = type(of: self).grantType req.params["redirect_uri"] = redirect req.params["client_id"] = clientId - + if clientConfig.useProofKeyForCodeExchange { + req.params["code_verifier"] = context.codeVerifier + } return req } diff --git a/Tests/FlowTests/OAuth2CodeGrantTests.swift b/Tests/FlowTests/OAuth2CodeGrantTests.swift index c656de8f..62160cf8 100644 --- a/Tests/FlowTests/OAuth2CodeGrantTests.swift +++ b/Tests/FlowTests/OAuth2CodeGrantTests.swift @@ -97,6 +97,26 @@ class OAuth2CodeGrantTests: XCTestCase { XCTAssertTrue(8 == (query["state"]!).count, "Expecting an auto-generated UUID for `state`") } + func testAuthorizeURIWithPKCE() { + let oauth = OAuth2CodeGrant(settings: [ + "client_id": "abc", + "authorize_uri": "https://auth.ful.io", + "token_uri": "https://token.ful.io", + "keychain": false, + "use_pkce" : true, + ]) + XCTAssertNotNil(oauth.authURL, "Must init `authorize_uri`") + + let comp = URLComponents(url: try! oauth.authorizeURL(withRedirect: "oauth2://callback", scope: nil, params: nil), resolvingAgainstBaseURL: true)! + XCTAssertEqual(comp.host!, "auth.ful.io", "Correct host") + let query = OAuth2CodeGrant.params(fromQuery: comp.percentEncodedQuery!) + XCTAssertEqual(query["client_id"]!, "abc", "Expecting correct `client_id`") + XCTAssertNotNil(query["code_challenge"], "Must have `code_challenge`") + XCTAssertEqual(query["code_challenge_method"]!, "S256", "Expecting correct `code_challenge_method`") + XCTAssertEqual(query["redirect_uri"]!, "oauth2://callback", "Expecting correct `redirect_uri`") + XCTAssertTrue(8 == (query["state"]!).count, "Expecting an auto-generated UUID for `state`") + } + func testRedirectURI() { let oauth = OAuth2CodeGrant(settings: baseSettings) oauth.redirect = "oauth2://callback" @@ -253,7 +273,50 @@ class OAuth2CodeGrantTests: XCTestCase { XCTAssertEqual(query2["redirect_uri"]!, "oauth2://callback", "Expecting correct `redirect_uri`") XCTAssertNil(query2["state"], "`state` must be empty") } - + + func testTokenRequestWithPKCE() { + let oauth = OAuth2CodeGrant(settings: [ + "client_id": "abc", + "authorize_uri": "https://auth.ful.io", + "token_uri": "https://token.ful.io", + "keychain": false, + "use_pkce" : true, + ]) + oauth.redirect = "oauth2://callback" + + // no redirect in context - fail + do { + _ = try oauth.accessTokenRequest(with: "pp") + XCTAssertTrue(false, "Should not be here any more") + } + catch OAuth2Error.noRedirectURL { + XCTAssertTrue(true, "Must be here") + } + catch { + XCTAssertTrue(false, "Should not be here") + } + + // with redirect in context - success + oauth.context.redirectURL = "oauth2://callback" + + // initialize code verifier in context + oauth.context.generateCodeVerifier() + + let req = try! oauth.accessTokenRequest(with: "pp").asURLRequest(for: oauth) + let comp = URLComponents(url: req.url!, resolvingAgainstBaseURL: true)! + XCTAssertEqual(comp.host!, "token.ful.io", "Correct host") + + let body = String(data: req.httpBody!, encoding: String.Encoding.utf8) + let query = OAuth2CodeGrant.params(fromQuery: body!) + XCTAssertEqual(query["client_id"]!, "abc", "Expecting correct `client_id`") + XCTAssertNil(query["client_secret"], "Must not have `client_secret`") + XCTAssertEqual(query["code"]!, "pp", "Expecting correct `code`") + XCTAssertEqual(query["grant_type"]!, "authorization_code", "Expecting correct `grant_type`") + XCTAssertEqual(query["redirect_uri"]!, "oauth2://callback", "Expecting correct `redirect_uri`") + XCTAssertNil(query["state"], "`state` must be empty") + XCTAssertNotNil(query["code_verifier"], "Must have `code_verifier`") + } + func testCustomAuthParameters() { let oauth = OAuth2CodeGrant(settings: baseSettings) oauth.redirect = "oauth2://callback" From c1cf4e9dcc4d33673f913bdd7e3e6dc1d764a684 Mon Sep 17 00:00:00 2001 From: PWrzesinski Date: Mon, 4 Nov 2019 15:38:42 +0100 Subject: [PATCH 11/16] Changed autorizeContext var to weak (#329) --- Sources/Base/OAuth2AuthConfig.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Base/OAuth2AuthConfig.swift b/Sources/Base/OAuth2AuthConfig.swift index 078050b9..feec39ed 100644 --- a/Sources/Base/OAuth2AuthConfig.swift +++ b/Sources/Base/OAuth2AuthConfig.swift @@ -67,7 +67,7 @@ public struct OAuth2AuthConfig { /// Context information for the authorization flow: /// - iOS: The parent view controller to present from /// - macOS: An NSWindow from which to present a modal sheet _or_ `nil` to present in a new window - public var authorizeContext: AnyObject? = nil + public weak var authorizeContext: AnyObject? = nil /// UI-specific configuration. public var ui = UI() From 8a035f73f7f2caacf54e2f024b0377f14367c4d6 Mon Sep 17 00:00:00 2001 From: Denis Date: Mon, 4 Nov 2019 16:41:01 +0200 Subject: [PATCH 12/16] Mac Catalyst support (#328) --- Sources/iOS/OAuth2Authorizer+iOS.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/iOS/OAuth2Authorizer+iOS.swift b/Sources/iOS/OAuth2Authorizer+iOS.swift index b1b1451e..1fcd16c2 100644 --- a/Sources/iOS/OAuth2Authorizer+iOS.swift +++ b/Sources/iOS/OAuth2Authorizer+iOS.swift @@ -146,7 +146,11 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { } self.authenticationSession = nil } - + +#if targetEnvironment(macCatalyst) + authenticationSession = ASWebAuthenticationSession(url: url, callbackURLScheme: redirect, completionHandler: completionHandler) + return (authenticationSession as! ASWebAuthenticationSession).start() +#else if #available(iOS 12, *) { authenticationSession = ASWebAuthenticationSession(url: url, callbackURLScheme: redirect, completionHandler: completionHandler) return (authenticationSession as! ASWebAuthenticationSession).start() @@ -154,6 +158,7 @@ open class OAuth2Authorizer: OAuth2AuthorizerUI { authenticationSession = SFAuthenticationSession(url: url, callbackURLScheme: redirect, completionHandler: completionHandler) return (authenticationSession as! SFAuthenticationSession).start() } +#endif } From 34c8c38f48f125155568ce774bc57be0554ecc56 Mon Sep 17 00:00:00 2001 From: Foti Dim <2326415+fotiDim@users.noreply.github.com> Date: Wed, 6 Nov 2019 18:04:57 +0100 Subject: [PATCH 13/16] Add refresh uri setting (#330) --- CONTRIBUTORS.md | 1 + README.md | 3 +- Sources/Base/OAuth2Base.swift | 1 + Sources/Base/OAuth2ClientConfig.swift | 6 ++++ Sources/Flows/OAuth2.swift | 3 +- Tests/BaseTests/OAuth2Tests.swift | 28 ++++++++++++++++ Tests/FlowTests/OAuth2RefreshTokenTests.swift | 32 +++++++++++++++++++ 7 files changed, 72 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f527d018..b1b6e50d 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -3,6 +3,7 @@ Contributors Contributors to the codebase, in reverse chronological order: +- Foti Dim, @fotidim - Larry Brunet, @larrybrunet - Dave Carlson, @drdavec - Sam Oakley, @blork diff --git a/README.md b/README.md index f9406d07..84a65414 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ oauth2.authConfig.authorizeEmbedded = true oauth2.authConfig.authorizeContext = <# your UIViewController / NSWindow #> ``` +Need to specify a separate refresh token URI? You can set the `refresh_uri` in the Settings Dictionary. If specified the library will refresh access tokens using the `refresh_uri` you specified, otherwise it will use the `token_uri`. + Need to debug? Use a `.debug` or even a `.trace` logger: ```swift @@ -543,4 +545,3 @@ Since there is no `NOTICE` file there is nothing that you have to include in you [sample]: https://github.com/p2/OAuth2App - diff --git a/Sources/Base/OAuth2Base.swift b/Sources/Base/OAuth2Base.swift index 098d65b3..31c63ddc 100644 --- a/Sources/Base/OAuth2Base.swift +++ b/Sources/Base/OAuth2Base.swift @@ -155,6 +155,7 @@ open class OAuth2Base: OAuth2Securable { - client_secret (String), usually only needed for code grant - authorize_uri (URL-String) - token_uri (URL-String), if omitted the authorize_uri will be used to obtain tokens + - refresh_uri (URL-String), if omitted the token_uri will be used to obtain tokens - redirect_uris (Array of URL-Strings) - scope (String) diff --git a/Sources/Base/OAuth2ClientConfig.swift b/Sources/Base/OAuth2ClientConfig.swift index 65338e83..8ee5fdb0 100644 --- a/Sources/Base/OAuth2ClientConfig.swift +++ b/Sources/Base/OAuth2ClientConfig.swift @@ -28,6 +28,9 @@ open class OAuth2ClientConfig { /// The URL where we can exchange a code for a token. public final var tokenURL: URL? + + /// The URL where we can refresh an access token using a refresh token. + public final var refreshURL: URL? /// Where a logo/icon for the app can be found. public final var logoURL: URL? @@ -112,6 +115,9 @@ open class OAuth2ClientConfig { if let token = settings["token_uri"] as? String { tokenURL = URL(string: token) } + if let refresh = settings["refresh_uri"] as? String { + refreshURL = URL(string: refresh) + } if let registration = settings["registration_uri"] as? String { registrationURL = URL(string: registration) } diff --git a/Sources/Flows/OAuth2.swift b/Sources/Flows/OAuth2.swift index e0a3e65e..a004b81f 100644 --- a/Sources/Flows/OAuth2.swift +++ b/Sources/Flows/OAuth2.swift @@ -57,6 +57,7 @@ open class OAuth2: OAuth2Base { - client_secret (String), usually only needed for code grant - authorize_uri (URL-String) - token_uri (URL-String), if omitted the authorize_uri will be used to obtain tokens + - refresh_uri (URL-String), if omitted the token_uri will be used to obtain tokens - redirect_uris (Array of URL-Strings) - scope (String) @@ -343,7 +344,7 @@ open class OAuth2: OAuth2Base { throw OAuth2Error.noRefreshToken } - let req = OAuth2AuthRequest(url: (clientConfig.tokenURL ?? clientConfig.authorizeURL)) + let req = OAuth2AuthRequest(url: (clientConfig.refreshURL ?? clientConfig.tokenURL ?? clientConfig.authorizeURL)) req.params["grant_type"] = "refresh_token" req.params["refresh_token"] = refreshToken if let clientId = clientId { diff --git a/Tests/BaseTests/OAuth2Tests.swift b/Tests/BaseTests/OAuth2Tests.swift index be262241..57e60ecf 100644 --- a/Tests/BaseTests/OAuth2Tests.swift +++ b/Tests/BaseTests/OAuth2Tests.swift @@ -43,6 +43,18 @@ class OAuth2Tests: XCTestCase { "keychain": false, ]) } + + func refreshOAuth2() -> OAuth2 { + return OAuth2(settings: [ + "client_id": "abc", + "authorize_uri": "https://auth.ful.io", + "token_uri": "https://token.ful.io", + "refresh_uri": "https://refresh.ful.io", + "scope": "login", + "verbose": true, + "keychain": false, + ]) + } func testInit() { var oauth = OAuth2(settings: ["client_id": "def"]) @@ -88,6 +100,22 @@ class OAuth2Tests: XCTestCase { //XCTAssertEqual(params["redirect_uri"]!, "oauth2app://callback", "Expecting correct `redirect_uri` in query") XCTAssertNil(params["state"], "Expecting no `state` in query") } + + func testTokenRefreshRequest() { + let oa = refreshOAuth2() + oa.verbose = false + oa.clientConfig.refreshToken = "abc" + let req = try! oa.tokenRequestForTokenRefresh().asURLRequest(for: oa) + let auth = req.url! + + let comp = URLComponents(url: auth, resolvingAgainstBaseURL: true)! + XCTAssertEqual("https", comp.scheme!, "Need correct scheme") + XCTAssertEqual("refresh.ful.io", comp.host!, "Need correct host") + + let params = OAuth2.params(fromQuery: comp.percentEncodedQuery ?? "") + //XCTAssertEqual(params["redirect_uri"]!, "oauth2app://callback", "Expecting correct `redirect_uri` in query") + XCTAssertNil(params["state"], "Expecting no `state` in query") + } func testAuthorizeCall() { let oa = genericOAuth2() diff --git a/Tests/FlowTests/OAuth2RefreshTokenTests.swift b/Tests/FlowTests/OAuth2RefreshTokenTests.swift index 05ea4852..e106e5a2 100644 --- a/Tests/FlowTests/OAuth2RefreshTokenTests.swift +++ b/Tests/FlowTests/OAuth2RefreshTokenTests.swift @@ -41,6 +41,16 @@ class OAuth2RefreshTokenTests: XCTestCase { "keychain": false, ]) } + + func refreshOAuth2() -> OAuth2 { + return OAuth2(settings: [ + "client_id": "abc", + "authorize_uri": "https://auth.ful.io", + "token_uri": "https://token.ful.io", + "refresh_uri": "https://refresh.ful.io", + "keychain": false, + ]) + } func testCannotRefresh() { let oauth = genericOAuth2() @@ -76,6 +86,28 @@ class OAuth2RefreshTokenTests: XCTestCase { XCTAssertNil(dict["client_secret"]) XCTAssertNil(req!.allHTTPHeaderFields?["Authorization"]) } + + func testRefreshRequestWithDedicatedRefreshURI() { + let oauth = refreshOAuth2() + oauth.clientConfig.refreshToken = "pov" + + let req = try? oauth.tokenRequestForTokenRefresh().asURLRequest(for: oauth) + XCTAssertNotNil(req) + XCTAssertNotNil(req?.url) + XCTAssertNotNil(req?.httpBody) + XCTAssertEqual("https://refresh.ful.io", req!.url!.absoluteString) + let comp = URLComponents(url: req!.url!, resolvingAgainstBaseURL: true) + let params = comp?.percentEncodedQuery + XCTAssertNil(params) + let body = String(data: req!.httpBody!, encoding: String.Encoding.utf8) + XCTAssertNotNil(body) + let dict = OAuth2.params(fromQuery: body!) + XCTAssertEqual(dict["client_id"], "abc") + XCTAssertEqual(dict["refresh_token"], "pov") + XCTAssertEqual(dict["grant_type"], "refresh_token") + XCTAssertNil(dict["client_secret"]) + XCTAssertNil(req!.allHTTPHeaderFields?["Authorization"]) + } func testRefreshRequestWithSecret() { let oauth = genericOAuth2() From 624120ed527d3da2eaba376521601c75805ba643 Mon Sep 17 00:00:00 2001 From: Ossus Date: Sun, 17 Nov 2019 01:00:05 +0100 Subject: [PATCH 14/16] Fix compilation errors and warnings from PKCE merge --- .swift-version | 2 +- Sources/Base/OAuth2Base.swift | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.swift-version b/.swift-version index bf77d549..a75b92f1 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2 +5.1 diff --git a/Sources/Base/OAuth2Base.swift b/Sources/Base/OAuth2Base.swift index 31c63ddc..e24178e7 100644 --- a/Sources/Base/OAuth2Base.swift +++ b/Sources/Base/OAuth2Base.swift @@ -510,10 +510,10 @@ open class OAuth2ContextStore { /** Generates a new code verifier string */ - func generateCodeVerifier() { + open func generateCodeVerifier() { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) - codeVerifier = Data(bytes: buffer).base64EncodedString() + codeVerifier = Data(buffer).base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") .replacingOccurrences(of: "=", with: "") @@ -521,13 +521,13 @@ open class OAuth2ContextStore { } - func codeChallenge() -> String? { + open func codeChallenge() -> String? { guard let verifier = codeVerifier, let data = verifier.data(using: .utf8) else { return nil } var buffer = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { - _ = CC_SHA256($0, CC_LONG(data.count), &buffer) + _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &buffer) } - let hash = Data(bytes: buffer) + let hash = Data(buffer) let challenge = hash.base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") From 5e2f70d12cdcbf7b10d8a9aab544e6990a490dcf Mon Sep 17 00:00:00 2001 From: Ossus Date: Sun, 17 Nov 2019 01:05:18 +0100 Subject: [PATCH 15/16] Improve documentation --- README.md | 47 ++++++++++++++--------------------- Sources/Base/OAuth2Base.swift | 1 + Sources/Flows/OAuth2.swift | 1 + 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 84a65414..76d98e9d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ OAuth2 frameworks for **macOS**, **iOS** and **tvOS** written in Swift 5.0. - [โคต๏ธ Installation](#installation) - [๐Ÿ›  Usage](#usage) - [๐Ÿ–ฅ Sample macOS app][sample] (with data loader examples) -- [๐Ÿ“ฑ Sample iOS app](https://github.com/p2/OAuth2PodApp) (using CocoaPods) - [๐Ÿ“– Technical Documentation](https://p2.github.io/OAuth2) OAuth2 requires Xcode 10.2, the built framework can be used on **OS X 10.11** or **iOS 8** and later. @@ -25,7 +24,7 @@ Code compatible with brand new Swift versions are to be found on a separate feat Usage ----- -To use OAuth2 in your own code, start with `import OAuth2` (use `p2_OAuth2` if you installed _p2.OAuth2_ via CocoaPods) in your source files. +To use OAuth2 in your own code, start with `import OAuth2` in your source files. In OAuth2 there are [**different kinds of _flows_**](https://tools.ietf.org/html/rfc6749#page-2). This library supports all of them, make sure you're using the correct one for your use-case and authorization server. @@ -408,6 +407,7 @@ To customize the _go back_ button when using `OAuth2WebViewController` on iOS 8 oauth2.authConfig.ui.backButton = <# UIBarButtonItem(...) #> +See below for settings about [the keychain](#keychain) and [PKCE](#pkce). Usage with Alamofire -------------------- @@ -457,7 +457,7 @@ dynreg.register(client: oauth2) { params, error in PKCE ---- -PKCE support is controlled by the `useProofKeyForCodeExchange` property, and the "use_pkce" setting. +PKCE support is controlled by the `useProofKeyForCodeExchange` property, and the `use_pkce` key in the settings dictionary. It is disabled by default. When enabled, a new code verifier string is generated for every authorization request. @@ -465,7 +465,7 @@ Keychain -------- This framework can transparently use the iOS and macOS keychain. -It is controlled by the `useKeychain` property, which can be disabled during initialization with the "keychain" setting. +It is controlled by the `useKeychain` property, which can be disabled during initialization with the `keychain` settings dictionary key. Since this is **enabled by default**, if you do _not_ turn it off during initialization, the keychain will be queried for tokens and client credentials related to the authorization URL. If you turn it off _after_ initialization, the keychain will be queried for existing tokens, but new tokens will not be written to the keychain. @@ -475,14 +475,25 @@ If you have dynamically registered your client and want to start anew, you can c Ideally, access tokens get delivered with an "expires_in" parameter that tells you how long the token is valid. If it is missing the framework will still use those tokens if one is found in the keychain and not re-perform the OAuth dance. You will need to intercept 401s and re-authorize if an access token has expired but the framework has still pulled it from the keychain. -This behavior can be turned off by supplying "token_assume_unexpired": false in settings or setting `clientConfig.accessTokenAssumeUnexpired` to false. +This behavior can be turned off by supplying `token_assume_unexpired: false` in settings or setting `clientConfig.accessTokenAssumeUnexpired` to false. +These are the settings dictionary keys you can use for more control: + +- `keychain`: a bool on whether to use keychain or not, true by default +- `keychain_access_mode`: a string value for keychain kSecAttrAccessible attribute, "kSecAttrAccessibleWhenUnlocked" by default, you can change this to e.g. "kSecAttrAccessibleAfterFirstUnlock" if you need the tokens to be available when the phone is locked. +- `keychain_access_group`: a string value for keychain kSecAttrAccessGroup attribute, nil by default +- `keychain_account_for_client_credentials`: the name to use to identify client credentials in the keychain, "clientCredentials" by default +- `keychain_account_for_tokens`: the name to use to identify the tokens in the keychain, "currentTokens" by default Installation ------------ -You can use _git_, _Carthage_ and even _CocoaPods_ to install the framework. -The preferred way is to use _git_ directly or _Carthage_. +You can use the _Swift Package Manager_, _git_ or _Carthage_. +The preferred way is to use the _Swift Package Manager_. + +#### Swift Package Manager + +In Xcode 11 and newer, choose "File" from the Xcode Menu, then "Swift Packages" ยป "Add Package Dependency..." and paste the URL of this repo: `https://github.com/p2/OAuth2.git`. Pick a version and Xcode should do the rest. #### Carthage @@ -514,28 +525,6 @@ These three steps are needed to: 2. Link the framework into your app 3. Embed the framework in your app when distributing -#### CocoaPods - -CocoaPods was nice back in the days for Obj-C and static libraries, but is overkill in the modern days of Swift and iOS frameworks. -You can however still use OAuth2 with Cocoapods. - -Add a `Podfile` that contains at least the following information to the root of your app project, then do `pod install`. -If you're unfamiliar with CocoaPods, read [using CocoaPods](http://guides.cocoapods.org/using/using-cocoapods.html). - -```ruby -platform :ios, '8.0' # or platform :osx, '10.9' -use_frameworks! -target `YourApp` do - pod 'p2.OAuth2', '~> 4.2' -end -``` - -If you want the bleeding edge, use this command for CocoaPods instead โ€“ note the `submodules` flag: without it the library will not compile. - -```ruby -pod 'p2.OAuth2', :git => 'https://github.com/p2/OAuth2', :submodules => true -``` - License ------- diff --git a/Sources/Base/OAuth2Base.swift b/Sources/Base/OAuth2Base.swift index e24178e7..f1a8009f 100644 --- a/Sources/Base/OAuth2Base.swift +++ b/Sources/Base/OAuth2Base.swift @@ -171,6 +171,7 @@ open class OAuth2Base: OAuth2Securable { - secret_in_body (Bool, false by default, forces the flow to use the request body for the client secret) - parameters ([String: String], custom request parameters to be added during authorization) - token_assume_unexpired (Bool, true by default, whether to use access tokens that do not come with an "expires_in" parameter) + - use_pkce (Bool, false by default) - verbose (Bool, false by default, applies to client logging) */ diff --git a/Sources/Flows/OAuth2.swift b/Sources/Flows/OAuth2.swift index a004b81f..ab2886fc 100644 --- a/Sources/Flows/OAuth2.swift +++ b/Sources/Flows/OAuth2.swift @@ -73,6 +73,7 @@ open class OAuth2: OAuth2Base { - secret_in_body (Bool, false by default, forces the flow to use the request body for the client secret) - parameters ([String: String], custom request parameters to be added during authorization) - token_assume_unexpired (Bool, true by default, whether to use access tokens that do not come with an "expires_in" parameter) + - use_pkce (Bool, false by default) - verbose (bool, false by default, applies to client logging) */ From b0c71466176f3d7a22e5da20532c01d78a661f1d Mon Sep 17 00:00:00 2001 From: Ossus Date: Sun, 9 Feb 2020 07:55:59 +0100 Subject: [PATCH 16/16] Update project settings and CHANGELOG --- CHANGELOG.md | 18 ++++++++++++++++-- CONTRIBUTORS.md | 1 + OAuth2.xcodeproj/project.pbxproj | 5 +++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e4c5c10..5209b3a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,28 @@ Changelog Version numbering represents the Swift version, plus a running number representing updates, fixes and new features at the same time. You can also refer to commit logs to get details on what was implemented, fixed and improved. +### 5.2.0 + +- Separate setting for `refresh_uri`. + [fotiDim](https://github.com/fotiDim) + [#330](https://github.com/p2/OAuth2/pull/330) +- Add Mac Catalyst support. + [telipskiy](https://github.com/telipskiy) + [#328](https://github.com/p2/OAuth2/pull/328) +- Add PKCE support. + [larrybrunet](https://github.com/larrybrunet) + [#324](https://github.com/p2/OAuth2/pull/324) + ### 5.1.0 - Update Swift package configuration for use with XCode 11. ### 5.0.0 -- Swift 5.0 support. -- Add support for Authentication Session. +- Swift 5.0 support. + [drdavec](https://github.com/drdavec) + [#313](https://github.com/p2/OAuth2/pull/313) +- Add support for Authentication Session. [blork](https://github.com/blork) [#305](https://github.com/p2/OAuth2/pull/305) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b1b6e50d..23786eda 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -4,6 +4,7 @@ Contributors Contributors to the codebase, in reverse chronological order: - Foti Dim, @fotidim +- Denis, @telipskiy - Larry Brunet, @larrybrunet - Dave Carlson, @drdavec - Sam Oakley, @blork diff --git a/OAuth2.xcodeproj/project.pbxproj b/OAuth2.xcodeproj/project.pbxproj index a032a8d4..a26f30b7 100644 --- a/OAuth2.xcodeproj/project.pbxproj +++ b/OAuth2.xcodeproj/project.pbxproj @@ -584,6 +584,7 @@ }; EEDB8623193FAAE500C4EEA1 = { CreatedOnToolsVersion = 6.0; + LastSwiftMigration = 1130; }; EEE209461942772800736F1A = { CreatedOnToolsVersion = 6.0; @@ -1007,7 +1008,7 @@ PRODUCT_NAME = OAuth2; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1029,7 +1030,7 @@ PRODUCT_NAME = OAuth2; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; };