ASWebAuthenticationSession.start() does not display authentication UI on macOS (no error, no callback)

Hello Apple Developer Community,

I'm experiencing an issue on macOS where ASWebAuthenticationSession fails to display its authentication window.
The session is created successfully and start() returns true, but:

  • no UI is shown,
  • presentationAnchor(for:) is never invoked,
  • the completion handler is never called,
  • and no errors appear in Console.app or Xcode logs.

This happens both when using the session via a Flutter plugin and when calling ASWebAuthenticationSession directly from Swift.


Environment

  • macOS 14.6 (Sonoma)
  • Xcode latest stable
  • Target: macOS 10.15+
  • App type: sandboxed macOS app, hardened runtime enabled
  • The project also includes a Login Item (SMAppService) target
  • Redirect URI scheme: myapp-auth://callback

Problem Description

When I trigger authentication, the logs show:

[AuthPlugin] Starting ASWebAuthenticationSession...

After that:

  • no authentication sheet appears,
  • presentationAnchor(for:) is never called,
  • the completion handler is not invoked.

The main window is visible and active when the method is called.


Swift Implementation

public class AuthPlugin: NSObject, FlutterPlugin, ASWebAuthenticationPresentationContextProviding {

    private var webAuthSession: ASWebAuthenticationSession?
    private var resultHandler: FlutterResult?

    private func authenticate(url: URL, callbackScheme: String, result: @escaping FlutterResult) {
        NSLog("[AuthPlugin] authenticate() called")
        NSLog("[AuthPlugin] URL: %@", url.absoluteString)
        NSLog("[AuthPlugin] Callback scheme: %@", callbackScheme)

        resultHandler = result

        let session = ASWebAuthenticationSession(
            url: url,
            callbackURLScheme: callbackScheme
        ) { [weak self] callbackURL, error in
            NSLog("[AuthPlugin] completion handler invoked")

            if let error = error {
                NSLog("[AuthPlugin] error: %@", error.localizedDescription)
                return
            }

            guard let callbackURL = callbackURL else {
                NSLog("[AuthPlugin] missing callback URL")
                return
            }

            self?.resultHandler?(callbackURL.absoluteString)
        }

        session.presentationContextProvider = self
        session.prefersEphemeralWebBrowserSession = false

        self.webAuthSession = session

        NSLog("[AuthPlugin] Starting ASWebAuthenticationSession...")
        let started = session.start()
        NSLog("[AuthPlugin] start() returned: %@", started ? "true" : "false")
    }

    public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        NSLog("[AuthPlugin] presentationAnchor called")

        if let keyWindow = NSApplication.shared.windows.first(where: { $0.isKeyWindow }) {
            NSLog("[AuthPlugin] using key window")
            return keyWindow
        }

        if let firstWindow = NSApplication.shared.windows.first {
            NSLog("[AuthPlugin] using first window")
            return firstWindow
        }

        NSLog("[AuthPlugin] creating fallback window")
        let window = NSWindow(
            contentRect: .init(x: 0, y: 0, width: 400, height: 300),
            styleMask: [.titled, .closable],
            backing: .buffered,
            defer: false
        )
        window.center()
        window.makeKeyAndOrderFront(nil)
        return window
    }
}

Key Observations

  1. The session is created successfully.
  2. ASWebAuthenticationSession.start() returns true.
  3. presentationAnchor(for:) is never invoked.
  4. The completion handler is never triggered.
  5. No errors appear in system logs.
  6. The same code works correctly in a non-sandboxed macOS app.
  7. The main window is visible and the app is active when the session is started.

Questions

  1. What conditions must be met for macOS to call presentationAnchor(for:)?
  2. Does the window need to be key, main, visible, or foreground?
  3. Does ASWebAuthenticationSession.start() need to be called strictly on the main thread?
  4. Are additional entitlements, sandbox permissions, or Info.plist keys required for using ASWebAuthenticationSession in a sandboxed macOS application?
  5. Could the existence of a Login Item (SMAppService) affect the app’s ability to present the authentication sheet?
  6. Are there known restrictions for using custom URL schemes (myapp-auth://callback) on macOS?
  7. Is there any way to obtain more detailed diagnostics when start() silently fails to display UI?

Summary

In a sandboxed macOS application, ASWebAuthenticationSession starts without errors but never attempts to present its UI. Since presentationAnchor(for:) is never called, it seems macOS is blocking the presentation for some reason — possibly sandbox configuration, entitlements, or window state constraints.

Any guidance or suggestions would be greatly appreciated.

Thank you!

Hello Apple Developer Community,

Following my previous unanswered post, I created a very simple macOS test application to reproduce the issue in the smallest possible setup.

I’m having an issue with ASWebAuthenticationSession on macOS when trying to authenticate with Microsoft Azure AD.

I wrote a very simple macOS application whose only purpose is to sign in to Microsoft Azure and obtain an authorization code/token. The app is registered in Microsoft Entra as an iOS/macOS public client.

Target macOS: 15.7+

App registration in Azure:

Bundle Identifier: com.organization.testauthapp

Redirect URI: msauth.com.organization.testauthapp://auth

MSAL configuration:

let kClientID = "CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC"
let kRedirectUri = "msauth.com.organization.testauthapp://auth"
let kAuthority = "https://login.microsoftonline.com/TTTTTTTT-TTTT-TTTT-TTTT-TTTTTTTTTTTT"
let kGraphEndpoint = "https://graph.microsoft.com/"

(I intentionally anonymized tenantId as TTTT... and clientId as CCCC....)

My app contains only one button, which calls AuthenticationManager.startAuth(), and inside that method I start an ASWebAuthenticationSession.

The problem

When my authorization URL includes the redirect_uri parameter, the session fails immediately with:

ASAuthorizationController credential request failed with error:
Error Domain=com.apple.AuthenticationServices.AuthorizationError Code=1003 "(null)"

Example of the failing URL:

https://login.microsoftonline.com/TTTT/oauth2/v2.0/authorize
 ?response_type=code
 &scope=openid offline_access CCCC/.default
 &client_id=CCCC
 &redirect_uri=msauth.com.organization.testauthapp://auth
 &prompt=login

If I remove the redirect_uri parameter, the browser session appears normally and Azure login works — but of course the final redirect cannot return to my app, so it ends with:

Authentication error:
The operation couldn’t be completed.
(com.apple.AuthenticationServices.WebAuthenticationSession error 1.)

Important detail

If I copy the exact same URL (with redirect_uri included) directly into Safari, the login works, and Safari successfully redirects back to my app using the custom URL scheme.

So the redirect URI is valid, the app receives it correctly, and Azure is configured properly.

The issue occurs only when the same URL is passed to ASWebAuthenticationSession, which fails with:

AuthorizationError Code=1003 "(null)"

What am I missing?

My question is: Is there something wrong in my app configuration, entitlements, or URL handling that would cause ASWebAuthenticationSession to reject this URL with error code 1003? Why does the URL work in Safari but not inside the session?

My code:

import Foundation
import Combine
import AuthenticationServices
import AppKit

class AuthenticationManager: NSObject, ObservableObject {
    private var webAuthSession: ASWebAuthenticationSession?
    private let presentationContextProvider = PresentationContextProvider()
    
    func startAuth() {
        // Konfiguracja OAuth2 dla Microsoft Azure AD
        let tenantId = "TTTTTTTT-TTTT-TTTT-TTTT-TTTTTTTTTTTT"
        let clientId = "CCCCCCCC-CCCC-CCCC-CCCC-CCCCCCCCCCCC"
        let callbackURLScheme = "msauth.com.organization.testauthapp"
        
        // Budowanie URL autoryzacji
        var urlComponents = URLComponents()
        urlComponents.scheme = "https"
        urlComponents.host = "login.microsoftonline.com"
        urlComponents.path = "/\(tenantId)/oauth2/v2.0/authorize"
        
        let redirectURI = "\(callbackURLScheme)://auth"
        
        urlComponents.queryItems = [
            URLQueryItem(name: "response_type", value: "code"),
            URLQueryItem(name: "scope", value: "openid offline_access \(clientId)/.default"),
            URLQueryItem(name: "client_id", value: clientId),
            URLQueryItem(name: "redirect_uri", value: redirectURI),
            URLQueryItem(name: "prompt", value: "login")
        ]
        
        print("startAuth() called - Opening authorization URL: \(urlComponents.url?.absoluteString ?? "invalid")")
        
        guard let authorizeURL = urlComponents.url else {
            print("Invalid authorization URL")
            return
        }
        
        webAuthSession = ASWebAuthenticationSession(
            url: authorizeURL,
            callbackURLScheme: callbackURLScheme
        ) { callbackURL, error in
            if let error = error {
                print("Authentication error: \(error.localizedDescription)")
                if let authError = error as? ASAuthorizationError {
                    print("Error code: \(authError.code.rawValue)")
                }
                return
            }
            
            guard let callbackURL = callbackURL else {
                print("No callback URL")
                return
            }
            
            print("Authentication successful with callback: \(callbackURL)")
            
            if let components = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false),
               let queryItems = components.queryItems {
                for item in queryItems {
                    if item.name == "code", let authCode = item.value {
                        print("Authorization code received: \(authCode)")
                    } else if item.name == "error", let error = item.value {
                        print("OAuth error: \(error)")
                    }
                }
            }
        }
        
        webAuthSession?.presentationContextProvider = presentationContextProvider
        webAuthSession?.prefersEphemeralWebBrowserSession = false
        
        guard let session = webAuthSession else {
            print("ERROR: webAuthSession is nil")
            return
        }
        
        let success = session.start()
        if !success {
            print("ERROR: Failed to start ASWebAuthenticationSession")
        }
    }
}
...

Info.plist:

<key>ASWebAuthenticationSessionWebBrowserSupportEnabled</key>
	<true/>
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLName</key>
			<string>com.organization.testauthapp.url</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>msauth.com.organization.testauthapp</string>
			</array>
		</dict>
	</array>
	<key>LSMinimumSystemVersion</key>
	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>

TestAuthApp.entitlements:

	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.network.client</key>
	<true/>

I would really appreciate any suggestions or insights into what could be causing AuthorizationError 1003 in this scenario.

Thank you in advance for your help!

ASWebAuthenticationSession.start() does not display authentication UI on macOS (no error, no callback)
 
 
Q