I'm attempting to use WebAuthenticationSession from Authentication Services (https://developer.apple.com/documentation/authenticationservices/webauthenticationsession) in a SwiftUI app on macOS.
According to the docs, it is supported since macOS 13.3 and I'm testing on 26.
I'm deploying the same code to iOS as well, and it works there in a simulator, but I sometimes have to tap the button that triggers the authenticate(…) call more than once.
I've attached a simplified and redacted version of the code below. It works on iOS, but on macOS the authenticate call never returns.
There seems to be very little documentation and discussion about this API and I'm running out of ideas for getting this to work. Is this just a bug that Apple hasn't noticed?
import SwiftUI
import AuthenticationServices
import Combine
struct PreAuthView: View {
@Binding var appState: AppState
@Binding var credentials: Credentials?
@Environment(\.webAuthenticationSession) private var webAuthenticationSession
@State private var plist: String = ""
func authenticate() async {
guard var authUrl = URL(string: "REDACTED") else { return }
guard var tokenUrl = URL(string: "REDACTED") else { return }
let redirectUri = "REDACTED"
let clientId = "REDACTED"
let verifier = "REDACTED"
let challenge = "REDACTED"
authUrl.append(queryItems: [
.init(name: "response_type", value: "code"),
.init(name: "client_id", value: clientId),
.init(name: "redirect_uri", value: redirectUri),
.init(name: "state", value: ""),
.init(name: "code_challenge", value: challenge),
.init(name: "code_challenge_method", value: "S256"),
])
let scheme = "wonderswitcher"
do {
print("Authenticating")
let redirectUrl = try await webAuthenticationSession.authenticate(
using: authUrl,
callback: .https(host: "REDACTED", path: "REDACTED"),
additionalHeaderFields: [:],
)
print("Authenticated?")
print(redirectUrl)
let queryItems = URLComponents(string: redirectUrl.absoluteString)?.queryItems ?? []
let code = queryItems.filter({$0.name == "code"}).first?.value
let session = URLSession(configuration: .ephemeral)
tokenUrl.append(queryItems: [
.init(name: "grant_type", value: "authorization_code"),
.init(name: "code", value: code),
.init(name: "redirect_uri", value: redirectUri),
.init(name: "code_verifier", value: verifier),
.init(name: "client_id", value: clientId),
])
var request = URLRequest(url: tokenUrl)
request.httpMethod = "POST"
let response = try await session.data(for: request)
print(response)
} catch {
return
}
}
var body: some View {
NavigationStack {
VStack(alignment: .leading, spacing: 12) {
Text("This is the pre-auth view.")
HStack {
Button("Log in") {
Task {
await authenticate()
}
}
Spacer()
}
Spacer(minLength: 0)
}
.padding()
.navigationTitle("Pre-Auth")
}
}
}
#Preview {
PreAuthView(appState: .constant(.preAuth), credentials: .constant(nil))
}