diff --git a/Package.swift b/Package.swift index aa5694a..f3fa273 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.10 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -24,7 +24,8 @@ let package = Package( .target( name: "SnapAuth", swiftSettings: [ - .define("HARDWARE_KEY_SUPPORT", .when(platforms: [.iOS, .macOS])) + .define("HARDWARE_KEY_SUPPORT", .when(platforms: [.iOS, .macOS])), + .swiftLanguageVersion(.v6), ]), .testTarget( name: "SnapAuthTests", diff --git a/Sources/SnapAuth/Errors.swift b/Sources/SnapAuth/Errors.swift index 3742060..72d4380 100644 --- a/Sources/SnapAuth/Errors.swift +++ b/Sources/SnapAuth/Errors.swift @@ -84,16 +84,13 @@ extension ASAuthorizationError.Code { case .invalidResponse: return .invalidResponse case .notHandled: return .notHandled case .notInteractive: return .notInteractive - @unknown default: - /* This is (AFAICT) correct, but doesn't seem to work on the Github - Actions runner version. + default: // This case only exists on new OS platforms if #available(iOS 18, visionOS 2, macOS 15, tvOS 18, *) { if case .matchedExcludedCredential = self { return .matchedExcludedCredential } } - */ return .unknown } } diff --git a/Sources/SnapAuth/PresentationAnchor.swift b/Sources/SnapAuth/PresentationAnchor.swift index 6ddc55d..52fb7bd 100644 --- a/Sources/SnapAuth/PresentationAnchor.swift +++ b/Sources/SnapAuth/PresentationAnchor.swift @@ -1,14 +1,20 @@ import AuthenticationServices +extension ASPresentationAnchor { + /// A platform-specific anchor, intended to be used by ASAuthorizationController + static var `default`: ASPresentationAnchor { #if os(macOS) -// FIXME: Figure out better fallback mechanisms here. -// This will cause a new window to open _and remain open_ -fileprivate let defaultPresentationAnchor: ASPresentationAnchor = NSApplication.shared.mainWindow ?? ASPresentationAnchor() + // FIXME: Figure out better fallback mechanisms here. + // This will cause a new window to open _and remain open_ + return NSApplication.shared.mainWindow ?? ASPresentationAnchor() #else -fileprivate let defaultPresentationAnchor: ASPresentationAnchor = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController?.view.window ?? ASPresentationAnchor() + return (UIApplication.shared.connectedScenes.first as? UIWindowScene)? + .windows + .first? + .rootViewController? + .view + .window + ?? ASPresentationAnchor() #endif - -extension ASPresentationAnchor { - /// A platform-specific anchor, intended to be used by ASAuthorizationController - static let `default` = defaultPresentationAnchor + } } diff --git a/Sources/SnapAuth/SnapAuth+ASACD.swift b/Sources/SnapAuth/SnapAuth+ASACD.swift index 3646ce4..47c83e6 100644 --- a/Sources/SnapAuth/SnapAuth+ASACD.swift +++ b/Sources/SnapAuth/SnapAuth+ASACD.swift @@ -6,39 +6,42 @@ extension SnapAuth: ASAuthorizationControllerDelegate { /// Delegate method for ASAuthorizationController. /// This should not be called directly. - public func authorizationController( + nonisolated public func authorizationController( controller: ASAuthorizationController, didCompleteWithError error: Error ) { logger.debug("ASACD error") - guard let asError = error as? ASAuthorizationError else { - logger.error("authorizationController didCompleteWithError error was not an ASAuthorizationError") - sendError(.unknown) - return - } + Task { @MainActor in + guard let asError = error as? ASAuthorizationError else { + logger.error("authorizationController didCompleteWithError error was not an ASAuthorizationError") + sendError(.unknown) + return + } - sendError(asError.code.snapAuthError) - // The start call can SILENTLY produce this error which never makes it into this handler - // ASAuthorizationController credential request failed with error: Error Domain=com.apple.AuthenticationServices.AuthorizationError Code=1004 "(null)" + sendError(asError.code.snapAuthError) + // The start call can SILENTLY produce this error which never makes it into this handler + // ASAuthorizationController credential request failed with error: Error Domain=com.apple.AuthenticationServices.AuthorizationError Code=1004 "(null)" + } } /// Delegate method for ASAuthorizationController. /// This should not be called directly. - public func authorizationController( + nonisolated public func authorizationController( controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization ) { logger.debug("ASACD did complete") - - switch authorization.credential { - case is ASAuthorizationPublicKeyCredentialAssertion: - handleAssertion(authorization.credential as! ASAuthorizationPublicKeyCredentialAssertion) - case is ASAuthorizationPublicKeyCredentialRegistration: - handleRegistration(authorization.credential as! ASAuthorizationPublicKeyCredentialRegistration) - default: - logger.error("Unexpected credential type \(String(describing: type(of: authorization.credential)))") - sendError(.unexpectedAuthorizationType) + Task { @MainActor in + switch authorization.credential { + case is ASAuthorizationPublicKeyCredentialAssertion: + handleAssertion(authorization.credential as! ASAuthorizationPublicKeyCredentialAssertion) + case is ASAuthorizationPublicKeyCredentialRegistration: + handleRegistration(authorization.credential as! ASAuthorizationPublicKeyCredentialRegistration) + default: + logger.error("Unexpected credential type \(String(describing: type(of: authorization.credential)))") + sendError(.unexpectedAuthorizationType) + } } } @@ -157,4 +160,3 @@ extension SnapAuth: ASAuthorizationControllerDelegate { // } // } } - diff --git a/Sources/SnapAuth/SnapAuth.swift b/Sources/SnapAuth/SnapAuth.swift index 20faf1c..6bcf31c 100644 --- a/Sources/SnapAuth/SnapAuth.swift +++ b/Sources/SnapAuth/SnapAuth.swift @@ -7,6 +7,7 @@ import os /// This is used to start the passkey registration and authentication processes, /// typically in the `action` of a `Button` @available(macOS 12.0, iOS 15.0, tvOS 16.0, *) +@MainActor public class SnapAuth: NSObject { // NSObject for ASAuthorizationControllerDelegate internal let api: SnapAuthClient @@ -40,7 +41,7 @@ public class SnapAuth: NSObject { // NSObject for ASAuthorizationControllerDeleg } /// Permitted authenticator types - public enum Authenticator: CaseIterable { + public enum Authenticator: CaseIterable, Sendable { /// Allow all available authenticator types to be used public static let all = Set(Authenticator.allCases) @@ -231,7 +232,7 @@ public class SnapAuth: NSObject { // NSObject for ASAuthorizationControllerDeleg } /// A representation of the user that is trying to authenticate. -public enum AuthenticatingUser { +public enum AuthenticatingUser: Sendable { /// Your application's internal identifier for the user (usually a primary key) case id(String) /// The user's handle, such as a username or email address