Skip to content

Commit

Permalink
feat: added support for webview as a provider for webauth (#875)
Browse files Browse the repository at this point in the history
Co-authored-by: Rita Zerrizuela <[email protected]>
  • Loading branch information
desusai7 and Widcket authored Oct 14, 2024
1 parent 0d9c3f3 commit 2edbb4f
Show file tree
Hide file tree
Showing 15 changed files with 628 additions and 111 deletions.
4 changes: 3 additions & 1 deletion Auth0.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ web_auth_files = [
'Auth0/WebAuth.swift',
'Auth0/WebAuthentication.swift',
'Auth0/WebAuthError.swift',
'Auth0/WebAuthUserAgent.swift'
'Auth0/WebAuthUserAgent.swift',
'Auth0/UIWindow+TopViewController.swift',
'Auth0/WebViewProvider.swift'
]

ios_files = ['Auth0/MobileWebAuth.swift']
Expand Down
24 changes: 24 additions & 0 deletions Auth0.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,12 @@
A7DDDF6D2BC9A81E0077B067 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A7DDDF6B2BC9A81E0077B067 /* PrivacyInfo.xcprivacy */; };
A7DDDF6E2BC9A81E0077B067 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A7DDDF6B2BC9A81E0077B067 /* PrivacyInfo.xcprivacy */; };
A7DDDF702BC9A93F0077B067 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A7DDDF6B2BC9A81E0077B067 /* PrivacyInfo.xcprivacy */; };
C107B51D2C9AC4D8006B6BEA /* WebViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C107B51B2C9AC4D3006B6BEA /* WebViewProvider.swift */; };
C107B5222CA27F7C006B6BEA /* WebViewProviderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C107B5202CA27F76006B6BEA /* WebViewProviderSpec.swift */; };
C12BFE432C352DD400D1CC00 /* NetworkStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = C177D76F2C2BDFE40094C657 /* NetworkStub.swift */; };
C12BFE442C352DD700D1CC00 /* StubURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C177D7742C2BE00D0094C657 /* StubURLProtocol.swift */; };
C160EE352CABD0E5005ACE8E /* UIWindow+TopViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C160EE302CABD0DA005ACE8E /* UIWindow+TopViewController.swift */; };
C160EE382CABD35A005ACE8E /* UIWindow+TopViewControllerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = C160EE372CABD358005ACE8E /* UIWindow+TopViewControllerSpec.swift */; };
C177D6C32C2ADDEB0094C657 /* Auth0.plist in Resources */ = {isa = PBXBuildFile; fileRef = C177D6C22C2ADDEB0094C657 /* Auth0.plist */; };
C177D6C72C2ADEB60094C657 /* CwlPreconditionTesting in Frameworks */ = {isa = PBXBuildFile; productRef = C177D6C62C2ADEB60094C657 /* CwlPreconditionTesting */; };
C177D6D42C2B0DCB0094C657 /* CwlPreconditionTesting in Frameworks */ = {isa = PBXBuildFile; productRef = C177D6D32C2B0DCB0094C657 /* CwlPreconditionTesting */; };
Expand Down Expand Up @@ -746,6 +750,10 @@
5FF465BB1CE2AC4500F7ED8C /* Management.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Management.swift; path = Auth0/Management.swift; sourceTree = SOURCE_ROOT; };
970BC36A25C27095007A7745 /* Challenge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Challenge.swift; sourceTree = "<group>"; };
A7DDDF6B2BC9A81E0077B067 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
C107B51B2C9AC4D3006B6BEA /* WebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewProvider.swift; sourceTree = "<group>"; };
C107B5202CA27F76006B6BEA /* WebViewProviderSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewProviderSpec.swift; sourceTree = "<group>"; };
C160EE302CABD0DA005ACE8E /* UIWindow+TopViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIWindow+TopViewController.swift"; sourceTree = "<group>"; };
C160EE372CABD358005ACE8E /* UIWindow+TopViewControllerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIWindow+TopViewControllerSpec.swift"; sourceTree = "<group>"; };
C177D6C22C2ADDEB0094C657 /* Auth0.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Auth0.plist; sourceTree = "<group>"; };
C177D76F2C2BDFE40094C657 /* NetworkStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkStub.swift; sourceTree = "<group>"; };
C177D7742C2BE00D0094C657 /* StubURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubURLProtocol.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -928,6 +936,7 @@
5C0AF09828330CA000162044 /* Providers */ = {
isa = PBXGroup;
children = (
C107B51B2C9AC4D3006B6BEA /* WebViewProvider.swift */,
5B16D88C1F7141A0009476A5 /* ASProvider.swift */,
5C0AF09928330CBA00162044 /* SafariProvider.swift */,
);
Expand Down Expand Up @@ -977,6 +986,7 @@
5CF539222836DC360073F623 /* Providers */ = {
isa = PBXGroup;
children = (
C107B5202CA27F76006B6BEA /* WebViewProviderSpec.swift */,
5CF5392A283835460073F623 /* ASProviderSpec.swift */,
5CF539232836DCC10073F623 /* SafariProviderSpec.swift */,
);
Expand Down Expand Up @@ -1051,6 +1061,7 @@
5F06DD921CC451430011842B /* Auth0Tests */ = {
isa = PBXGroup;
children = (
C160EE362CABD352005ACE8E /* Extensions */,
C177D76E2C2BDF9D0094C657 /* StubNetworking */,
5F28B4651D8300BB0000EB23 /* Logger */,
5FE686A81D1894990075874C /* Telemetry */,
Expand Down Expand Up @@ -1203,6 +1214,7 @@
5FCAB16E1D08FFE900331C84 /* Extensions */ = {
isa = PBXGroup;
children = (
C160EE302CABD0DA005ACE8E /* UIWindow+TopViewController.swift */,
5C4F551923C8FB8E00C89615 /* Array+Encode.swift */,
5C4F551823C8FB8E00C89615 /* String+URLSafe.swift */,
5FCAB1721D09009600331C84 /* NSData+URLSafe.swift */,
Expand Down Expand Up @@ -1287,6 +1299,14 @@
name = Management;
sourceTree = "<group>";
};
C160EE362CABD352005ACE8E /* Extensions */ = {
isa = PBXGroup;
children = (
C160EE372CABD358005ACE8E /* UIWindow+TopViewControllerSpec.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
C177D76E2C2BDF9D0094C657 /* StubNetworking */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -2046,8 +2066,10 @@
5F3965C21CF67CF000CDE7C0 /* WebAuth.swift in Sources */,
5FCAB1761D0900CF00331C84 /* TransactionStore.swift in Sources */,
5FDE87471D8A422300EA27DC /* Telemetry.swift in Sources */,
C107B51D2C9AC4D8006B6BEA /* WebViewProvider.swift in Sources */,
5FAE9C911D8878D400A871CE /* Auth0WebAuth.swift in Sources */,
5B5E93F91EC45C22002A37F9 /* CredentialsManagerError.swift in Sources */,
C160EE352CABD0E5005ACE8E /* UIWindow+TopViewController.swift in Sources */,
5CA541CD2B1A81A700E4284D /* Documentation.docc in Sources */,
5C41F6AA244DCAFB00252548 /* ClearSessionTransaction.swift in Sources */,
5FDE875D1D8A424700EA27DC /* AuthenticationError.swift in Sources */,
Expand Down Expand Up @@ -2151,7 +2173,9 @@
5FADB6091CED500900D4BB50 /* ManagementSpec.swift in Sources */,
5FCAB16D1D07AC3500331C84 /* WebAuthSpec.swift in Sources */,
5F28B4671D8300D50000EB23 /* LoggerSpec.swift in Sources */,
C107B5222CA27F7C006B6BEA /* WebViewProviderSpec.swift in Sources */,
5FBBF0431CCA90300024D2AF /* AuthenticationSpec.swift in Sources */,
C160EE382CABD35A005ACE8E /* UIWindow+TopViewControllerSpec.swift in Sources */,
5B2860D61EEF210A00C75D54 /* UserInfoSpec.swift in Sources */,
5C809D9A275FA3EF00F15A67 /* ManagementErrorSpec.swift in Sources */,
5FCAB16B1D07AC3500331C84 /* OAuth2GrantSpec.swift in Sources */,
Expand Down
6 changes: 5 additions & 1 deletion Auth0.xcodeproj/xcshareddata/xcschemes/Auth0.iOS.xcscheme
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
Expand Down Expand Up @@ -47,6 +47,10 @@
BlueprintName = "Auth0Tests.iOS"
ReferencedContainer = "container:Auth0.xcodeproj">
</BuildableReference>
<LocationScenarioReference
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
referenceType = "1">
</LocationScenarioReference>
</TestableReference>
</Testables>
</TestAction>
Expand Down
4 changes: 2 additions & 2 deletions Auth0/Auth0WebAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ final class Auth0WebAuth: WebAuth {
state: state,
organization: organization,
invitation: invitation)
let provider = self.provider ?? WebAuthentication.asProvider(redirectURL: redirectURL,
ephemeralSession: ephemeralSession)

let provider = self.provider ?? WebAuthentication.asProvider(redirectURL: redirectURL, ephemeralSession: ephemeralSession)
let userAgent = provider(authorizeURL) { [storage, onCloseCallback] result in
storage.clear()

Expand Down
13 changes: 13 additions & 0 deletions Auth0/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,16 @@ func includeRequiredScope(in scope: String?) -> String? {
guard let scope = scope, !scope.split(separator: " ").map(String.init).contains("openid") else { return scope }
return "openid \(scope)"
}

func extractRedirectURL(from url: URL) -> URL? {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return nil
}

if let redirectURIString = components.queryItems?.first(where: { $0.name == "redirect_uri" || $0.name == "returnTo" })?.value,
let redirectURI = URL(string: redirectURIString) {
return redirectURI
}

return nil
}
36 changes: 1 addition & 35 deletions Auth0/SafariProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,39 +45,6 @@ public extension WebAuthentication {

}

extension SFSafariViewController {

var topViewController: UIViewController? {
guard let root = UIApplication.shared()?.windows.last(where: \.isKeyWindow)?.rootViewController else {
return nil
}
return self.findTopViewController(from: root)
}

func present() {
self.topViewController?.present(self, animated: true, completion: nil)
}

private func findTopViewController(from root: UIViewController) -> UIViewController? {
if let presented = root.presentedViewController { return self.findTopViewController(from: presented) }

switch root {
case let split as UISplitViewController:
guard let last = split.viewControllers.last else { return split }
return self.findTopViewController(from: last)
case let navigation as UINavigationController:
guard let top = navigation.topViewController else { return navigation }
return self.findTopViewController(from: top)
case let tab as UITabBarController:
guard let selected = tab.selectedViewController else { return tab }
return self.findTopViewController(from: selected)
default:
return root
}
}

}

class SafariUserAgent: NSObject, WebAuthUserAgent {

let controller: SFSafariViewController
Expand All @@ -92,7 +59,7 @@ class SafariUserAgent: NSObject, WebAuthUserAgent {
}

func start() {
self.controller.present()
UIWindow.topViewController?.present(controller, animated: true, completion: nil)
}

func finish(with result: WebAuthResult<Void>) {
Expand Down Expand Up @@ -125,7 +92,6 @@ extension SafariUserAgent: SFSafariViewControllerDelegate {
// If you are developing a custom Web Auth provider, call WebAuthentication.cancel() instead
// TransactionStore is internal
TransactionStore.shared.cancel()

}

}
Expand Down
38 changes: 38 additions & 0 deletions Auth0/UIWindow+TopViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// UIWindow+TopViewController.swift
// Auth0
//
// Created by Desu Sai Venkat on 01/10/24.
// Copyright © 2024 Auth0. All rights reserved.
//

#if os(iOS)
import UIKit

extension UIWindow {
static var topViewController: UIViewController? {
guard let root = UIApplication.shared()?.windows.last(where: \.isKeyWindow)?.rootViewController else {
return nil
}
return findTopViewController(from: root)
}

private static func findTopViewController(from root: UIViewController) -> UIViewController? {
if let presented = root.presentedViewController { return self.findTopViewController(from: presented) }

switch root {
case let split as UISplitViewController:
guard let last = split.viewControllers.last else { return split }
return self.findTopViewController(from: last)
case let navigation as UINavigationController:
guard let top = navigation.topViewController else { return navigation }
return self.findTopViewController(from: top)
case let tab as UITabBarController:
guard let selected = tab.selectedViewController else { return tab }
return self.findTopViewController(from: selected)
default:
return root
}
}
}
#endif
1 change: 0 additions & 1 deletion Auth0/WebAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,5 @@ public extension WebAuth {
return try await self.clearSession(federated: federated)
}
#endif

}
#endif
2 changes: 2 additions & 0 deletions Auth0/WebAuthError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Foundation
public struct WebAuthError: Auth0Error {

enum Code: Equatable {
case webViewFailure(String)
case noBundleIdentifier
case transactionActiveAlready
case invalidInvitationURL(String)
Expand Down Expand Up @@ -82,6 +83,7 @@ extension WebAuthError {

var message: String {
switch self.code {
case .webViewFailure(let webViewFailureMessage): return webViewFailureMessage
case .noBundleIdentifier: return "Unable to retrieve the bundle identifier from Bundle.main.bundleIdentifier,"
+ " or it could not be used to build a valid URL."
case .transactionActiveAlready: return "Failed to start this transaction, as there is an active transaction at the"
Expand Down
Loading

0 comments on commit 2edbb4f

Please sign in to comment.