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

Use ephemeral browser for the OpenID popup #5

Merged
merged 6 commits into from
May 17, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @contentpass/cp-dev @contentpass/mobile
* @contentpass/cp-dev @contentpass/mobile
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]}
run: xcodebuild test -scheme ContentPassExample -workspace ContentPassExample/ContentPassExample.xcworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' | xcpretty && exit ${PIPESTATUS[0]}
12 changes: 6 additions & 6 deletions ContentPass.podspec
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
]
```

Expand Down Expand Up @@ -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
Expand All @@ -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.

Expand All @@ -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.

Expand All @@ -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
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions Sources/ContentPass/ContentPassState.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// swiftlint:disable cyclomatic_complexity

extension ContentPass {

/// The possible contentpass authentication states.
Expand All @@ -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:
Expand Down Expand Up @@ -56,5 +55,6 @@ extension ContentPass {
}
}
}
// swiftlint:enable cyclomatic_complexity
}
}
19 changes: 14 additions & 5 deletions Sources/ContentPass/OIDClientWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading