diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8e21b10..b2be627 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @contentpass/cp-dev @contentpass/mobile \ No newline at end of file +* @contentpass/cp-dev @contentpass/mobile \ No newline at end of file diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 8c5b2c8..982b294 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -12,4 +12,4 @@ jobs: - name: Checkout uses: actions/checkout@v2 - name: Run tests - run: xcodebuild test -scheme ContentPassExample -workspace ContentPassExample/ContentPassExample.xcworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12' | xcpretty && exit ${PIPESTATUS[0]} \ No newline at end of file + run: xcodebuild test -scheme ContentPassExample -workspace ContentPassExample/ContentPassExample.xcworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' | xcpretty && exit ${PIPESTATUS[0]} \ No newline at end of file diff --git a/ContentPass.podspec b/ContentPass.podspec index febbd94..57b5666 100644 --- a/ContentPass.podspec +++ b/ContentPass.podspec @@ -1,18 +1,18 @@ Pod::Spec.new do |spec| spec.name = "ContentPass" - spec.version = "2.0.0" + spec.version = "2.1.0" spec.summary = "Handles all authentication and validation with contentpass servers for you." spec.homepage = "https://contentpass.de" spec.license = { :type => "MIT", :file => "LICENSE" } - spec.author = "Content Pass GmbH" - - spec.platform = :ios + spec.author = "Content Pass GmbH" + + spec.platform = :ios spec.ios.deployment_target = "10.0" - spec.source = { :git => "https://github.com/contentpass/contentpass-ios.git", :tag => "#{spec.version}" } + spec.source = { :git => "https://github.com/contentpass/contentpass-ios.git", :tag => "#{spec.version}" } spec.source_files = "Sources/**/*" - spec.dependency 'AppAuth', '1.4.0' + spec.dependency 'AppAuth', '1.7.5' spec.dependency 'Strongbox', '0.6.1' end diff --git a/ContentPassExample/ContentPassExample.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ContentPassExample/ContentPassExample.xcworkspace/xcshareddata/swiftpm/Package.resolved index 39a1daa..f4fe009 100644 --- a/ContentPassExample/ContentPassExample.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ContentPassExample/ContentPassExample.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/openid/AppAuth-iOS", "state": { "branch": null, - "revision": "01131d68346c8ae552961c768d583c715fbe1410", - "version": "1.4.0" + "revision": "c89ed571ae140f8eb1142735e6e23d7bb8c34cb2", + "version": "1.7.5" } }, { diff --git a/Package.resolved b/Package.resolved index 39a1daa..f4fe009 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/openid/AppAuth-iOS", "state": { "branch": null, - "revision": "01131d68346c8ae552961c768d583c715fbe1410", - "version": "1.4.0" + "revision": "c89ed571ae140f8eb1142735e6e23d7bb8c34cb2", + "version": "1.7.5" } }, { diff --git a/Package.swift b/Package.swift index 9f1a4a9..c07e7b6 100644 --- a/Package.swift +++ b/Package.swift @@ -13,7 +13,7 @@ let package = Package( targets: ["ContentPass"]) ], dependencies: [ - .package(name: "AppAuth", url: "https://github.com/openid/AppAuth-iOS", .exact("1.4.0")), + .package(name: "AppAuth", url: "https://github.com/openid/AppAuth-iOS", .exact("1.7.5")), .package(name: "Strongbox", url: "https://github.com/granoff/Strongbox", .exact("0.6.1")) ], targets: [ diff --git a/README.md b/README.md index e390557..dd3ab40 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ or * Add the following `dependency` to your `Package.swift`: ```swift dependencies: [ - .package(url: "https://github.com/contentpass/contentpass-ios", .upToNextMajor(from: "2.0.0")) + .package(url: "https://github.com/contentpass/contentpass-ios", .upToNextMajor(from: "2.1.0")) ] ``` @@ -63,7 +63,7 @@ You should instantiate and hold one instance of the `ContentPass` class in one o ```swift class SceneDelegate: UIResponder, UIWindowSceneDelegate { let contentPass = ContentPass() - + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // ... contentPass.delegate = self @@ -89,7 +89,7 @@ extension SceneDelegate: ContentPassDelegate { ### Authentication -We use [AppAuth](https://github.com/openid/AppAuth-iOS) for the OAuth 2.0 process. AppAuth uses Apple's `ASWebAuthenticationSession` or if below iOS 12 `SFAuthenticationSession`. +We use [AppAuth](https://github.com/openid/AppAuth-iOS) for the OAuth 2.0 process. AppAuth uses Apple's `ASWebAuthenticationSession` or if below iOS 12 `SFAuthenticationSession`. That means, that the user will be presented with a modal `SFSafariViewController` like view in which they can authenticate themselves with our servers. We then use the OAuth result to validate whether the user has any active contentpass subscriptions that are applicable. @@ -99,24 +99,24 @@ To authenticate and authorize the user, simply call `authenticate` on the `Conte contentPass.authenticate(presentingViewController: viewController) { result in switch result { case .success: - // this only means that authentication was successful, + // this only means that authentication was successful, // it doesn't tell you anything about the subscription status break case .failure(let error): // handle errors accordingly - refer to "Error handling" in this document - } + } } ``` If the authentication was a success, we will poll our servers for subscription plans in the background. -The `delegate` will be called with the final authentication and subscription state. +The `delegate` will be called with the final authentication and subscription state. **Be aware that a successfully authenticated user may have no active subscription plans** and act accordingly! ### A few words on persistence -* We store tokens that anonymously identify the logged in user's session to our servers in the device's keychain. +* We store tokens that anonymously identify the logged in user's session to our servers in the device's keychain. * We refresh these tokens automatically in the background before they're invalidated. * The subscription information gets validated as well on every token refresh. @@ -129,7 +129,7 @@ contentPass.logout() ``` The user will of course have to log in again afterwards. -You can also call `authenticate` again and all previous user information will get overwritten. +You can also call `authenticate` again and all previous user information will get overwritten. We only store *one* user session at any one time. ### Error handling @@ -138,11 +138,11 @@ An error can occur during the `authenticate` function's lifetime or you can get We have our own `ContentPassError` enum for the following cases: -* The user has canceled or dismissed the OAuth flow: `userCanceledAuthentication` +* The user has canceled or dismissed the OAuth flow: `userCanceledAuthentication` You should handle this accordingly. * Something went wrong while communicating with the backend: `subscriptionDataCorrupted`, `corruptedResponseFromWeb`, `badHTTPStatusCode` or `oidAuthenticatedButMissingIdToken` If you're sure that you have configured and set up everything correctly and one of these problems persists, contact us via GitHub issues or by mail. -* Something very unexpected happened: `unexpectedState` +* Something very unexpected happened: `unexpectedState` This should never occur and is basically our `throws` replacement since we're async. If you encounter one of these, feel very free to open a GitHub issue. We also bubble up underlying errors that may occur because of connectivity problems or other issues regarding the OAuth flow. diff --git a/Sources/ContentPass/ContentPassState.swift b/Sources/ContentPass/ContentPassState.swift index 97bd08f..166c562 100644 --- a/Sources/ContentPass/ContentPassState.swift +++ b/Sources/ContentPass/ContentPassState.swift @@ -1,5 +1,3 @@ -// swiftlint:disable cyclomatic_complexity - extension ContentPass { /// The possible contentpass authentication states. @@ -24,6 +22,7 @@ extension ContentPass { /// This might be a `ContentPassError` but it also can be something underlying. In the latter case, cast it to `NSError` and act according to `domain` and `code`. case error(Error) + // swiftlint:disable cyclomatic_complexity public static func == (lhs: ContentPass.State, rhs: ContentPass.State) -> Bool { switch lhs { case .error: @@ -56,5 +55,6 @@ extension ContentPass { } } } + // swiftlint:enable cyclomatic_complexity } } diff --git a/Sources/ContentPass/OIDClientWrapper.swift b/Sources/ContentPass/OIDClientWrapper.swift index cc31e86..44163e1 100644 --- a/Sources/ContentPass/OIDClientWrapper.swift +++ b/Sources/ContentPass/OIDClientWrapper.swift @@ -8,11 +8,20 @@ class OIDClientWrapper: OIDClientWrapping { } func doAuthorization(byPresenting: OIDAuthorizationRequest, presenting: UIViewController, completionHandler: @escaping (OIDAuthStateWrapping?, Error?) -> Void) { - currentAuthorizationFlow = OIDAuthState.authState( - byPresenting: byPresenting, - presenting: presenting, - callback: completionHandler - ) + if #available(iOS 13, *) { + currentAuthorizationFlow = OIDAuthState.authState( + byPresenting: byPresenting, + presenting: presenting, + prefersEphemeralSession: true, + callback: completionHandler + ) + } else { + currentAuthorizationFlow = OIDAuthState.authState( + byPresenting: byPresenting, + presenting: presenting, + callback: completionHandler + ) + } } func fireValidationRequest(_ validationRequest: URLRequest, completionHandler: @escaping (Data?, Error?) -> Void) {