Skip to content

feat: Add presentation window support#1085

Open
NandanPrabhu wants to merge 4 commits intodevelop/v3.0from
multiwindow-handling
Open

feat: Add presentation window support#1085
NandanPrabhu wants to merge 4 commits intodevelop/v3.0from
multiwindow-handling

Conversation

@NandanPrabhu
Copy link
Contributor

  • All new/changed/fixed functionality is covered by tests (or N/A)
  • I have added documentation for all new/changed functionality (or N/A)

📋 Changes

  • Added multi-window presentation support by plumbing optional UIWindow/NSWindow through ContentView, ContentViewModel, Auth0WebAuth, and all built-in web auth providers, plus enabling multi-scene mode in Info.plist.
  • Extended WebAuth API surface with presentationWindow(...), updated ASUserAgent, Safari/WKWebView providers, and ASWebAuthentication scaffolding to honor custom anchors on iOS, visionOS, and macOS.
  • Updated Auth0 defaults to request offline_access, set credentials manager minTTL to $60$, and give database signup a default "Username-Password-Authentication" connection, with aligned documentation updates and new Quick/Nimble specs covering default behaviors and presentation windows.

🎯 Testing

  • Ran unit suite including new WebAuthPresentationWindowSpec, extended AuthenticationSpec, and additional CredentialsManagerSpec cases to verify defaults and window handling.

@NandanPrabhu NandanPrabhu requested a review from a team as a code owner February 12, 2026 14:38
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for presenting Web Auth flows from a caller-specified window (UIWindow/NSWindow) to better support multi-window / multi-scene apps across iOS, visionOS, and macOS, and includes test coverage plus demo app wiring.

Changes:

  • Extended WebAuth/Auth0WebAuth + ASWebAuthenticationSession provider plumbing to accept an optional presentation window/anchor.
  • Updated Safari/WKWebView user agents to present from the top view controller of a provided UIWindow (fallback to key window).
  • Added Quick/Nimble specs validating presentationWindow(...) behavior and provider propagation; enabled multi-scene support in the demo app’s Info.plist.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
Auth0Tests/WebAuthPresentationWindowSpec.swift Adds specs for presentationWindow(...) and provider window propagation.
Auth0/WebAuth.swift Extends WebAuth protocol with presentationWindow(_:) API (UIWindow/NSWindow).
Auth0/Auth0WebAuth.swift Stores presentation window and passes it to the default AS provider for login/logout.
Auth0/ASProvider.swift Extends AS provider factory + ASUserAgent to carry a presentation window and use it as anchor.
Auth0/MobileWebAuth.swift Uses provided UIWindow as the ASPresentationAnchor on iOS/visionOS.
Auth0/DesktopWebAuth.swift Uses provided NSWindow as the ASPresentationAnchor on macOS.
Auth0/SafariProvider.swift Allows Safari user agent to present from a provided UIWindow.
Auth0/WebViewProvider.swift Allows WKWebView user agent to present from a provided UIWindow.
Auth0/UIWindow+TopViewController.swift Exposes a helper to find the top VC from a specific root VC.
App/Info.plist Enables multiple scenes in the demo app.
App/ContentView.swift Captures current UIWindow (iOS/visionOS) and passes it to login/logout calls.
App/ContentViewModel.swift Adds window-aware login/logout variants and threads the window into WebAuth.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +14 to +20
#if os(iOS)
import UIKit
typealias PlatformWindow = UIWindow
#elseif os(macOS)
import AppKit
typealias PlatformWindow = NSWindow
#endif
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UIWindow is referenced under #if os(iOS) || os(visionOS) but UIKit is only imported for os(iOS). This will fail to compile the test target on visionOS; update the conditional import to include visionOS (or avoid direct UIKit types in the visionOS blocks).

Copilot uses AI. Check for mistakes.
#if os(iOS) || os(visionOS)
import UIKit
#endif

Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file conditionally defines APIs using NSWindow for os(macOS), but AppKit is never imported. A macOS build of the demo app will fail to compile; add an #if os(macOS) import for AppKit (and keep UIKit under iOS/visionOS).

Suggested change
#if os(macOS)
import AppKit
#endif

Copilot uses AI. Check for mistakes.
Comment on lines 29 to 34
// Use specific window for multi-window iPad support
#if os(iOS) || os(visionOS)
if let window = window {
webAuth = webAuth.presentationWindow(window)
}
#endif
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the os(macOS) implementation, the presentationWindow parameter is NSWindow? but the code that applies it is wrapped in #if os(iOS) || os(visionOS), so it never runs on macOS and the parameter becomes unused. Apply the window under the macOS branch (and update the comment about “UIWindow / iPad” accordingly).

Copilot uses AI. Check for mistakes.
Comment on lines 66 to 71
// Use specific window for multi-window iPad support
#if os(iOS) || os(visionOS)
if let window = window {
webAuth = webAuth.presentationWindow(window)
}
#endif
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as webLogin: in the os(macOS) logout implementation, the NSWindow? parameter is only used inside an #if os(iOS) || os(visionOS) block, so it will never be applied on macOS. Use the macOS presentationWindow(_:) API here (and update the doc comment to refer to NSWindow).

Copilot uses AI. Check for mistakes.
Comment on lines 12 to 16
#if os(macOS)
@State private var currentWindow: NSWindow?
#else
@State private var currentWindow: UIWindow?
#endif
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NSWindow is referenced under #if os(macOS) but AppKit is not imported anywhere in this file. If the demo app is built for macOS, this will not compile; add an #if os(macOS) import for AppKit.

Copilot uses AI. Check for mistakes.
Comment on lines 21 to 26
Button {
Task {
#if !os(macOS)
await viewModel.webLogin(presentationWindow: currentWindow)
#endif
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both button actions are compiled out on macOS (#if !os(macOS)), so the demo app UI does nothing on macOS even though ContentViewModel provides macOS-specific webLogin/logout overloads. Either call the macOS overloads under #if os(macOS) or remove the macOS branches from the view/model to avoid dead code paths.

Copilot uses AI. Check for mistakes.
Comment on lines 59 to 64
.onAppear {
// Capture the current window for multi-window iPad support
#if os(iOS) || os(visionOS)
currentWindow = getCurrentWindow()
#endif
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On macOS, currentWindow is never populated (the .onAppear window capture is iOS/visionOS-only). If macOS support is intended, populate currentWindow from NSApp.keyWindow/NSApplication.shared.keyWindow (or remove currentWindow on macOS if it’s not needed).

Copilot uses AI. Check for mistakes.
@sanchitmehtagit
Copy link
Contributor

sanchitmehtagit commented Feb 13, 2026

I have added mandate PR CI checks and 1 mandatory approval for develop/v3 branch too.
cc: @NandanPrabhu

import Auth0

#if os(iOS) || os(visionOS)
import UIKit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have optional UIKit import here?

Copy link
Contributor

@sanchitmehtagit sanchitmehtagit Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to import AppKit for NSWindow in MacOS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

private(set) var provider: WebAuthProvider?
private(set) var onCloseCallback: (() -> Void)?
#if os(macOS)
private(set) var presentationWindow: NSWindow?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should store the weak references for these since the window is owned by the application, not by the Auth0WebAuth instance.
private(set) weak var presentationWindow: NSWindow?
private(set) var presentationWindow: UIWindow?

let callback: WebAuthProviderCallback

init(authorizeURL: URL, redirectURL: URL, viewController: UIViewController = UIViewController(), modalPresentationStyle: UIModalPresentationStyle = .fullScreen, callback: @escaping WebAuthProviderCallback) {
var presentationWindow: UIWindow?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be using weak reference in SDK

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be private(set)

@sanchitmehtagit sanchitmehtagit added the review:medium Medium review label Feb 13, 2026
@sanchitmehtagit sanchitmehtagit changed the title Add presentation window support feat: Add presentation window support Feb 13, 2026
}

private static func findTopViewController(from root: UIViewController) -> UIViewController? {
static func findTopViewController(from root: UIViewController) -> UIViewController? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we make this private back .


/// Logout and clear stored credentials
func logout() async {
/// - Parameter window: The UIWindow to present the authentication browser. Used for multi-window iPad apps.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do update this comment . This API is being used for MacOS

#if os(macOS)
/// Web Authentication using Universal Login (Recommended)
func webLogin() async {
/// - Parameter window: The UIWindow to present the authentication browser. Used for multi-window iPad apps.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this paramter is being used for MacOS

var webAuth = Auth0.webAuth()

// Use specific window for multi-window iPad support
#if os(iOS) || os(visionOS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    #if os(iOS) || os(visionOS)  /
    if let window = window {
        webAuth = webAuth.presentationWindow(window)
    }
    #endif

This will never execute as the current logout function will be executed for MacOS



// Use specific window for multi-window iPad support
#if os(iOS) || os(visionOS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    #if os(iOS) || os(visionOS)  /
    if let window = window {
        webAuth = webAuth.presentationWindow(window)
    }
    #endif

This will never execute as the current login function will be executed for MacOS

/// - Parameter window: The UIWindow to present the authentication browser. Used for multi-window iPad apps.
func webLogin(presentationWindow window: NSWindow? = nil) async {
isLoading = true
errorMessage = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets update the documentation . We should have Multi Window Support in examples.md file

#endif

#if os(macOS)
init(session: ASWebAuthenticationSession,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not add have 2 initialiser implementation

Please user the the below design. It improved readability and eliminates the code duplicity

#if os(macOS)
typealias PlatformWindow = NSWindow
#else
typealias PlatformWindow = UIWindow
#endif

class ASUserAgent: NSObject, WebAuthUserAgent {
    var presentationWindow: PlatformWindow?

    init(session: ASWebAuthenticationSession,
         callback: @escaping WebAuthProviderCallback,
         presentationWindow: PlatformWindow? = nil) {
        // ... single implementation
    }
}

}

#if os(macOS)
@available(macOS 10.15, *)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#if os(macOS)
typealias PlatformWindow = NSWindow
#else
typealias PlatformWindow = UIWindow
#endif

final class Auth0WebAuth: WebAuth {
    // ... properties ...
    
    private(set) weak var presentationWindow: PlatformWindow?
    
    // ... later in the file ...
    
    @available(iOS 13.0, macOS 10.15, visionOS 1.0, *)
    func presentationWindow(_ window: PlatformWindow) -> Self {
        self.presentationWindow = window
        return self
    }
}

Preferred design we should use same approach everywhere

  • Eliminates duplicate property declarations
  • Single method definition instead of two platform-specific versions
  • Easier to maintain - changes only need to be made once

func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return UIApplication.shared()?.windows.last(where: \.isKeyWindow) ?? ASPresentationAnchor()
// Use custom window if provided
if let window = presentationWindow {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we use guard here

#if os(visionOS)
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
// Use custom window if provided
if let window = presentationWindow {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

guard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:medium Medium review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants