diff --git a/packages/celest_auth/Makefile b/packages/celest_auth/Makefile deleted file mode 100644 index d7c51339..00000000 --- a/packages/celest_auth/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -FOUNDATION_SYMBOLS=lib/src/platform/darwin/foundation.yaml -FOUNDATION_URI=package:celest_auth/src/platform/darwin/foundation.ffi.dart - -ffi: - @echo "Generating FFI bindings..." - dart run ffigen --config=ffigen.foundation.yaml - @echo "Removing 'instancetype' from FFI bindings..." - @yq -i "del(.files.\"${FOUNDATION_URI}\".symbols.\"c:@T@instancetype\")" ${FOUNDATION_SYMBOLS} - dart run ffigen --config=ffigen.authentication_services.yaml - dart run ffigen --config=ffigen.uikit.yaml - - @echo "Generating JNI bindings..." - @pushd example - flutter pub get - flutter build apk - @popd - dart run jnigen --config=jnigen.yaml diff --git a/packages/celest_auth/darwin/Classes/CelestAuth.swift b/packages/celest_auth/darwin/Classes/CelestAuth.swift new file mode 100644 index 00000000..18b2554e --- /dev/null +++ b/packages/celest_auth/darwin/Classes/CelestAuth.swift @@ -0,0 +1,351 @@ +#if os(iOS) +import UIKit +#elseif os(macOS) +import AppKit +#else +#error("Unsupported OS") +#endif + +import AuthenticationServices + +public typealias OnSuccess = (UnsafePointer) -> Void +public typealias OnError = (CelestAuthErrorCode, UnsafePointer) -> Void + +@objc public enum CelestAuthErrorCode: Int, RawRepresentable { + case unknown = 0 + case unsupported = 1 + case serde = 2 +} + +@objc protocol CelestAuthProtocol: NSObjectProtocol { + /// Whether passkeys are supported on the current platform. + @objc var isPasskeysSupported: Bool { get } + + /// Sends a registration request to the platform authenticator and + /// completes with the registration response or an error. + @objc func register( + request: String, + onSuccess: @escaping OnSuccess, + onError: @escaping OnError + ) + + /// Sends an authentication request to the platform authenticator and + /// completes with the authentication response or an error. + @objc func authenticate( + request: String, + onSuccess: @escaping OnSuccess, + onError: @escaping OnError + ) +} + +@objc public class CelestAuth: NSObject, CelestAuthProtocol { + @objc public override init() { + if #available(iOS 15.0, macOS 12.0, *) { + self.impl = CelestAuthSupported() + } else { + self.impl = CelestAuthUnsupported() + } + } + + private let impl: CelestAuthProtocol + + @objc public var isPasskeysSupported: Bool { + impl.isPasskeysSupported + } + + @objc public func register(request: String, onSuccess: @escaping OnSuccess, onError: @escaping OnError) { + impl.register(request: request, onSuccess: onSuccess, onError: onError) + } + + @objc public func authenticate(request: String, onSuccess: @escaping OnSuccess, onError: @escaping OnError) { + impl.authenticate(request: request, onSuccess: onSuccess, onError: onError) + } + + @objc public func freePointer(_ ptr: UnsafePointer) { + ptr.deallocate() + } +} + +@available(iOS 15.0, macOS 12.0, *) +class CelestAuthSupported: NSObject, CelestAuthProtocol, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { + + private var onSuccess: OnSuccess? + private var onError: OnError? + + var isPasskeysSupported: Bool { true } + + func register( + request: String, + onSuccess: @escaping OnSuccess, + onError: @escaping OnError + ) { + guard let data = request.data(using: .utf8), + let options = try? JSONDecoder().decode(PasskeyRequestOptions.self, from: data), + let challenge = Data(base64URLEncoded: options.challenge), + let userID = options.user.id.data(using: .utf8) + else { + onError(.serde, "Failed to deserialize registration request") + return + } + self.onSuccess = onSuccess + self.onError = onError + let platformProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: options.rp.id) + let platformKeyRequest = platformProvider.createCredentialRegistrationRequest( + challenge: challenge, + name: options.user.name, + userID: userID + ) + let authController = ASAuthorizationController(authorizationRequests: [platformKeyRequest]) + authController.delegate = self + authController.presentationContextProvider = self + authController.performRequests() + } + + func authenticate( + request: String, + onSuccess: @escaping OnSuccess, + onError: @escaping OnError + ) { + guard let data = request.data(using: .utf8), + let options = try? JSONDecoder().decode(PasskeyAuthenticationOptions.self, from: data), + let challenge = Data(base64URLEncoded: options.challenge) + else { + onError(.serde, "Failed to deserialize authentication request") + return + } + self.onSuccess = onSuccess + self.onError = onError + let platformProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: options.rpId) + let platformKeyRequest = platformProvider.createCredentialAssertionRequest(challenge: challenge) + let authController = ASAuthorizationController(authorizationRequests: [platformKeyRequest]) + authController.delegate = self + authController.presentationContextProvider = self + authController.performRequests() + } + + private func reset() { + self.onSuccess = nil + self.onError = nil + } + + private func complete(value: Data) { + onSuccess?(value.unsafePointer) + reset() + } + + private func complete(error: CelestAuthErrorCode, _ message: String) { + onError?(error, message.unsafePointer) + reset() + } + + private func complete(error: Error) { + onError?(.unknown, error.localizedDescription.unsafePointer) + reset() + } + + public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + #if os(iOS) + ASPresentationAnchor() + #else + let windows = NSApplication.shared.windows + var presentationAnchor = windows.first + for window in windows { + if window.isKeyWindow { + presentationAnchor = window + } + } + return presentationAnchor ?? ASPresentationAnchor() + #endif + } + + public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { + if let credential = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialRegistration { + let response = PasskeyRegistration(credential: credential) + guard let responseJson = try? JSONEncoder().encode(response) else { + complete(error: .serde, "Failed to serialize registration response") + return + } + return complete(value: responseJson) + } else if let credential = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialAssertion { + let response = PasskeyAuthentication(credential: credential) + guard let responseJson = try? JSONEncoder().encode(response) else { + complete(error: .serde, "Failed to serialize authentication response") + return + } + return complete(value: responseJson) + } else { + complete(error: .unknown, "Unknown credential type: \(authorization.self)") + } + } + + public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { + complete(error: error) + } +} + +class CelestAuthUnsupported: NSObject, CelestAuthProtocol { + var isPasskeysSupported: Bool { false } + + func register(request: String, onSuccess: @escaping OnSuccess, onError: @escaping OnError) { + onError(.unsupported, "Unsupported platform".unsafePointer) + } + + func authenticate(request: String, onSuccess: @escaping OnSuccess, onError: @escaping OnError) { + onError(.unsupported, "Unsupported platform".unsafePointer) + } +} + +struct PasskeyRequestOptions: Codable { + let challenge: String + let rp: PasskeyRequestRp + let user: PasskeyRequestUser +} + +struct PasskeyRequestRp: Codable { + let name: String + let id: String +} + +struct PasskeyRequestUser: Codable { + let id: String + let name: String + let displayName: String +} + +struct PasskeyAuthenticationOptions: Codable { + let rpId: String + let challenge: String +} + +@available(iOS 15.0, macOS 12.0, *) +struct PasskeyRegistration: Codable { + init(credential: ASAuthorizationPlatformPublicKeyCredentialRegistration) { + let credentialID = credential.credentialID.base64URLEncodedString() + self.id = credentialID + self.rawId = credentialID + self.response = PasskeyRegistrationResponse( + clientDataJSON: credential.rawClientDataJSON.base64URLEncodedString(), + attestationObject: credential.rawAttestationObject?.base64URLEncodedString() + ) + if #available(iOS 16.6, macOS 13.5, *) { + switch credential.attachment { + case .platform: + self.authenticatorAttachment = "platform" + case .crossPlatform: + self.authenticatorAttachment = "cross-platform" + @unknown default: + self.authenticatorAttachment = "unknown" + } + } else { + self.authenticatorAttachment = nil + } + } + + let id: String + let rawId: String + var type = "public-key" + let response: PasskeyRegistrationResponse + let authenticatorAttachment: String? +} + +struct PasskeyRegistrationResponse: Codable { + let clientDataJSON: String + let attestationObject: String? +} + +@available(iOS 15.0, macOS 12.0, *) +struct PasskeyAuthentication: Codable { + init(credential: ASAuthorizationPlatformPublicKeyCredentialAssertion) { + let credentialID = credential.credentialID.base64URLEncodedString() + self.id = credentialID + self.rawId = credentialID + self.response = PasskeyAuthenticationResponse( + clientDataJSON: credential.rawClientDataJSON.base64URLEncodedString(), + authenticatorData: credential.rawAuthenticatorData.base64URLEncodedString(), + signature: credential.signature.base64URLEncodedString(), + userHandle: credential.userID.base64URLEncodedString() + ) + if #available(iOS 16.6, macOS 13.5, *) { + switch credential.attachment { + case .platform: + self.authenticatorAttachment = "platform" + case .crossPlatform: + self.authenticatorAttachment = "cross-platform" + @unknown default: + self.authenticatorAttachment = "unknown" + } + } else { + self.authenticatorAttachment = nil + } + } + + let id: String + let rawId: String + var type: String = "public-key" + let response: PasskeyAuthenticationResponse + let authenticatorAttachment: String? +} + +struct PasskeyAuthenticationResponse: Codable { + let clientDataJSON: String + let authenticatorData: String + let signature: String + let userHandle: String +} + +extension Data { + + /// Instantiates data by decoding a base64url string into base64 + /// + /// - Parameter string: A base64url encoded string + init?(base64URLEncoded string: String) { + self.init(base64Encoded: string.toggleBase64URLSafe(on: false)) + } + + /// Encodes the string into a base64url safe representation + /// + /// - Returns: A string that is base64 encoded but made safe for passing + /// in as a query parameter into a URL string + func base64URLEncodedString() -> String { + return self.base64EncodedString().toggleBase64URLSafe(on: true) + } + + + var unsafePointer: UnsafeMutablePointer { + let ptr = UnsafeMutablePointer.allocate(capacity: count + 1) + copyBytes(to: ptr, count: count) + return ptr + } + +} + +extension String { + + var unsafePointer: UnsafeMutablePointer { + data(using: .utf8)!.unsafePointer + } + + /// Encodes or decodes into a base64url safe representation + /// + /// - Parameter on: Whether or not the string should be made safe for URL strings + /// - Returns: if `on`, then a base64url string; if `off` then a base64 string + func toggleBase64URLSafe(on: Bool) -> String { + if on { + // Make base64 string safe for passing into URL query params + let base64url = self.replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "=", with: "") + return base64url + } else { + // Return to base64 encoding + var base64 = self.replacingOccurrences(of: "_", with: "/") + .replacingOccurrences(of: "-", with: "+") + // Add any necessary padding with `=` + if base64.count % 4 != 0 { + base64.append(String(repeating: "=", count: 4 - base64.count % 4)) + } + return base64 + } + } + +} diff --git a/packages/celest_auth/darwin/celest_auth.podspec b/packages/celest_auth/darwin/celest_auth.podspec new file mode 100644 index 00000000..ffdd34b6 --- /dev/null +++ b/packages/celest_auth/darwin/celest_auth.podspec @@ -0,0 +1,27 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint ffi_plugin.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'celest_auth' + s.version = '0.0.1' + s.summary = 'Shared iOS/macOS platform code of Celest Auth.' + s.description = <<-DESC + Shared iOS/macOS platform code of Celest Auth. + DESC + s.homepage = 'https://celest.dev' + s.license = { :file => '../LICENSE' } + s.author = { 'Dillon Nys' => 'dillon@celest.dev' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + + s.ios.dependency 'Flutter' + s.osx.dependency 'FlutterMacOS' + s.ios.deployment_target = '11.0' + s.osx.deployment_target = '10.11' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.module_name = 'celest_auth' + s.swift_version = '5.0' +end diff --git a/packages/celest_auth/example/ios/Podfile.lock b/packages/celest_auth/example/ios/Podfile.lock new file mode 100644 index 00000000..3550bbf8 --- /dev/null +++ b/packages/celest_auth/example/ios/Podfile.lock @@ -0,0 +1,23 @@ +PODS: + - celest_auth (0.0.1): + - Flutter + - FlutterMacOS + - Flutter (1.0.0) + +DEPENDENCIES: + - celest_auth (from `.symlinks/plugins/celest_auth/darwin`) + - Flutter (from `Flutter`) + +EXTERNAL SOURCES: + celest_auth: + :path: ".symlinks/plugins/celest_auth/darwin" + Flutter: + :path: Flutter + +SPEC CHECKSUMS: + celest_auth: 94a61a3de8684f0bb7459baba4d5e8b5b9780f72 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 + +COCOAPODS: 1.15.0 diff --git a/packages/celest_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/celest_auth/example/ios/Runner.xcodeproj/project.pbxproj index 9663f5ac..6d73cf39 100644 --- a/packages/celest_auth/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/celest_auth/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,12 +8,14 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 28DB6D6E79B78B827EA52321 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA41D9D03F0845F56EED13E8 /* Pods_RunnerTests.framework */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + D2DE32F25AF0169F89BEFB6D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC26DE6EFB87491B2A14E832 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,9 +44,13 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 16F2CF9C4003BEF4F1163958 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 1B826A9919327048F3017894 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 43B113F6AA4D863C070D780C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 4A803A079E9B02D0656FF2F9 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -55,13 +61,27 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AC1A9A7D92F4C66A94DF4502 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + AC26DE6EFB87491B2A14E832 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C0A1769B2B954E91009FD7F1 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + EA41D9D03F0845F56EED13E8 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F39F6AAC7F27A00A2406F559 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 87B3C14EA4C1D6344360B996 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 28DB6D6E79B78B827EA52321 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D2DE32F25AF0169F89BEFB6D /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,6 +96,19 @@ path = RunnerTests; sourceTree = ""; }; + 55CD265E653F237C38D62A19 /* Pods */ = { + isa = PBXGroup; + children = ( + AC1A9A7D92F4C66A94DF4502 /* Pods-Runner.debug.xcconfig */, + 43B113F6AA4D863C070D780C /* Pods-Runner.release.xcconfig */, + F39F6AAC7F27A00A2406F559 /* Pods-Runner.profile.xcconfig */, + 16F2CF9C4003BEF4F1163958 /* Pods-RunnerTests.debug.xcconfig */, + 1B826A9919327048F3017894 /* Pods-RunnerTests.release.xcconfig */, + 4A803A079E9B02D0656FF2F9 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -94,6 +127,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + 55CD265E653F237C38D62A19 /* Pods */, + E94F0FFABF6E45393CFB6659 /* Frameworks */, ); sourceTree = ""; }; @@ -109,6 +144,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + C0A1769B2B954E91009FD7F1 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -121,6 +157,15 @@ path = Runner; sourceTree = ""; }; + E94F0FFABF6E45393CFB6659 /* Frameworks */ = { + isa = PBXGroup; + children = ( + AC26DE6EFB87491B2A14E832 /* Pods_Runner.framework */, + EA41D9D03F0845F56EED13E8 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -128,8 +173,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + FF50F668124EA2F2A5E0E44B /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + 87B3C14EA4C1D6344360B996 /* Frameworks */, ); buildRules = ( ); @@ -145,12 +192,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 60271842E3BCBF6EC455752F /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 233434462875656F03F245AF /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -222,6 +271,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 233434462875656F03F245AF /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -238,6 +304,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 60271842E3BCBF6EC455752F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -253,6 +341,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + FF50F668124EA2F2A5E0E44B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -361,6 +471,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 3N6FCLHNUW; ENABLE_BITCODE = NO; @@ -379,6 +490,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 16F2CF9C4003BEF4F1163958 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -396,6 +508,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 1B826A9919327048F3017894 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -411,6 +524,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4A803A079E9B02D0656FF2F9 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -541,6 +655,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 3N6FCLHNUW; ENABLE_BITCODE = NO; @@ -564,6 +679,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 3N6FCLHNUW; ENABLE_BITCODE = NO; diff --git a/packages/celest_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/celest_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/packages/celest_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/celest_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/celest_auth/example/ios/Runner/Runner.entitlements b/packages/celest_auth/example/ios/Runner/Runner.entitlements new file mode 100644 index 00000000..328f4467 --- /dev/null +++ b/packages/celest_auth/example/ios/Runner/Runner.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.associated-domains + + webcredentials:65b7-136-24-157-119.ngrok-free.app?mode=developer + + + diff --git a/packages/celest_auth/example/lib/main.dart b/packages/celest_auth/example/lib/main.dart index a7256585..e6345805 100644 --- a/packages/celest_auth/example/lib/main.dart +++ b/packages/celest_auth/example/lib/main.dart @@ -1,18 +1,95 @@ +import 'package:celest_auth/celest_auth.dart'; +import 'package:celest_core/celest_core.dart'; +import 'package:corks/corks.dart'; import 'package:flutter/material.dart'; +final authClient = AuthClient( + baseUri: Uri.http('localhost:8080'), +); +final passkeys = PasskeyPlatform(protocol: authClient.passkeys); + void main() { runApp(const MainApp()); } -class MainApp extends StatelessWidget { +class MainApp extends StatefulWidget { const MainApp({super.key}); + @override + State createState() => _MainAppState(); +} + +typedef User = ({ + String userId, + String email, +}); + +final class _State { + _State( + this.signingIn, { + this.user, + }); + + final bool signingIn; + final User? user; +} + +class _MainAppState extends State { + final _state = ValueNotifier(_State(false)); + + Future signInWithPasskey() async { + try { + _state.value = _State(true); + final response = await passkeys.register( + const PasskeyRegistrationRequest( + username: 'dillon@celest.dev', + ), + ); + await authClient.passkeys.verifyRegistration( + registration: response, + ); + final token = authClient.passkeys.token!; + print('Got token: $token'); + final cork = Cork.parse(token); + final bearer = (cork.bearer!.block as EntityBearer).entity; + final userId = bearer.uid.id; + final email = bearer.attributes['email']!.string; + print('User ID: $userId'); + print('Email: $email'); + _state.value = _State(false, user: ( + userId: userId, + email: email, + )); + } on Exception catch (e, st) { + print('Error: $e'); + print('Stacktrace: $st'); + _state.value = _State(false); + } + } + @override Widget build(BuildContext context) { - return const MaterialApp( + return MaterialApp( home: Scaffold( body: Center( - child: Text('Hello World!'), + child: ValueListenableBuilder( + valueListenable: _state, + builder: (context, state, child) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Currently signed in: ${state.user?.email}'), + const SizedBox(height: 16), + TextButton( + onPressed: !state.signingIn && state.user == null + ? signInWithPasskey + : null, + child: const Text('Sign in with Passkey'), + ), + ], + ); + }, + ), ), ), ); diff --git a/packages/celest_auth/example/macos/Podfile.lock b/packages/celest_auth/example/macos/Podfile.lock new file mode 100644 index 00000000..6772ce4f --- /dev/null +++ b/packages/celest_auth/example/macos/Podfile.lock @@ -0,0 +1,23 @@ +PODS: + - celest_auth (0.0.1): + - Flutter + - FlutterMacOS + - FlutterMacOS (1.0.0) + +DEPENDENCIES: + - celest_auth (from `Flutter/ephemeral/.symlinks/plugins/celest_auth/darwin`) + - FlutterMacOS (from `Flutter/ephemeral`) + +EXTERNAL SOURCES: + celest_auth: + :path: Flutter/ephemeral/.symlinks/plugins/celest_auth/darwin + FlutterMacOS: + :path: Flutter/ephemeral + +SPEC CHECKSUMS: + celest_auth: 94a61a3de8684f0bb7459baba4d5e8b5b9780f72 + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 + +COCOAPODS: 1.15.0 diff --git a/packages/celest_auth/example/macos/Runner.xcodeproj/project.pbxproj b/packages/celest_auth/example/macos/Runner.xcodeproj/project.pbxproj index 2bb55215..3bd0033f 100644 --- a/packages/celest_auth/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/celest_auth/example/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 406DE7CE9B74754E1048117A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5803BE715FD01790F514D3B9 /* Pods_Runner.framework */; }; + 40F7B19533D9D558AF09AF85 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC3EDB190A40F413F8563C7F /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,11 +62,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 13EEB93C8B0869792FED80FB /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* celest_auth_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "celest_auth_example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* celest_auth_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = celest_auth_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -76,8 +79,15 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 398CD059A3C0B954CBC57916 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 459449A0413987CB865132A7 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 5803BE715FD01790F514D3B9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 97D8D099AFFAEF9E5C5B4A95 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + B5D7353413FE372DB380FDD9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + F030AA16F188F4B6F4ECDC34 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + FC3EDB190A40F413F8563C7F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,6 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 40F7B19533D9D558AF09AF85 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,6 +103,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 406DE7CE9B74754E1048117A /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -125,6 +137,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 5A21AB31001C2D938B38C254 /* Pods */, ); sourceTree = ""; }; @@ -172,9 +185,24 @@ path = Runner; sourceTree = ""; }; + 5A21AB31001C2D938B38C254 /* Pods */ = { + isa = PBXGroup; + children = ( + 398CD059A3C0B954CBC57916 /* Pods-Runner.debug.xcconfig */, + F030AA16F188F4B6F4ECDC34 /* Pods-Runner.release.xcconfig */, + B5D7353413FE372DB380FDD9 /* Pods-Runner.profile.xcconfig */, + 13EEB93C8B0869792FED80FB /* Pods-RunnerTests.debug.xcconfig */, + 459449A0413987CB865132A7 /* Pods-RunnerTests.release.xcconfig */, + 97D8D099AFFAEF9E5C5B4A95 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + 5803BE715FD01790F514D3B9 /* Pods_Runner.framework */, + FC3EDB190A40F413F8563C7F /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -186,6 +214,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 6D1F9FA5468921BA1FAA5966 /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -204,11 +233,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 041A10BE767685543E2C5776 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 82A83F3EA03936B423761E5A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -291,6 +322,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 041A10BE767685543E2C5776 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -329,6 +382,45 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 6D1F9FA5468921BA1FAA5966 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 82A83F3EA03936B423761E5A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -380,6 +472,7 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 13EEB93C8B0869792FED80FB /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -394,6 +487,7 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 459449A0413987CB865132A7 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -408,6 +502,7 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 97D8D099AFFAEF9E5C5B4A95 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -476,8 +571,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 3N6FCLHNUW; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -608,8 +705,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 3N6FCLHNUW; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -628,8 +727,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 3N6FCLHNUW; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/celest_auth/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/celest_auth/example/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16..21a3cc14 100644 --- a/packages/celest_auth/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/celest_auth/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/celest_auth/example/macos/Runner/DebugProfile.entitlements b/packages/celest_auth/example/macos/Runner/DebugProfile.entitlements index dddb8a30..4fe45f71 100644 --- a/packages/celest_auth/example/macos/Runner/DebugProfile.entitlements +++ b/packages/celest_auth/example/macos/Runner/DebugProfile.entitlements @@ -2,10 +2,16 @@ + com.apple.developer.associated-domains + + webcredentials:65b7-136-24-157-119.ngrok-free.app?mode=developer + com.apple.security.app-sandbox com.apple.security.cs.allow-jit + com.apple.security.network.client + com.apple.security.network.server diff --git a/packages/celest_auth/example/macos/Runner/Release.entitlements b/packages/celest_auth/example/macos/Runner/Release.entitlements index 852fa1a4..6e10687a 100644 --- a/packages/celest_auth/example/macos/Runner/Release.entitlements +++ b/packages/celest_auth/example/macos/Runner/Release.entitlements @@ -2,7 +2,15 @@ + com.apple.developer.associated-domains + + webcredentials:65b7-136-24-157-119.ngrok-free.app?mode=developer + com.apple.security.app-sandbox + com.apple.security.network.client + + com.apple.security.network.server + diff --git a/packages/celest_auth/example/pubspec.yaml b/packages/celest_auth/example/pubspec.yaml index 7e50db91..dbee3da9 100644 --- a/packages/celest_auth/example/pubspec.yaml +++ b/packages/celest_auth/example/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: celest: ^0.2.0 celest_auth: ^0.3.0 + celest_core: ^0.2.0 flutter: sdk: flutter diff --git a/packages/celest_auth/ffigen.darwin.yaml b/packages/celest_auth/ffigen.darwin.yaml new file mode 100644 index 00000000..bfbe03e8 --- /dev/null +++ b/packages/celest_auth/ffigen.darwin.yaml @@ -0,0 +1,34 @@ +name: CelestAuthDarwin +description: | + Bindings for CelestAuth on iOS/macOS. + + Regenerate bindings with `dart run ffigen --config=ffigen.darwin.yaml`. +language: "objc" +output: + bindings: "lib/src/platform/darwin/celest_auth.ffi.dart" +headers: + entry-points: + - "example/build/macos/Build/Products/Release/celest_auth/celest_auth.framework/Headers/celest_auth-Swift.h" + include-directives: + - "example/build/macos/Build/Products/Release/celest_auth/celest_auth.framework/Headers/celest_auth-Swift.h" + - "/System/Volumes/Data/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" +import: + symbol-files: + - "package:celest_auth/src/platform/darwin/foundation.yaml" +preamble: | + // ignore_for_file: type=lint + // ignore_for_file: return_of_invalid_type + // ignore_for_file: unnecessary_non_null_assertion +comments: + style: any + length: full + +compiler-opts-automatic: + macos: + include-c-standard-library: true +exclude-all-by-default: true +objc-interfaces: + include: + - CelestAuth + module: + CelestAuth: celest_auth diff --git a/packages/celest_auth/ffigen.uikit.yaml b/packages/celest_auth/ffigen.uikit.yaml index 2ca59c30..272e3160 100644 --- a/packages/celest_auth/ffigen.uikit.yaml +++ b/packages/celest_auth/ffigen.uikit.yaml @@ -32,7 +32,7 @@ comments: compiler-opts-automatic: macos: - include-c-standard-library: false + include-c-standard-library: true exclude-all-by-default: true objc-interfaces: include: diff --git a/packages/celest_auth/generate.sh b/packages/celest_auth/generate.sh new file mode 100755 index 00000000..d1853c67 --- /dev/null +++ b/packages/celest_auth/generate.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -euo pipefail + +FOUNDATION_SYMBOLS=lib/src/platform/darwin/foundation.yaml +FOUNDATION_URI=package:celest_auth/src/platform/darwin/foundation.ffi.dart + +echo "Building example app..." +pushd example +flutter pub get +flutter build apk +flutter build macos +popd + +echo "Generating FFI bindings..." +dart run ffigen --config=ffigen.foundation.yaml +echo "Removing 'instancetype' from FFI bindings..." +yq -i "del(.files.\"${FOUNDATION_URI}\".symbols.\"c:@T@instancetype\")" ${FOUNDATION_SYMBOLS} +dart run ffigen --config=ffigen.authentication_services.yaml +dart run ffigen --config=ffigen.uikit.yaml +dart run ffigen --config=ffigen.darwin.yaml + +echo "Generating JNI bindings..." +dart run jnigen --config=jnigen.yaml diff --git a/packages/celest_auth/lib/celest_auth.dart b/packages/celest_auth/lib/celest_auth.dart index 09374ea4..77d9b395 100644 --- a/packages/celest_auth/lib/celest_auth.dart +++ b/packages/celest_auth/lib/celest_auth.dart @@ -1,4 +1,4 @@ library; -export 'src/client/auth_client.dart'; -export 'src/client/passkeys/passkey_client.dart'; +export 'src/client/auth_platform.dart'; +export 'src/client/passkeys/passkey_platform.dart'; diff --git a/packages/celest_auth/lib/src/client/auth_client_platform.vm.dart b/packages/celest_auth/lib/src/client/auth_client_platform.vm.dart deleted file mode 100644 index 621a61fa..00000000 --- a/packages/celest_auth/lib/src/client/auth_client_platform.vm.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:celest_auth/src/client/auth_client.android.dart'; -import 'package:celest_auth/src/client/auth_client.dart'; -import 'package:celest_auth/src/client/auth_client.darwin.dart'; -import 'package:celest_auth/src/client/auth_client.native.dart'; -import 'package:celest_core/celest_core.dart'; -// ignore: implementation_imports -import 'package:celest_core/src/util/globals.dart'; -import 'package:meta/meta.dart'; -import 'package:os_detect/os_detect.dart' as os; - -abstract base class AuthClientPlatform extends AuthClient { - factory AuthClientPlatform({ - required AuthProtocol protocol, - }) { - if (kIsDartNative) { - return AuthClientNative(protocol: protocol); - } - if (os.isIOS || os.isMacOS) { - return AuthClientDarwin(protocol: protocol); - } - if (os.isAndroid) { - return AuthClientAndroid(protocol: protocol); - } - throw UnsupportedError( - 'The current platform is not supported: ${os.operatingSystem}', - ); - } - - @protected - AuthClientPlatform.base({ - required super.protocol, - }) : super.base(); -} diff --git a/packages/celest_auth/lib/src/client/auth_client.android.dart b/packages/celest_auth/lib/src/client/auth_platform.android.dart similarity index 96% rename from packages/celest_auth/lib/src/client/auth_client.android.dart rename to packages/celest_auth/lib/src/client/auth_platform.android.dart index db07e57f..ba593514 100644 --- a/packages/celest_auth/lib/src/client/auth_client.android.dart +++ b/packages/celest_auth/lib/src/client/auth_platform.android.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:celest_auth/src/client/auth_client_platform.vm.dart'; +import 'package:celest_auth/src/client/auth_platform_impl.vm.dart'; import 'package:celest_auth/src/platform/android/jni_bindings.ffi.dart' as android show Uri; import 'package:celest_auth/src/platform/android/jni_bindings.ffi.dart' @@ -8,8 +8,8 @@ import 'package:celest_auth/src/platform/android/jni_bindings.ffi.dart' import 'package:jni/jni.dart'; import 'package:logging/logging.dart'; -final class AuthClientAndroid extends AuthClientPlatform { - AuthClientAndroid({ +final class AuthPlatformAndroid extends AuthPlatformImpl { + AuthPlatformAndroid({ required super.protocol, }) : super.base() { Jni.initDLApi(); diff --git a/packages/celest_auth/lib/src/client/auth_client.dart b/packages/celest_auth/lib/src/client/auth_platform.dart similarity index 57% rename from packages/celest_auth/lib/src/client/auth_client.dart rename to packages/celest_auth/lib/src/client/auth_platform.dart index c82a39d8..375cf2fa 100644 --- a/packages/celest_auth/lib/src/client/auth_client.dart +++ b/packages/celest_auth/lib/src/client/auth_platform.dart @@ -1,15 +1,15 @@ -import 'package:celest_auth/src/client/auth_client_platform.vm.dart' - if (dart.library.js_interop) 'package:celest_auth/src/client/auth_client_platform.web.dart'; +import 'package:celest_auth/src/client/auth_platform_impl.vm.dart' + if (dart.library.js_interop) 'package:celest_auth/src/client/auth_platform_impl.web.dart'; import 'package:celest_core/celest_core.dart'; import 'package:meta/meta.dart'; -abstract base class AuthClient { - factory AuthClient({ +abstract base class AuthPlatform { + factory AuthPlatform({ required AuthProtocol protocol, - }) = AuthClientPlatform; + }) = AuthPlatformImpl; @protected - AuthClient.base({ + AuthPlatform.base({ required this.protocol, }); diff --git a/packages/celest_auth/lib/src/client/auth_client.darwin.dart b/packages/celest_auth/lib/src/client/auth_platform.darwin.dart similarity index 93% rename from packages/celest_auth/lib/src/client/auth_client.darwin.dart rename to packages/celest_auth/lib/src/client/auth_platform.darwin.dart index b106b544..479aabaf 100644 --- a/packages/celest_auth/lib/src/client/auth_client.darwin.dart +++ b/packages/celest_auth/lib/src/client/auth_platform.darwin.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; -import 'package:celest_auth/src/client/auth_client_platform.vm.dart'; +import 'package:celest_auth/src/client/auth_platform_impl.vm.dart'; import 'package:celest_auth/src/platform/darwin/authentication_services.ffi.dart'; -final class AuthClientDarwin extends AuthClientPlatform { - AuthClientDarwin({ +final class AuthPlatformDarwin extends AuthPlatformImpl { + AuthPlatformDarwin({ required super.protocol, }) : super.base(); diff --git a/packages/celest_auth/lib/src/client/auth_client.native.dart b/packages/celest_auth/lib/src/client/auth_platform.native.dart similarity index 94% rename from packages/celest_auth/lib/src/client/auth_client.native.dart rename to packages/celest_auth/lib/src/client/auth_platform.native.dart index dc67327d..0a929090 100644 --- a/packages/celest_auth/lib/src/client/auth_client.native.dart +++ b/packages/celest_auth/lib/src/client/auth_platform.native.dart @@ -1,9 +1,9 @@ import 'dart:io'; -import 'package:celest_auth/src/client/auth_client_platform.vm.dart'; +import 'package:celest_auth/src/client/auth_platform_impl.vm.dart'; -final class AuthClientNative extends AuthClientPlatform { - AuthClientNative({ +final class AuthPlatformNative extends AuthPlatformImpl { + AuthPlatformNative({ required super.protocol, }) : super.base(); diff --git a/packages/celest_auth/lib/src/client/auth_client.web.dart b/packages/celest_auth/lib/src/client/auth_platform.web.dart similarity index 76% rename from packages/celest_auth/lib/src/client/auth_client.web.dart rename to packages/celest_auth/lib/src/client/auth_platform.web.dart index f20c559f..7455432d 100644 --- a/packages/celest_auth/lib/src/client/auth_client.web.dart +++ b/packages/celest_auth/lib/src/client/auth_platform.web.dart @@ -1,9 +1,9 @@ -import 'package:celest_auth/src/client/auth_client_platform.web.dart'; +import 'package:celest_auth/src/client/auth_platform_impl.web.dart'; import 'package:path/path.dart'; import 'package:web/web.dart'; -final class AuthClientWeb extends AuthClientPlatform { - AuthClientWeb({ +final class AuthPlatformWeb extends AuthPlatformImpl { + AuthPlatformWeb({ required super.protocol, }) : super.base(); diff --git a/packages/celest_auth/lib/src/client/auth_platform_impl.vm.dart b/packages/celest_auth/lib/src/client/auth_platform_impl.vm.dart new file mode 100644 index 00000000..e2a19a17 --- /dev/null +++ b/packages/celest_auth/lib/src/client/auth_platform_impl.vm.dart @@ -0,0 +1,33 @@ +import 'package:celest_auth/src/client/auth_platform.android.dart'; +import 'package:celest_auth/src/client/auth_platform.dart'; +import 'package:celest_auth/src/client/auth_platform.darwin.dart'; +import 'package:celest_auth/src/client/auth_platform.native.dart'; +import 'package:celest_core/celest_core.dart'; +// ignore: implementation_imports +import 'package:celest_core/src/util/globals.dart'; +import 'package:meta/meta.dart'; +import 'package:os_detect/os_detect.dart' as os; + +abstract base class AuthPlatformImpl extends AuthPlatform { + factory AuthPlatformImpl({ + required AuthProtocol protocol, + }) { + if (kIsDartNative) { + return AuthPlatformNative(protocol: protocol); + } + if (os.isIOS || os.isMacOS) { + return AuthPlatformDarwin(protocol: protocol); + } + if (os.isAndroid) { + return AuthPlatformAndroid(protocol: protocol); + } + throw UnsupportedError( + 'The current platform is not supported: ${os.operatingSystem}', + ); + } + + @protected + AuthPlatformImpl.base({ + required super.protocol, + }) : super.base(); +} diff --git a/packages/celest_auth/lib/src/client/auth_client_platform.web.dart b/packages/celest_auth/lib/src/client/auth_platform_impl.web.dart similarity index 61% rename from packages/celest_auth/lib/src/client/auth_client_platform.web.dart rename to packages/celest_auth/lib/src/client/auth_platform_impl.web.dart index 2a14abd2..8e37e44b 100644 --- a/packages/celest_auth/lib/src/client/auth_client_platform.web.dart +++ b/packages/celest_auth/lib/src/client/auth_platform_impl.web.dart @@ -1,17 +1,17 @@ -import 'package:celest_auth/src/client/auth_client.dart'; -import 'package:celest_auth/src/client/auth_client.web.dart'; +import 'package:celest_auth/src/client/auth_platform.dart'; +import 'package:celest_auth/src/client/auth_platform.web.dart'; import 'package:celest_core/celest_core.dart'; // ignore: implementation_imports import 'package:celest_core/src/util/globals.dart'; import 'package:meta/meta.dart'; import 'package:os_detect/os_detect.dart' as os; -abstract base class AuthClientPlatform extends AuthClient { - factory AuthClientPlatform({ +abstract base class AuthPlatformImpl extends AuthPlatform { + factory AuthPlatformImpl({ required AuthProtocol protocol, }) { if (kIsWeb) { - return AuthClientWeb(protocol: protocol); + return AuthPlatformWeb(protocol: protocol); } throw UnsupportedError( 'The current platform is not supported: ${os.operatingSystem}', @@ -19,7 +19,7 @@ abstract base class AuthClientPlatform extends AuthClient { } @protected - AuthClientPlatform.base({ + AuthPlatformImpl.base({ required super.protocol, }) : super.base(); } diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_client.darwin.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_client.darwin.dart deleted file mode 100644 index 1232a64b..00000000 --- a/packages/celest_auth/lib/src/client/passkeys/passkey_client.darwin.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:celest_auth/src/client/passkeys/passkey_client_platform.vm.dart'; -import 'package:celest_core/celest_core.dart'; - -final class PasskeyClientDarwin extends PasskeyClientPlatform { - PasskeyClientDarwin({ - required super.protocol, - }) : super.base(); - - @override - Future get isSupported => throw UnimplementedError(); - - @override - Future register( - PasskeyRegistrationRequest request, - ) async { - throw UnimplementedError(); - } - - @override - Future authenticate( - PasskeyAuthenticationRequest request, - ) { - throw UnimplementedError(); - } -} diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_client_platform.vm.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_client_platform.vm.dart deleted file mode 100644 index 15697ab5..00000000 --- a/packages/celest_auth/lib/src/client/passkeys/passkey_client_platform.vm.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:celest_auth/src/client/passkeys/passkey_client.android.dart'; -import 'package:celest_auth/src/client/passkeys/passkey_client.dart'; -import 'package:celest_auth/src/client/passkeys/passkey_client.darwin.dart'; -import 'package:celest_core/celest_core.dart'; -// ignore: implementation_imports -import 'package:celest_core/src/util/globals.dart'; -import 'package:meta/meta.dart'; -import 'package:os_detect/os_detect.dart' as os; - -abstract base class PasskeyClientPlatform extends PasskeyClient { - factory PasskeyClientPlatform({ - required PasskeyProtocol protocol, - }) { - if (kIsFlutter) { - if (os.isIOS || os.isMacOS) { - return PasskeyClientDarwin(protocol: protocol); - } - if (os.isAndroid) { - return PasskeyClientAndroid(protocol: protocol); - } - } - throw UnsupportedError( - 'The current platform is not supported: ${os.operatingSystem}', - ); - } - - @protected - PasskeyClientPlatform.base({ - required super.protocol, - }) : super.base(); -} diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_client.android.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.android.dart similarity index 97% rename from packages/celest_auth/lib/src/client/passkeys/passkey_client.android.dart rename to packages/celest_auth/lib/src/client/passkeys/passkey_platform.android.dart index c13a168c..4a8da748 100644 --- a/packages/celest_auth/lib/src/client/passkeys/passkey_client.android.dart +++ b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.android.dart @@ -1,15 +1,15 @@ import 'dart:async'; import 'dart:convert'; -import 'package:celest_auth/src/client/passkeys/passkey_client_platform.vm.dart'; +import 'package:celest_auth/src/client/passkeys/passkey_platform_impl.vm.dart'; import 'package:celest_auth/src/platform/android/jni_bindings.ffi.dart' hide Exception, Uri; import 'package:celest_auth/src/platform/android/jni_helpers.dart'; import 'package:celest_core/celest_core.dart'; import 'package:jni/jni.dart'; -final class PasskeyClientAndroid extends PasskeyClientPlatform { - PasskeyClientAndroid({ +final class PasskeyPlatformAndroid extends PasskeyPlatformImpl { + PasskeyPlatformAndroid({ required super.protocol, }) : super.base(); diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_client.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.dart similarity index 67% rename from packages/celest_auth/lib/src/client/passkeys/passkey_client.dart rename to packages/celest_auth/lib/src/client/passkeys/passkey_platform.dart index a22081c2..63e0f5ae 100644 --- a/packages/celest_auth/lib/src/client/passkeys/passkey_client.dart +++ b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.dart @@ -1,15 +1,15 @@ -import 'package:celest_auth/src/client/passkeys/passkey_client_platform.vm.dart' - if (dart.library.js_interop) 'package:celest_auth/src/client/passkeys/passkey_client_platform.web.dart'; +import 'package:celest_auth/src/client/passkeys/passkey_platform_impl.vm.dart' + if (dart.library.js_interop) 'package:celest_auth/src/client/passkeys/passkey_platform_impl.web.dart'; import 'package:celest_core/celest_core.dart'; import 'package:meta/meta.dart'; -abstract base class PasskeyClient { - factory PasskeyClient({ +abstract base class PasskeyPlatform { + factory PasskeyPlatform({ required PasskeyProtocol protocol, - }) = PasskeyClientPlatform; + }) = PasskeyPlatformImpl; @protected - PasskeyClient.base({ + PasskeyPlatform.base({ required this.protocol, }); diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_platform.darwin.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.darwin.dart new file mode 100644 index 00000000..83da0523 --- /dev/null +++ b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.darwin.dart @@ -0,0 +1,113 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ffi'; + +import 'package:celest_auth/src/client/passkeys/passkey_platform_impl.vm.dart'; +import 'package:celest_auth/src/platform/darwin/celest_auth.ffi.dart'; +import 'package:celest_core/celest_core.dart'; +// ignore: implementation_imports +import 'package:celest_core/src/util/globals.dart'; +import 'package:ffi/ffi.dart'; + +final class PasskeyPlatformDarwin extends PasskeyPlatformImpl { + PasskeyPlatformDarwin({ + required super.protocol, + }) : super.base(); + + late final _platform = CelestAuthDarwin(DynamicLibrary.process()); + late final _celestAuth = CelestAuth.new1(_platform); + + @override + Future get isSupported async { + return kIsFlutter && _celestAuth.isPasskeysSupported; + } + + @override + Future register( + PasskeyRegistrationRequest request, + ) async { + if (!await isSupported) { + throw const PasskeyException( + message: 'Passkeys are not supported in this environment', + ); + } + final options = await protocol.requestRegistration(request: request); + + final completer = Completer(); + final onSuccess = ObjCBlock_ffiVoid_Uint8.listener(_platform, (json) { + if (json == nullptr) { + return completer.completeError(StateError('Bad ptr')); + } + final response = PasskeyRegistrationResponse.fromJson( + jsonDecode(json.cast().toDartString()) as Map, + ); + completer.complete(response); + _celestAuth.freePointer_(json); + }); + final onError = ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8.listener( + _platform, + (code, error) { + if (error == nullptr) { + return completer.completeError(StateError('Bad pointer')); + } + completer.completeError( + PasskeyException(message: error.cast().toDartString()), + ); + _celestAuth.freePointer_(error); + }, + ); + _celestAuth.registerWithRequest_onSuccess_onError_( + jsonEncode(options.toJson()).toNSString(_platform), + onSuccess, + onError, + ); + + // Await here so the blocks are not GC'd before they are called. + final response = await completer.future; + return response; + } + + @override + Future authenticate( + PasskeyAuthenticationRequest request, + ) async { + if (!await isSupported) { + throw const PasskeyException( + message: 'Passkeys are not supported in this environment', + ); + } + final options = await protocol.requestAuthentication(request: request); + final completer = Completer(); + final onSuccess = ObjCBlock_ffiVoid_Uint8.listener(_platform, (json) { + if (json == nullptr) { + return completer.completeError(StateError('Bad ptr')); + } + final response = PasskeyAuthenticationResponse.fromJson( + jsonDecode(json.cast().toDartString()) as Map, + ); + completer.complete(response); + _celestAuth.freePointer_(json); + }); + final onError = ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8.listener( + _platform, + (code, error) { + if (error == nullptr) { + return completer.completeError(StateError('Bad pointer')); + } + completer.completeError( + PasskeyException(message: error.cast().toString()), + ); + _celestAuth.freePointer_(error); + }, + ); + _celestAuth.authenticateWithRequest_onSuccess_onError_( + jsonEncode(options.toJson()).toNSString(_platform), + onSuccess, + onError, + ); + + // Await here so the blocks are not GC'd before they are called. + final response = await completer.future; + return response; + } +} diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_client.web.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.web.dart similarity index 79% rename from packages/celest_auth/lib/src/client/passkeys/passkey_client.web.dart rename to packages/celest_auth/lib/src/client/passkeys/passkey_platform.web.dart index 49a7972e..74b5fe44 100644 --- a/packages/celest_auth/lib/src/client/passkeys/passkey_client.web.dart +++ b/packages/celest_auth/lib/src/client/passkeys/passkey_platform.web.dart @@ -3,9 +3,9 @@ import 'dart:js_interop'; import 'dart:js_interop_unsafe'; import 'dart:typed_data'; -import 'package:celest_auth/src/client/passkeys/passkey_client_platform.web.dart'; -import 'package:celest_auth/src/client/passkeys/passkey_exception.dart'; -import 'package:celest_core/celest_core.dart'; +import 'package:celest_auth/src/client/passkeys/passkey_platform_impl.web.dart'; +import 'package:celest_core/celest_core.dart' + hide AuthenticatorSelectionCriteria; import 'package:web/web.dart' hide COSEAlgorithmIdentifier, @@ -14,8 +14,8 @@ import 'package:web/web.dart' UserVerificationRequirement, ResidentKeyRequirement; -final class PasskeyClientWeb extends PasskeyClientPlatform { - PasskeyClientWeb({ +final class PasskeyPlatformWeb extends PasskeyPlatformImpl { + PasskeyPlatformWeb({ required super.protocol, }) : super.base(); @@ -45,9 +45,33 @@ final class PasskeyClientWeb extends PasskeyClientPlatform { final credential = await window.navigator.credentials .create( CredentialCreationOptions( - publicKey: PublicKeyCredential.parseCreationOptionsFromJSON( - options.toJson().jsify() - as PublicKeyCredentialCreationOptionsJSON, + publicKey: PublicKeyCredentialCreationOptions( + challenge: options.challenge.buffer.toJS, + pubKeyCredParams: [ + for (final param in options.publicKeyCredentialParameters) + PublicKeyCredentialParameters( + alg: param.algorithm, + type: 'public-key', + ), + ].toJS, + rp: PublicKeyCredentialRpEntity( + id: options.rpId, + )..name = options.rpName, + user: PublicKeyCredentialUserEntity( + id: utf8.encode(options.userId).buffer.toJS, + displayName: options.userDisplayName, + )..name = options.userName, + attestation: 'none', + authenticatorSelection: AuthenticatorSelectionCriteria( + authenticatorAttachment: + options.authenticatorSelection.authenticatorAttachment!, + requireResidentKey: + options.authenticatorSelection.requireResidentKey, + residentKey: options.authenticatorSelection.residentKey!, + userVerification: + options.authenticatorSelection.userVerification, + ), + timeout: options.timeout.inMilliseconds, ), ), ) @@ -66,11 +90,7 @@ final class PasskeyClientWeb extends PasskeyClientPlatform { return PasskeyRegistrationResponse( id: credential.id, rawId: credential.rawId.toDart.asUint8List(), - clientData: PasskeyClientData.fromJson( - jsonDecode( - utf8.decode(response.clientDataJSON.toDart.asUint8List()), - ) as Map, - ), + clientDataJson: response.clientDataJSON.toDart.asUint8List(), attestationObject: response.attestationObject.toDart.asUint8List(), transports: response.transports, publicKeyAlgorithm: response.publicKeyAlgorithm, diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_platform_impl.vm.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_platform_impl.vm.dart new file mode 100644 index 00000000..6630b960 --- /dev/null +++ b/packages/celest_auth/lib/src/client/passkeys/passkey_platform_impl.vm.dart @@ -0,0 +1,27 @@ +import 'package:celest_auth/src/client/passkeys/passkey_platform.android.dart'; +import 'package:celest_auth/src/client/passkeys/passkey_platform.dart'; +import 'package:celest_auth/src/client/passkeys/passkey_platform.darwin.dart'; +import 'package:celest_core/celest_core.dart'; +import 'package:meta/meta.dart'; +import 'package:os_detect/os_detect.dart' as os; + +abstract base class PasskeyPlatformImpl extends PasskeyPlatform { + factory PasskeyPlatformImpl({ + required PasskeyProtocol protocol, + }) { + if (os.isIOS || os.isMacOS) { + return PasskeyPlatformDarwin(protocol: protocol); + } + if (os.isAndroid) { + return PasskeyPlatformAndroid(protocol: protocol); + } + throw UnsupportedError( + 'The current platform is not supported: ${os.operatingSystem}', + ); + } + + @protected + PasskeyPlatformImpl.base({ + required super.protocol, + }) : super.base(); +} diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_client_platform.web.dart b/packages/celest_auth/lib/src/client/passkeys/passkey_platform_impl.web.dart similarity index 58% rename from packages/celest_auth/lib/src/client/passkeys/passkey_client_platform.web.dart rename to packages/celest_auth/lib/src/client/passkeys/passkey_platform_impl.web.dart index c585e71a..bb834280 100644 --- a/packages/celest_auth/lib/src/client/passkeys/passkey_client_platform.web.dart +++ b/packages/celest_auth/lib/src/client/passkeys/passkey_platform_impl.web.dart @@ -1,17 +1,17 @@ -import 'package:celest_auth/src/client/passkeys/passkey_client.dart'; -import 'package:celest_auth/src/client/passkeys/passkey_client.web.dart'; +import 'package:celest_auth/src/client/passkeys/passkey_platform.dart'; +import 'package:celest_auth/src/client/passkeys/passkey_platform.web.dart'; import 'package:celest_core/celest_core.dart'; // ignore: implementation_imports import 'package:celest_core/src/util/globals.dart'; import 'package:meta/meta.dart'; import 'package:os_detect/os_detect.dart' as os; -abstract base class PasskeyClientPlatform extends PasskeyClient { - factory PasskeyClientPlatform({ +abstract base class PasskeyPlatformImpl extends PasskeyPlatform { + factory PasskeyPlatformImpl({ required PasskeyProtocol protocol, }) { if (kIsWeb) { - return PasskeyClientWeb(protocol: protocol); + return PasskeyPlatformWeb(protocol: protocol); } throw UnsupportedError( 'The current platform is not supported: ${os.operatingSystem}', @@ -19,7 +19,7 @@ abstract base class PasskeyClientPlatform extends PasskeyClient { } @protected - PasskeyClientPlatform.base({ + PasskeyPlatformImpl.base({ required super.protocol, }) : super.base(); } diff --git a/packages/celest_auth/lib/src/platform/darwin/authentication_services.yaml b/packages/celest_auth/lib/src/platform/darwin/authentication_services.yaml index 225d4dfa..30090c89 100644 --- a/packages/celest_auth/lib/src/platform/darwin/authentication_services.yaml +++ b/packages/celest_auth/lib/src/platform/darwin/authentication_services.yaml @@ -342,25 +342,25 @@ files: name: NSWindowTabGroup c:objc(cs)Protocol: name: Protocol - "objcBlock: 8ol47u? 8ol47u": - name: ObjCBlock_NSEvent_NSEvent - "objcBlock: divyil": + "objcBlock: 9pebjx": name: ObjCBlock_ffiVoid - "objcBlock: divyil 4m4hs3? 5clcv7?": - name: ObjCBlock_ffiVoid_NSURL_NSError - "objcBlock: divyil 5clcv7?": - name: ObjCBlock_ffiVoid_NSError - "objcBlock: divyil 684z1l": + "objcBlock: 9pebjx 1ybxrl": name: ObjCBlock_ffiVoid_ffiLong - "objcBlock: divyil 7xwwls aopvxr bool bool*": + "objcBlock: 9pebjx 4h1qi5 bool*": + name: ObjCBlock_ffiVoid_NSWindow_bool + "objcBlock: 9pebjx 4zw819? em34eq?": + name: ObjCBlock_ffiVoid_NSURL_NSError + "objcBlock: 9pebjx a2pk9j h5yc50 bool bool*": name: ObjCBlock_ffiVoid_ffiDouble_NSEventPhase_bool_bool - "objcBlock: divyil 8ol47u": + "objcBlock: 9pebjx em34eq?": + name: ObjCBlock_ffiVoid_NSError + "objcBlock: 9pebjx ep7r3p": name: ObjCBlock_ffiVoid_NSEvent - "objcBlock: divyil 8ol47u? bool*": + "objcBlock: 9pebjx ep7r3p? bool*": name: ObjCBlock_ffiVoid_NSEvent_bool - "objcBlock: divyil dce4iq bool*": - name: ObjCBlock_ffiVoid_NSWindow_bool - "objcBlock: divyil gkgg5n* bool*": + "objcBlock: 9pebjx eruvzs* bool*": name: ObjCBlock_ffiVoid_ObjCObject_bool - "objcBlock: gkgg5n*? 5clcv7 7op1mv": + "objcBlock: ep7r3p? ep7r3p": + name: ObjCBlock_NSEvent_NSEvent + "objcBlock: eruvzs*? em34eq 1xwvun": name: ObjCBlock_ObjCObject_NSError_NSString diff --git a/packages/celest_auth/lib/src/platform/darwin/celest_auth.ffi.dart b/packages/celest_auth/lib/src/platform/darwin/celest_auth.ffi.dart new file mode 100644 index 00000000..3e20fff2 --- /dev/null +++ b/packages/celest_auth/lib/src/platform/darwin/celest_auth.ffi.dart @@ -0,0 +1,2120 @@ +// ignore_for_file: type=lint +// ignore_for_file: return_of_invalid_type +// ignore_for_file: unnecessary_non_null_assertion + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; +import 'package:ffi/ffi.dart' as pkg_ffi; +import 'package:celest_auth/src/platform/darwin/foundation.ffi.dart' as imp1; + +/// Bindings for CelestAuth on iOS/macOS. +/// +/// Regenerate bindings with `dart run ffigen --config=ffigen.darwin.yaml`. +/// +class CelestAuthDarwin { + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + CelestAuthDarwin(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + CelestAuthDarwin.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + ffi.Pointer _registerName1(String name) { + final cstr = name.toNativeUtf8(); + final sel = _sel_registerName(cstr.cast()); + pkg_ffi.calloc.free(cstr); + return sel; + } + + ffi.Pointer _sel_registerName( + ffi.Pointer str, + ) { + return __sel_registerName( + str, + ); + } + + late final __sel_registerNamePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('sel_registerName'); + late final __sel_registerName = __sel_registerNamePtr + .asFunction Function(ffi.Pointer)>(); + + ffi.Pointer _getClass1(String name) { + final cstr = name.toNativeUtf8(); + final clazz = _objc_getClass(cstr.cast()); + pkg_ffi.calloc.free(cstr); + if (clazz == ffi.nullptr) { + throw Exception('Failed to load Objective-C class: $name'); + } + return clazz; + } + + ffi.Pointer _objc_getClass( + ffi.Pointer str, + ) { + return __objc_getClass( + str, + ); + } + + late final __objc_getClassPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('objc_getClass'); + late final __objc_getClass = __objc_getClassPtr + .asFunction Function(ffi.Pointer)>(); + + ffi.Pointer _objc_retain( + ffi.Pointer value, + ) { + return __objc_retain( + value, + ); + } + + late final __objc_retainPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('objc_retain'); + late final __objc_retain = __objc_retainPtr + .asFunction Function(ffi.Pointer)>(); + + void _objc_release( + ffi.Pointer value, + ) { + return __objc_release( + value, + ); + } + + late final __objc_releasePtr = + _lookup)>>( + 'objc_release'); + late final __objc_release = + __objc_releasePtr.asFunction)>(); + + late final _objc_releaseFinalizer2 = + ffi.NativeFinalizer(__objc_releasePtr.cast()); + late final _class_CelestAuth1 = _getClass1("celest_auth.CelestAuth"); + bool _objc_msgSend_0( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer clazz, + ) { + return __objc_msgSend_0( + obj, + sel, + clazz, + ); + } + + late final __objc_msgSend_0Ptr = _lookup< + ffi.NativeFunction< + ffi.Bool Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_0 = __objc_msgSend_0Ptr.asFunction< + bool Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_isKindOfClass_1 = _registerName1("isKindOfClass:"); + late final _class_NSObject1 = _getClass1("NSObject"); + late final _sel_load1 = _registerName1("load"); + void _objc_msgSend_1( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_1( + obj, + sel, + ); + } + + late final __objc_msgSend_1Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_1 = __objc_msgSend_1Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer)>(); + + late final _sel_initialize1 = _registerName1("initialize"); + late final _sel_init1 = _registerName1("init"); + instancetype _objc_msgSend_2( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_2( + obj, + sel, + ); + } + + late final __objc_msgSend_2Ptr = _lookup< + ffi.NativeFunction< + instancetype Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_2 = __objc_msgSend_2Ptr.asFunction< + instancetype Function(ffi.Pointer, ffi.Pointer)>(); + + late final _sel_new1 = _registerName1("new"); + late final _sel_allocWithZone_1 = _registerName1("allocWithZone:"); + instancetype _objc_msgSend_3( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer zone, + ) { + return __objc_msgSend_3( + obj, + sel, + zone, + ); + } + + late final __objc_msgSend_3Ptr = _lookup< + ffi.NativeFunction< + instancetype Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_3 = __objc_msgSend_3Ptr.asFunction< + instancetype Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_alloc1 = _registerName1("alloc"); + late final _sel_dealloc1 = _registerName1("dealloc"); + late final _sel_finalize1 = _registerName1("finalize"); + late final _sel_copy1 = _registerName1("copy"); + late final _sel_mutableCopy1 = _registerName1("mutableCopy"); + late final _sel_copyWithZone_1 = _registerName1("copyWithZone:"); + late final _sel_mutableCopyWithZone_1 = + _registerName1("mutableCopyWithZone:"); + late final _sel_instancesRespondToSelector_1 = + _registerName1("instancesRespondToSelector:"); + bool _objc_msgSend_4( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer aSelector, + ) { + return __objc_msgSend_4( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_4Ptr = _lookup< + ffi.NativeFunction< + ffi.Bool Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_4 = __objc_msgSend_4Ptr.asFunction< + bool Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _class_Protocol1 = _getClass1("Protocol"); + late final _sel_conformsToProtocol_1 = _registerName1("conformsToProtocol:"); + bool _objc_msgSend_5( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer protocol, + ) { + return __objc_msgSend_5( + obj, + sel, + protocol, + ); + } + + late final __objc_msgSend_5Ptr = _lookup< + ffi.NativeFunction< + ffi.Bool Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_5 = __objc_msgSend_5Ptr.asFunction< + bool Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_methodForSelector_1 = _registerName1("methodForSelector:"); + ffi.Pointer> _objc_msgSend_6( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer aSelector, + ) { + return __objc_msgSend_6( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_6Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer> Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_6 = __objc_msgSend_6Ptr.asFunction< + ffi.Pointer> Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_instanceMethodForSelector_1 = + _registerName1("instanceMethodForSelector:"); + late final _sel_doesNotRecognizeSelector_1 = + _registerName1("doesNotRecognizeSelector:"); + void _objc_msgSend_7( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer aSelector, + ) { + return __objc_msgSend_7( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_7Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_7 = __objc_msgSend_7Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_forwardingTargetForSelector_1 = + _registerName1("forwardingTargetForSelector:"); + ffi.Pointer _objc_msgSend_8( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer aSelector, + ) { + return __objc_msgSend_8( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_8Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_8 = __objc_msgSend_8Ptr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); + + late final _class_NSInvocation1 = _getClass1("NSInvocation"); + late final _class_NSMethodSignature1 = _getClass1("NSMethodSignature"); + late final _sel_signatureWithObjCTypes_1 = + _registerName1("signatureWithObjCTypes:"); + ffi.Pointer _objc_msgSend_9( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer types, + ) { + return __objc_msgSend_9( + obj, + sel, + types, + ); + } + + late final __objc_msgSend_9Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_9 = __objc_msgSend_9Ptr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_numberOfArguments1 = _registerName1("numberOfArguments"); + int _objc_msgSend_10( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_10( + obj, + sel, + ); + } + + late final __objc_msgSend_10Ptr = _lookup< + ffi.NativeFunction< + ffi.UnsignedLong Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_10 = __objc_msgSend_10Ptr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + late final _sel_getArgumentTypeAtIndex_1 = + _registerName1("getArgumentTypeAtIndex:"); + ffi.Pointer _objc_msgSend_11( + ffi.Pointer obj, + ffi.Pointer sel, + int idx, + ) { + return __objc_msgSend_11( + obj, + sel, + idx, + ); + } + + late final __objc_msgSend_11Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.UnsignedLong)>>('objc_msgSend'); + late final __objc_msgSend_11 = __objc_msgSend_11Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + late final _sel_frameLength1 = _registerName1("frameLength"); + late final _sel_isOneway1 = _registerName1("isOneway"); + bool _objc_msgSend_12( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_12( + obj, + sel, + ); + } + + late final __objc_msgSend_12Ptr = _lookup< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_12 = __objc_msgSend_12Ptr.asFunction< + bool Function(ffi.Pointer, ffi.Pointer)>(); + + late final _sel_methodReturnType1 = _registerName1("methodReturnType"); + ffi.Pointer _objc_msgSend_13( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_13( + obj, + sel, + ); + } + + late final __objc_msgSend_13Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_13 = __objc_msgSend_13Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_methodReturnLength1 = _registerName1("methodReturnLength"); + late final _sel_invocationWithMethodSignature_1 = + _registerName1("invocationWithMethodSignature:"); + ffi.Pointer _objc_msgSend_14( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer sig, + ) { + return __objc_msgSend_14( + obj, + sel, + sig, + ); + } + + late final __objc_msgSend_14Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_14 = __objc_msgSend_14Ptr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_methodSignature1 = _registerName1("methodSignature"); + ffi.Pointer _objc_msgSend_15( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_15( + obj, + sel, + ); + } + + late final __objc_msgSend_15Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_15 = __objc_msgSend_15Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_retainArguments1 = _registerName1("retainArguments"); + late final _sel_argumentsRetained1 = _registerName1("argumentsRetained"); + late final _sel_target1 = _registerName1("target"); + ffi.Pointer _objc_msgSend_16( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_16( + obj, + sel, + ); + } + + late final __objc_msgSend_16Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_16 = __objc_msgSend_16Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_setTarget_1 = _registerName1("setTarget:"); + void _objc_msgSend_17( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer value, + ) { + return __objc_msgSend_17( + obj, + sel, + value, + ); + } + + late final __objc_msgSend_17Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_17 = __objc_msgSend_17Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_selector1 = _registerName1("selector"); + ffi.Pointer _objc_msgSend_18( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_18( + obj, + sel, + ); + } + + late final __objc_msgSend_18Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_18 = __objc_msgSend_18Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_setSelector_1 = _registerName1("setSelector:"); + void _objc_msgSend_19( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer value, + ) { + return __objc_msgSend_19( + obj, + sel, + value, + ); + } + + late final __objc_msgSend_19Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_19 = __objc_msgSend_19Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_getReturnValue_1 = _registerName1("getReturnValue:"); + void _objc_msgSend_20( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer retLoc, + ) { + return __objc_msgSend_20( + obj, + sel, + retLoc, + ); + } + + late final __objc_msgSend_20Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_20 = __objc_msgSend_20Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_setReturnValue_1 = _registerName1("setReturnValue:"); + late final _sel_getArgument_atIndex_1 = + _registerName1("getArgument:atIndex:"); + void _objc_msgSend_21( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer argumentLocation, + int idx, + ) { + return __objc_msgSend_21( + obj, + sel, + argumentLocation, + idx, + ); + } + + late final __objc_msgSend_21Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Long)>>('objc_msgSend'); + late final __objc_msgSend_21 = __objc_msgSend_21Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, int)>(); + + late final _sel_setArgument_atIndex_1 = + _registerName1("setArgument:atIndex:"); + late final _sel_invoke1 = _registerName1("invoke"); + late final _sel_invokeWithTarget_1 = _registerName1("invokeWithTarget:"); + void _objc_msgSend_22( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer target, + ) { + return __objc_msgSend_22( + obj, + sel, + target, + ); + } + + late final __objc_msgSend_22Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_22 = __objc_msgSend_22Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_invokeUsingIMP_1 = _registerName1("invokeUsingIMP:"); + void _objc_msgSend_23( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer> imp, + ) { + return __objc_msgSend_23( + obj, + sel, + imp, + ); + } + + late final __objc_msgSend_23Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer>)>>( + 'objc_msgSend'); + late final __objc_msgSend_23 = __objc_msgSend_23Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer>)>(); + + late final _sel_forwardInvocation_1 = _registerName1("forwardInvocation:"); + void _objc_msgSend_24( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer anInvocation, + ) { + return __objc_msgSend_24( + obj, + sel, + anInvocation, + ); + } + + late final __objc_msgSend_24Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_24 = __objc_msgSend_24Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_methodSignatureForSelector_1 = + _registerName1("methodSignatureForSelector:"); + ffi.Pointer _objc_msgSend_25( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer aSelector, + ) { + return __objc_msgSend_25( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_25Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_25 = __objc_msgSend_25Ptr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_instanceMethodSignatureForSelector_1 = + _registerName1("instanceMethodSignatureForSelector:"); + late final _sel_allowsWeakReference1 = _registerName1("allowsWeakReference"); + late final _sel_retainWeakReference1 = _registerName1("retainWeakReference"); + late final _sel_isSubclassOfClass_1 = _registerName1("isSubclassOfClass:"); + late final _sel_resolveClassMethod_1 = _registerName1("resolveClassMethod:"); + late final _sel_resolveInstanceMethod_1 = + _registerName1("resolveInstanceMethod:"); + late final _sel_hash1 = _registerName1("hash"); + late final _sel_superclass1 = _registerName1("superclass"); + late final _sel_class1 = _registerName1("class"); + late final _class_NSString1 = _getClass1("NSString"); + late final _sel_length1 = _registerName1("length"); + late final _sel_characterAtIndex_1 = _registerName1("characterAtIndex:"); + int _objc_msgSend_26( + ffi.Pointer obj, + ffi.Pointer sel, + int index, + ) { + return __objc_msgSend_26( + obj, + sel, + index, + ); + } + + late final __objc_msgSend_26Ptr = _lookup< + ffi.NativeFunction< + ffi.UnsignedShort Function(ffi.Pointer, + ffi.Pointer, ffi.UnsignedLong)>>('objc_msgSend'); + late final __objc_msgSend_26 = __objc_msgSend_26Ptr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, int)>(); + + late final _class_NSCoder1 = _getClass1("NSCoder"); + late final _sel_encodeValueOfObjCType_at_1 = + _registerName1("encodeValueOfObjCType:at:"); + void _objc_msgSend_27( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer type, + ffi.Pointer addr, + ) { + return __objc_msgSend_27( + obj, + sel, + type, + addr, + ); + } + + late final __objc_msgSend_27Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_27 = __objc_msgSend_27Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); + + late final _class_NSData1 = _getClass1("NSData"); + late final _sel_bytes1 = _registerName1("bytes"); + ffi.Pointer _objc_msgSend_28( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_28( + obj, + sel, + ); + } + + late final __objc_msgSend_28Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_28 = __objc_msgSend_28Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_encodeDataObject_1 = _registerName1("encodeDataObject:"); + void _objc_msgSend_29( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer data, + ) { + return __objc_msgSend_29( + obj, + sel, + data, + ); + } + + late final __objc_msgSend_29Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_29 = __objc_msgSend_29Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_decodeDataObject1 = _registerName1("decodeDataObject"); + ffi.Pointer _objc_msgSend_30( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_30( + obj, + sel, + ); + } + + late final __objc_msgSend_30Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_30 = __objc_msgSend_30Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_decodeValueOfObjCType_at_size_1 = + _registerName1("decodeValueOfObjCType:at:size:"); + void _objc_msgSend_31( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer type, + ffi.Pointer data, + int size, + ) { + return __objc_msgSend_31( + obj, + sel, + type, + data, + size, + ); + } + + late final __objc_msgSend_31Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.UnsignedLong)>>('objc_msgSend'); + late final __objc_msgSend_31 = __objc_msgSend_31Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer, int)>(); + + late final _sel_versionForClassName_1 = + _registerName1("versionForClassName:"); + int _objc_msgSend_32( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer className, + ) { + return __objc_msgSend_32( + obj, + sel, + className, + ); + } + + late final __objc_msgSend_32Ptr = _lookup< + ffi.NativeFunction< + ffi.Long Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_32 = __objc_msgSend_32Ptr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_initWithCoder_1 = _registerName1("initWithCoder:"); + instancetype _objc_msgSend_33( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer coder, + ) { + return __objc_msgSend_33( + obj, + sel, + coder, + ); + } + + late final __objc_msgSend_33Ptr = _lookup< + ffi.NativeFunction< + instancetype Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_33 = __objc_msgSend_33Ptr.asFunction< + instancetype Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + late final _sel_stringWithCharacters_length_1 = + _registerName1("stringWithCharacters:length:"); + ffi.Pointer _objc_msgSend_34( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer characters, + int length, + ) { + return __objc_msgSend_34( + obj, + sel, + characters, + length, + ); + } + + late final __objc_msgSend_34Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.UnsignedInt)>>('objc_msgSend'); + late final __objc_msgSend_34 = __objc_msgSend_34Ptr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer, int)>(); + + late final _sel_dataUsingEncoding_1 = _registerName1("dataUsingEncoding:"); + ffi.Pointer _objc_msgSend_35( + ffi.Pointer obj, + ffi.Pointer sel, + int encoding, + ) { + return __objc_msgSend_35( + obj, + sel, + encoding, + ); + } + + late final __objc_msgSend_35Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.UnsignedInt)>>('objc_msgSend'); + late final __objc_msgSend_35 = __objc_msgSend_35Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + late final _sel_description1 = _registerName1("description"); + ffi.Pointer _objc_msgSend_36( + ffi.Pointer obj, + ffi.Pointer sel, + ) { + return __objc_msgSend_36( + obj, + sel, + ); + } + + late final __objc_msgSend_36Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_36 = __objc_msgSend_36Ptr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + late final _sel_debugDescription1 = _registerName1("debugDescription"); + late final _sel_isPasskeysSupported1 = _registerName1("isPasskeysSupported"); + ffi.Pointer<_ObjCBlockDesc> _newBlockDesc1() { + final d = + pkg_ffi.calloc.allocate<_ObjCBlockDesc>(ffi.sizeOf<_ObjCBlockDesc>()); + d.ref.reserved = 0; + d.ref.size = ffi.sizeOf<_ObjCBlock>(); + d.ref.copy_helper = ffi.nullptr; + d.ref.dispose_helper = ffi.nullptr; + d.ref.signature = ffi.nullptr; + return d; + } + + late final _objc_block_desc1 = _newBlockDesc1(); + late final _objc_concrete_global_block1 = + _lookup('_NSConcreteGlobalBlock'); + ffi.Pointer<_ObjCBlock> _newBlock1( + ffi.Pointer invoke, ffi.Pointer target) { + final b = pkg_ffi.calloc.allocate<_ObjCBlock>(ffi.sizeOf<_ObjCBlock>()); + b.ref.isa = _objc_concrete_global_block1; + b.ref.flags = 0; + b.ref.reserved = 0; + b.ref.invoke = invoke; + b.ref.target = target; + b.ref.descriptor = _objc_block_desc1; + final copy = _Block_copy(b.cast()).cast<_ObjCBlock>(); + pkg_ffi.calloc.free(b); + return copy; + } + + ffi.Pointer _Block_copy( + ffi.Pointer value, + ) { + return __Block_copy( + value, + ); + } + + late final __Block_copyPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('_Block_copy'); + late final __Block_copy = __Block_copyPtr + .asFunction Function(ffi.Pointer)>(); + + void _Block_release( + ffi.Pointer value, + ) { + return __Block_release( + value, + ); + } + + late final __Block_releasePtr = + _lookup)>>( + '_Block_release'); + late final __Block_release = + __Block_releasePtr.asFunction)>(); + + late final _objc_releaseFinalizer11 = + ffi.NativeFinalizer(__Block_releasePtr.cast()); + late final _sel_registerWithRequest_onSuccess_onError_1 = + _registerName1("registerWithRequest:onSuccess:onError:"); + void _objc_msgSend_37( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer request, + ffi.Pointer<_ObjCBlock> onSuccess, + ffi.Pointer<_ObjCBlock> onError, + ) { + return __objc_msgSend_37( + obj, + sel, + request, + onSuccess, + onError, + ); + } + + late final __objc_msgSend_37Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer<_ObjCBlock>, + ffi.Pointer<_ObjCBlock>)>>('objc_msgSend'); + late final __objc_msgSend_37 = __objc_msgSend_37Ptr.asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer<_ObjCBlock>, + ffi.Pointer<_ObjCBlock>)>(); + + late final _sel_authenticateWithRequest_onSuccess_onError_1 = + _registerName1("authenticateWithRequest:onSuccess:onError:"); + late final _sel_freePointer_1 = _registerName1("freePointer:"); + void _objc_msgSend_38( + ffi.Pointer obj, + ffi.Pointer sel, + ffi.Pointer ptr, + ) { + return __objc_msgSend_38( + obj, + sel, + ptr, + ); + } + + late final __objc_msgSend_38Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('objc_msgSend'); + late final __objc_msgSend_38 = __objc_msgSend_38Ptr.asFunction< + void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); +} + +class _ObjCWrapper implements ffi.Finalizable { + final ffi.Pointer _id; + final CelestAuthDarwin _lib; + bool _pendingRelease; + + _ObjCWrapper._(this._id, this._lib, + {bool retain = false, bool release = false}) + : _pendingRelease = release { + if (retain) { + _lib._objc_retain(_id.cast()); + } + if (release) { + _lib._objc_releaseFinalizer2.attach(this, _id.cast(), detach: this); + } + } + + /// Releases the reference to the underlying ObjC object held by this wrapper. + /// Throws a StateError if this wrapper doesn't currently hold a reference. + void release() { + if (_pendingRelease) { + _pendingRelease = false; + _lib._objc_release(_id.cast()); + _lib._objc_releaseFinalizer2.detach(this); + } else { + throw StateError( + 'Released an ObjC object that was unowned or already released.'); + } + } + + @override + bool operator ==(Object other) { + return other is _ObjCWrapper && _id == other._id; + } + + @override + int get hashCode => _id.hashCode; + + /// Return a pointer to this object. + ffi.Pointer get pointer => _id; + + ffi.Pointer _retainAndReturnId() { + _lib._objc_retain(_id.cast()); + return _id; + } +} + +class CelestAuth extends NSObject { + CelestAuth._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [CelestAuth] that points to the same underlying object as [other]. + static CelestAuth castFrom(T other) { + return CelestAuth._(other._id, other._lib, retain: true, release: true); + } + + /// Returns a [CelestAuth] that wraps the given raw object pointer. + static CelestAuth castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return CelestAuth._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [CelestAuth]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0( + obj._id, obj._lib._sel_isKindOfClass_1, obj._lib._class_CelestAuth1); + } + + @override + CelestAuth init() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1); + return CelestAuth._(_ret, _lib, retain: true, release: true); + } + + bool get isPasskeysSupported { + return _lib._objc_msgSend_12(_id, _lib._sel_isPasskeysSupported1); + } + + void registerWithRequest_onSuccess_onError_( + NSString request, + ObjCBlock_ffiVoid_Uint8 onSuccess, + ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8 onError) { + _lib._objc_msgSend_37( + _id, + _lib._sel_registerWithRequest_onSuccess_onError_1, + request._id, + onSuccess._id, + onError._id); + } + + void authenticateWithRequest_onSuccess_onError_( + NSString request, + ObjCBlock_ffiVoid_Uint8 onSuccess, + ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8 onError) { + _lib._objc_msgSend_37( + _id, + _lib._sel_authenticateWithRequest_onSuccess_onError_1, + request._id, + onSuccess._id, + onError._id); + } + + void freePointer_(ffi.Pointer ptr) { + _lib._objc_msgSend_38(_id, _lib._sel_freePointer_1, ptr); + } + + static CelestAuth new1(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_CelestAuth1, _lib._sel_new1); + return CelestAuth._(_ret, _lib, retain: false, release: true); + } + + static CelestAuth allocWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_CelestAuth1, _lib._sel_allocWithZone_1, zone); + return CelestAuth._(_ret, _lib, retain: false, release: true); + } + + static CelestAuth alloc(CelestAuthDarwin _lib) { + final _ret = + _lib._objc_msgSend_2(_lib._class_CelestAuth1, _lib._sel_alloc1); + return CelestAuth._(_ret, _lib, retain: false, release: true); + } +} + +final class ObjCSel extends ffi.Opaque {} + +final class ObjCObject extends ffi.Opaque {} + +class NSObject extends _ObjCWrapper { + NSObject._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [NSObject] that points to the same underlying object as [other]. + static NSObject castFrom(T other) { + return NSObject._(other._id, other._lib, retain: true, release: true); + } + + /// Returns a [NSObject] that wraps the given raw object pointer. + static NSObject castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return NSObject._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [NSObject]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0( + obj._id, obj._lib._sel_isKindOfClass_1, obj._lib._class_NSObject1); + } + + static void load(CelestAuthDarwin _lib) { + _lib._objc_msgSend_1(_lib._class_NSObject1, _lib._sel_load1); + } + + static void initialize(CelestAuthDarwin _lib) { + _lib._objc_msgSend_1(_lib._class_NSObject1, _lib._sel_initialize1); + } + + NSObject init() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1); + return NSObject._(_ret, _lib, retain: true, release: true); + } + + static NSObject new1(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSObject1, _lib._sel_new1); + return NSObject._(_ret, _lib, retain: false, release: true); + } + + static NSObject allocWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSObject1, _lib._sel_allocWithZone_1, zone); + return NSObject._(_ret, _lib, retain: false, release: true); + } + + static NSObject alloc(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSObject1, _lib._sel_alloc1); + return NSObject._(_ret, _lib, retain: false, release: true); + } + + void dealloc() { + _lib._objc_msgSend_1(_id, _lib._sel_dealloc1); + } + + void finalize() { + _lib._objc_msgSend_1(_id, _lib._sel_finalize1); + } + + NSObject copy() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_copy1); + return NSObject._(_ret, _lib, retain: false, release: true); + } + + NSObject mutableCopy() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_mutableCopy1); + return NSObject._(_ret, _lib, retain: false, release: true); + } + + static NSObject copyWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSObject1, _lib._sel_copyWithZone_1, zone); + return NSObject._(_ret, _lib, retain: false, release: true); + } + + static NSObject mutableCopyWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSObject1, _lib._sel_mutableCopyWithZone_1, zone); + return NSObject._(_ret, _lib, retain: false, release: true); + } + + static bool instancesRespondToSelector_( + CelestAuthDarwin _lib, ffi.Pointer aSelector) { + return _lib._objc_msgSend_4(_lib._class_NSObject1, + _lib._sel_instancesRespondToSelector_1, aSelector); + } + + static bool conformsToProtocol_(CelestAuthDarwin _lib, Protocol protocol) { + return _lib._objc_msgSend_5( + _lib._class_NSObject1, _lib._sel_conformsToProtocol_1, protocol._id); + } + + ffi.Pointer> methodForSelector_( + ffi.Pointer aSelector) { + return _lib._objc_msgSend_6(_id, _lib._sel_methodForSelector_1, aSelector); + } + + static ffi.Pointer> + instanceMethodForSelector_( + CelestAuthDarwin _lib, ffi.Pointer aSelector) { + return _lib._objc_msgSend_6(_lib._class_NSObject1, + _lib._sel_instanceMethodForSelector_1, aSelector); + } + + void doesNotRecognizeSelector_(ffi.Pointer aSelector) { + _lib._objc_msgSend_7(_id, _lib._sel_doesNotRecognizeSelector_1, aSelector); + } + + NSObject forwardingTargetForSelector_(ffi.Pointer aSelector) { + final _ret = _lib._objc_msgSend_8( + _id, _lib._sel_forwardingTargetForSelector_1, aSelector); + return NSObject._(_ret, _lib, retain: true, release: true); + } + + void forwardInvocation_(NSInvocation anInvocation) { + _lib._objc_msgSend_24(_id, _lib._sel_forwardInvocation_1, anInvocation._id); + } + + NSMethodSignature methodSignatureForSelector_( + ffi.Pointer aSelector) { + final _ret = _lib._objc_msgSend_25( + _id, _lib._sel_methodSignatureForSelector_1, aSelector); + return NSMethodSignature._(_ret, _lib, retain: true, release: true); + } + + static NSMethodSignature instanceMethodSignatureForSelector_( + CelestAuthDarwin _lib, ffi.Pointer aSelector) { + final _ret = _lib._objc_msgSend_25(_lib._class_NSObject1, + _lib._sel_instanceMethodSignatureForSelector_1, aSelector); + return NSMethodSignature._(_ret, _lib, retain: true, release: true); + } + + bool allowsWeakReference() { + return _lib._objc_msgSend_12(_id, _lib._sel_allowsWeakReference1); + } + + bool retainWeakReference() { + return _lib._objc_msgSend_12(_id, _lib._sel_retainWeakReference1); + } + + static bool isSubclassOfClass_(CelestAuthDarwin _lib, NSObject aClass) { + return _lib._objc_msgSend_0( + _lib._class_NSObject1, _lib._sel_isSubclassOfClass_1, aClass._id); + } + + static bool resolveClassMethod_( + CelestAuthDarwin _lib, ffi.Pointer sel) { + return _lib._objc_msgSend_4( + _lib._class_NSObject1, _lib._sel_resolveClassMethod_1, sel); + } + + static bool resolveInstanceMethod_( + CelestAuthDarwin _lib, ffi.Pointer sel) { + return _lib._objc_msgSend_4( + _lib._class_NSObject1, _lib._sel_resolveInstanceMethod_1, sel); + } + + static int hash(CelestAuthDarwin _lib) { + return _lib._objc_msgSend_10(_lib._class_NSObject1, _lib._sel_hash1); + } + + static NSObject superclass(CelestAuthDarwin _lib) { + final _ret = + _lib._objc_msgSend_2(_lib._class_NSObject1, _lib._sel_superclass1); + return NSObject._(_ret, _lib, retain: true, release: true); + } + + static NSObject class1(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSObject1, _lib._sel_class1); + return NSObject._(_ret, _lib, retain: true, release: true); + } + + static NSString description(CelestAuthDarwin _lib) { + final _ret = + _lib._objc_msgSend_36(_lib._class_NSObject1, _lib._sel_description1); + return NSString._(_ret, _lib, retain: true, release: true); + } + + static NSString debugDescription(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_36( + _lib._class_NSObject1, _lib._sel_debugDescription1); + return NSString._(_ret, _lib, retain: true, release: true); + } +} + +typedef instancetype = ffi.Pointer; +typedef Dartinstancetype = NSObject; + +class Protocol extends _ObjCWrapper { + Protocol._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [Protocol] that points to the same underlying object as [other]. + static Protocol castFrom(T other) { + return Protocol._(other._id, other._lib, retain: true, release: true); + } + + /// Returns a [Protocol] that wraps the given raw object pointer. + static Protocol castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return Protocol._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [Protocol]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0( + obj._id, obj._lib._sel_isKindOfClass_1, obj._lib._class_Protocol1); + } +} + +class NSInvocation extends NSObject { + NSInvocation._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [NSInvocation] that points to the same underlying object as [other]. + static NSInvocation castFrom(T other) { + return NSInvocation._(other._id, other._lib, retain: true, release: true); + } + + /// Returns a [NSInvocation] that wraps the given raw object pointer. + static NSInvocation castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return NSInvocation._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [NSInvocation]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0( + obj._id, obj._lib._sel_isKindOfClass_1, obj._lib._class_NSInvocation1); + } + + static NSInvocation invocationWithMethodSignature_( + CelestAuthDarwin _lib, NSMethodSignature sig) { + final _ret = _lib._objc_msgSend_14(_lib._class_NSInvocation1, + _lib._sel_invocationWithMethodSignature_1, sig._id); + return NSInvocation._(_ret, _lib, retain: true, release: true); + } + + NSMethodSignature get methodSignature { + final _ret = _lib._objc_msgSend_15(_id, _lib._sel_methodSignature1); + return NSMethodSignature._(_ret, _lib, retain: true, release: true); + } + + void retainArguments() { + _lib._objc_msgSend_1(_id, _lib._sel_retainArguments1); + } + + bool get argumentsRetained { + return _lib._objc_msgSend_12(_id, _lib._sel_argumentsRetained1); + } + + NSObject? get target { + final _ret = _lib._objc_msgSend_16(_id, _lib._sel_target1); + return _ret.address == 0 + ? null + : NSObject._(_ret, _lib, retain: true, release: true); + } + + set target(NSObject? value) { + return _lib._objc_msgSend_17( + _id, _lib._sel_setTarget_1, value?._id ?? ffi.nullptr); + } + + ffi.Pointer get selector { + return _lib._objc_msgSend_18(_id, _lib._sel_selector1); + } + + set selector(ffi.Pointer value) { + return _lib._objc_msgSend_19(_id, _lib._sel_setSelector_1, value); + } + + void getReturnValue_(ffi.Pointer retLoc) { + _lib._objc_msgSend_20(_id, _lib._sel_getReturnValue_1, retLoc); + } + + void setReturnValue_(ffi.Pointer retLoc) { + _lib._objc_msgSend_20(_id, _lib._sel_setReturnValue_1, retLoc); + } + + void getArgument_atIndex_(ffi.Pointer argumentLocation, int idx) { + _lib._objc_msgSend_21( + _id, _lib._sel_getArgument_atIndex_1, argumentLocation, idx); + } + + void setArgument_atIndex_(ffi.Pointer argumentLocation, int idx) { + _lib._objc_msgSend_21( + _id, _lib._sel_setArgument_atIndex_1, argumentLocation, idx); + } + + void invoke() { + _lib._objc_msgSend_1(_id, _lib._sel_invoke1); + } + + void invokeWithTarget_(NSObject target) { + _lib._objc_msgSend_22(_id, _lib._sel_invokeWithTarget_1, target._id); + } + + void invokeUsingIMP_( + ffi.Pointer> imp) { + _lib._objc_msgSend_23(_id, _lib._sel_invokeUsingIMP_1, imp); + } + + @override + NSInvocation init() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1); + return NSInvocation._(_ret, _lib, retain: true, release: true); + } + + static NSInvocation new1(CelestAuthDarwin _lib) { + final _ret = + _lib._objc_msgSend_2(_lib._class_NSInvocation1, _lib._sel_new1); + return NSInvocation._(_ret, _lib, retain: false, release: true); + } + + static NSInvocation allocWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSInvocation1, _lib._sel_allocWithZone_1, zone); + return NSInvocation._(_ret, _lib, retain: false, release: true); + } + + static NSInvocation alloc(CelestAuthDarwin _lib) { + final _ret = + _lib._objc_msgSend_2(_lib._class_NSInvocation1, _lib._sel_alloc1); + return NSInvocation._(_ret, _lib, retain: false, release: true); + } +} + +class NSMethodSignature extends NSObject { + NSMethodSignature._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [NSMethodSignature] that points to the same underlying object as [other]. + static NSMethodSignature castFrom(T other) { + return NSMethodSignature._(other._id, other._lib, + retain: true, release: true); + } + + /// Returns a [NSMethodSignature] that wraps the given raw object pointer. + static NSMethodSignature castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return NSMethodSignature._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [NSMethodSignature]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0(obj._id, obj._lib._sel_isKindOfClass_1, + obj._lib._class_NSMethodSignature1); + } + + static NSMethodSignature? signatureWithObjCTypes_( + CelestAuthDarwin _lib, ffi.Pointer types) { + final _ret = _lib._objc_msgSend_9(_lib._class_NSMethodSignature1, + _lib._sel_signatureWithObjCTypes_1, types); + return _ret.address == 0 + ? null + : NSMethodSignature._(_ret, _lib, retain: true, release: true); + } + + int get numberOfArguments { + return _lib._objc_msgSend_10(_id, _lib._sel_numberOfArguments1); + } + + ffi.Pointer getArgumentTypeAtIndex_(int idx) { + return _lib._objc_msgSend_11(_id, _lib._sel_getArgumentTypeAtIndex_1, idx); + } + + int get frameLength { + return _lib._objc_msgSend_10(_id, _lib._sel_frameLength1); + } + + bool isOneway() { + return _lib._objc_msgSend_12(_id, _lib._sel_isOneway1); + } + + ffi.Pointer get methodReturnType { + return _lib._objc_msgSend_13(_id, _lib._sel_methodReturnType1); + } + + int get methodReturnLength { + return _lib._objc_msgSend_10(_id, _lib._sel_methodReturnLength1); + } + + @override + NSMethodSignature init() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1); + return NSMethodSignature._(_ret, _lib, retain: true, release: true); + } + + static NSMethodSignature new1(CelestAuthDarwin _lib) { + final _ret = + _lib._objc_msgSend_2(_lib._class_NSMethodSignature1, _lib._sel_new1); + return NSMethodSignature._(_ret, _lib, retain: false, release: true); + } + + static NSMethodSignature allocWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSMethodSignature1, _lib._sel_allocWithZone_1, zone); + return NSMethodSignature._(_ret, _lib, retain: false, release: true); + } + + static NSMethodSignature alloc(CelestAuthDarwin _lib) { + final _ret = + _lib._objc_msgSend_2(_lib._class_NSMethodSignature1, _lib._sel_alloc1); + return NSMethodSignature._(_ret, _lib, retain: false, release: true); + } +} + +class NSString extends NSObject { + NSString._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [NSString] that points to the same underlying object as [other]. + static NSString castFrom(T other) { + return NSString._(other._id, other._lib, retain: true, release: true); + } + + /// Returns a [NSString] that wraps the given raw object pointer. + static NSString castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return NSString._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [NSString]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0( + obj._id, obj._lib._sel_isKindOfClass_1, obj._lib._class_NSString1); + } + + factory NSString(CelestAuthDarwin _lib, String str) { + final cstr = str.toNativeUtf16(); + final nsstr = stringWithCharacters_length_(_lib, cstr.cast(), str.length); + pkg_ffi.calloc.free(cstr); + return nsstr; + } + + @override + String toString() { + final data = + dataUsingEncoding_(0x94000100 /* NSUTF16LittleEndianStringEncoding */); + return data!.bytes.cast().toDartString(length: length); + } + + int get length { + return _lib._objc_msgSend_10(_id, _lib._sel_length1); + } + + int characterAtIndex_(int index) { + return _lib._objc_msgSend_26(_id, _lib._sel_characterAtIndex_1, index); + } + + @override + NSString init() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1); + return NSString._(_ret, _lib, retain: true, release: true); + } + + NSString? initWithCoder_(NSCoder coder) { + final _ret = + _lib._objc_msgSend_33(_id, _lib._sel_initWithCoder_1, coder._id); + return _ret.address == 0 + ? null + : NSString._(_ret, _lib, retain: true, release: true); + } + + static NSString stringWithCharacters_length_( + CelestAuthDarwin _lib, ffi.Pointer characters, int length) { + final _ret = _lib._objc_msgSend_34(_lib._class_NSString1, + _lib._sel_stringWithCharacters_length_1, characters, length); + return NSString._(_ret, _lib, retain: true, release: true); + } + + NSData dataUsingEncoding_(int encoding) { + final _ret = + _lib._objc_msgSend_35(_id, _lib._sel_dataUsingEncoding_1, encoding); + return NSData._(_ret, _lib, retain: true, release: true); + } + + static NSString new1(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSString1, _lib._sel_new1); + return NSString._(_ret, _lib, retain: false, release: true); + } + + static NSString allocWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSString1, _lib._sel_allocWithZone_1, zone); + return NSString._(_ret, _lib, retain: false, release: true); + } + + static NSString alloc(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSString1, _lib._sel_alloc1); + return NSString._(_ret, _lib, retain: false, release: true); + } +} + +extension StringToNSString on String { + NSString toNSString(CelestAuthDarwin lib) => NSString(lib, this); +} + +class NSCoder extends NSObject { + NSCoder._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [NSCoder] that points to the same underlying object as [other]. + static NSCoder castFrom(T other) { + return NSCoder._(other._id, other._lib, retain: true, release: true); + } + + /// Returns a [NSCoder] that wraps the given raw object pointer. + static NSCoder castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return NSCoder._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [NSCoder]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0( + obj._id, obj._lib._sel_isKindOfClass_1, obj._lib._class_NSCoder1); + } + + void encodeValueOfObjCType_at_( + ffi.Pointer type, ffi.Pointer addr) { + _lib._objc_msgSend_27( + _id, _lib._sel_encodeValueOfObjCType_at_1, type, addr); + } + + void encodeDataObject_(NSData data) { + _lib._objc_msgSend_29(_id, _lib._sel_encodeDataObject_1, data._id); + } + + NSData? decodeDataObject() { + final _ret = _lib._objc_msgSend_30(_id, _lib._sel_decodeDataObject1); + return _ret.address == 0 + ? null + : NSData._(_ret, _lib, retain: true, release: true); + } + + void decodeValueOfObjCType_at_size_( + ffi.Pointer type, ffi.Pointer data, int size) { + _lib._objc_msgSend_31( + _id, _lib._sel_decodeValueOfObjCType_at_size_1, type, data, size); + } + + int versionForClassName_(NSString className) { + return _lib._objc_msgSend_32( + _id, _lib._sel_versionForClassName_1, className._id); + } + + @override + NSCoder init() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1); + return NSCoder._(_ret, _lib, retain: true, release: true); + } + + static NSCoder new1(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSCoder1, _lib._sel_new1); + return NSCoder._(_ret, _lib, retain: false, release: true); + } + + static NSCoder allocWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSCoder1, _lib._sel_allocWithZone_1, zone); + return NSCoder._(_ret, _lib, retain: false, release: true); + } + + static NSCoder alloc(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSCoder1, _lib._sel_alloc1); + return NSCoder._(_ret, _lib, retain: false, release: true); + } +} + +class NSData extends NSObject { + NSData._(ffi.Pointer id, CelestAuthDarwin lib, + {bool retain = false, bool release = false}) + : super._(id, lib, retain: retain, release: release); + + /// Returns a [NSData] that points to the same underlying object as [other]. + static NSData castFrom(T other) { + return NSData._(other._id, other._lib, retain: true, release: true); + } + + /// Returns a [NSData] that wraps the given raw object pointer. + static NSData castFromPointer( + CelestAuthDarwin lib, ffi.Pointer other, + {bool retain = false, bool release = false}) { + return NSData._(other, lib, retain: retain, release: release); + } + + /// Returns whether [obj] is an instance of [NSData]. + static bool isInstance(_ObjCWrapper obj) { + return obj._lib._objc_msgSend_0( + obj._id, obj._lib._sel_isKindOfClass_1, obj._lib._class_NSData1); + } + + int get length { + return _lib._objc_msgSend_10(_id, _lib._sel_length1); + } + + ffi.Pointer get bytes { + return _lib._objc_msgSend_28(_id, _lib._sel_bytes1); + } + + @override + NSData init() { + final _ret = _lib._objc_msgSend_2(_id, _lib._sel_init1); + return NSData._(_ret, _lib, retain: true, release: true); + } + + static NSData new1(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSData1, _lib._sel_new1); + return NSData._(_ret, _lib, retain: false, release: true); + } + + static NSData allocWithZone_( + CelestAuthDarwin _lib, ffi.Pointer zone) { + final _ret = _lib._objc_msgSend_3( + _lib._class_NSData1, _lib._sel_allocWithZone_1, zone); + return NSData._(_ret, _lib, retain: false, release: true); + } + + static NSData alloc(CelestAuthDarwin _lib) { + final _ret = _lib._objc_msgSend_2(_lib._class_NSData1, _lib._sel_alloc1); + return NSData._(_ret, _lib, retain: false, release: true); + } +} + +class _ObjCBlockBase implements ffi.Finalizable { + final ffi.Pointer<_ObjCBlock> _id; + final CelestAuthDarwin _lib; + bool _pendingRelease; + + _ObjCBlockBase._(this._id, this._lib, + {bool retain = false, bool release = false}) + : _pendingRelease = release { + if (retain) { + _lib._Block_copy(_id.cast()); + } + if (release) { + _lib._objc_releaseFinalizer11.attach(this, _id.cast(), detach: this); + } + } + + /// Releases the reference to the underlying ObjC block held by this wrapper. + /// Throws a StateError if this wrapper doesn't currently hold a reference. + void release() { + if (_pendingRelease) { + _pendingRelease = false; + _lib._Block_release(_id.cast()); + _lib._objc_releaseFinalizer11.detach(this); + } else { + throw StateError( + 'Released an ObjC block that was unowned or already released.'); + } + } + + @override + bool operator ==(Object other) { + return other is _ObjCBlockBase && _id == other._id; + } + + @override + int get hashCode => _id.hashCode; + + /// Return a pointer to this object. + ffi.Pointer<_ObjCBlock> get pointer => _id; + + ffi.Pointer<_ObjCBlock> _retainAndReturnId() { + _lib._Block_copy(_id.cast()); + return _id; + } +} + +void _ObjCBlock_ffiVoid_Uint8_fnPtrTrampoline( + ffi.Pointer<_ObjCBlock> block, ffi.Pointer arg0) => + block.ref.target + .cast< + ffi + .NativeFunction arg0)>>() + .asFunction)>()(arg0); +final _ObjCBlock_ffiVoid_Uint8_closureRegistry = + )>{}; +int _ObjCBlock_ffiVoid_Uint8_closureRegistryIndex = 0; +ffi.Pointer _ObjCBlock_ffiVoid_Uint8_registerClosure( + void Function(ffi.Pointer) fn) { + final id = ++_ObjCBlock_ffiVoid_Uint8_closureRegistryIndex; + _ObjCBlock_ffiVoid_Uint8_closureRegistry[id] = fn; + return ffi.Pointer.fromAddress(id); +} + +void _ObjCBlock_ffiVoid_Uint8_closureTrampoline( + ffi.Pointer<_ObjCBlock> block, ffi.Pointer arg0) => + _ObjCBlock_ffiVoid_Uint8_closureRegistry[block.ref.target.address]!(arg0); + +class ObjCBlock_ffiVoid_Uint8 extends _ObjCBlockBase { + ObjCBlock_ffiVoid_Uint8._(ffi.Pointer<_ObjCBlock> id, CelestAuthDarwin lib, + {bool retain = false, bool release = true}) + : super._(id, lib, retain: retain, release: release); + + /// Creates a block from a C function pointer. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + ObjCBlock_ffiVoid_Uint8.fromFunctionPointer( + CelestAuthDarwin lib, + ffi.Pointer< + ffi + .NativeFunction arg0)>> + ptr) + : this._( + lib._newBlock1( + _cFuncTrampoline ??= ffi.Pointer.fromFunction< + ffi.Void Function(ffi.Pointer<_ObjCBlock>, + ffi.Pointer)>( + _ObjCBlock_ffiVoid_Uint8_fnPtrTrampoline) + .cast(), + ptr.cast()), + lib); + static ffi.Pointer? _cFuncTrampoline; + + /// Creates a block from a Dart function. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + ObjCBlock_ffiVoid_Uint8.fromFunction( + CelestAuthDarwin lib, void Function(ffi.Pointer) fn) + : this._( + lib._newBlock1( + _dartFuncTrampoline ??= ffi.Pointer.fromFunction< + ffi.Void Function(ffi.Pointer<_ObjCBlock>, + ffi.Pointer)>( + _ObjCBlock_ffiVoid_Uint8_closureTrampoline) + .cast(), + _ObjCBlock_ffiVoid_Uint8_registerClosure( + (ffi.Pointer arg0) => fn(arg0))), + lib); + static ffi.Pointer? _dartFuncTrampoline; + + /// Creates a listener block from a Dart function. + /// + /// This is based on FFI's NativeCallable.listener, and has the same + /// capabilities and limitations. This block can be invoked from any thread, + /// but only supports void functions, and is not run synchronously. See + /// NativeCallable.listener for more details. + /// + /// Note that unlike the default behavior of NativeCallable.listener, listener + /// blocks do not keep the isolate alive. + ObjCBlock_ffiVoid_Uint8.listener( + CelestAuthDarwin lib, void Function(ffi.Pointer) fn) + : this._( + lib._newBlock1( + (_dartFuncListenerTrampoline ??= ffi.NativeCallable< + ffi.Void Function(ffi.Pointer<_ObjCBlock>, + ffi.Pointer)>.listener( + _ObjCBlock_ffiVoid_Uint8_closureTrampoline) + ..keepIsolateAlive = false) + .nativeFunction + .cast(), + _ObjCBlock_ffiVoid_Uint8_registerClosure( + (ffi.Pointer arg0) => fn(arg0))), + lib); + static ffi.NativeCallable< + ffi.Void Function(ffi.Pointer<_ObjCBlock>, ffi.Pointer)>? + _dartFuncListenerTrampoline; + + void call(ffi.Pointer arg0) => _id.ref.invoke + .cast< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer<_ObjCBlock> block, + ffi.Pointer arg0)>>() + .asFunction< + void Function( + ffi.Pointer<_ObjCBlock>, ffi.Pointer)>()(_id, arg0); +} + +final class _ObjCBlockDesc extends ffi.Struct { + @ffi.UnsignedLong() + external int reserved; + + @ffi.UnsignedLong() + external int size; + + external ffi.Pointer copy_helper; + + external ffi.Pointer dispose_helper; + + external ffi.Pointer signature; +} + +final class _ObjCBlock extends ffi.Struct { + external ffi.Pointer isa; + + @ffi.Int() + external int flags; + + @ffi.Int() + external int reserved; + + external ffi.Pointer invoke; + + external ffi.Pointer<_ObjCBlockDesc> descriptor; + + external ffi.Pointer target; +} + +void _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_fnPtrTrampoline( + ffi.Pointer<_ObjCBlock> block, int arg0, ffi.Pointer arg1) => + block.ref.target + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int32 arg0, ffi.Pointer arg1)>>() + .asFunction)>()(arg0, arg1); +final _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureRegistry = + )>{}; +int _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureRegistryIndex = 0; +ffi.Pointer + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_registerClosure( + void Function(int, ffi.Pointer) fn) { + final id = + ++_ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureRegistryIndex; + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureRegistry[id] = fn; + return ffi.Pointer.fromAddress(id); +} + +void _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureTrampoline( + ffi.Pointer<_ObjCBlock> block, int arg0, ffi.Pointer arg1) => + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureRegistry[ + block.ref.target.address]!(arg0, arg1); + +class ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8 extends _ObjCBlockBase { + ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8._( + ffi.Pointer<_ObjCBlock> id, CelestAuthDarwin lib, + {bool retain = false, bool release = true}) + : super._(id, lib, retain: retain, release: release); + + /// Creates a block from a C function pointer. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8.fromFunctionPointer( + CelestAuthDarwin lib, + ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int32 arg0, ffi.Pointer arg1)>> + ptr) + : this._( + lib._newBlock1( + _cFuncTrampoline ??= ffi.Pointer.fromFunction< + ffi.Void Function(ffi.Pointer<_ObjCBlock>, + ffi.Int32, ffi.Pointer)>( + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_fnPtrTrampoline) + .cast(), + ptr.cast()), + lib); + static ffi.Pointer? _cFuncTrampoline; + + /// Creates a block from a Dart function. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8.fromFunction( + CelestAuthDarwin lib, void Function(int, ffi.Pointer) fn) + : this._( + lib._newBlock1( + _dartFuncTrampoline ??= ffi.Pointer.fromFunction< + ffi.Void Function(ffi.Pointer<_ObjCBlock>, + ffi.Int32, ffi.Pointer)>( + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureTrampoline) + .cast(), + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_registerClosure( + (int arg0, ffi.Pointer arg1) => fn(arg0, arg1))), + lib); + static ffi.Pointer? _dartFuncTrampoline; + + /// Creates a listener block from a Dart function. + /// + /// This is based on FFI's NativeCallable.listener, and has the same + /// capabilities and limitations. This block can be invoked from any thread, + /// but only supports void functions, and is not run synchronously. See + /// NativeCallable.listener for more details. + /// + /// Note that unlike the default behavior of NativeCallable.listener, listener + /// blocks do not keep the isolate alive. + ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8.listener( + CelestAuthDarwin lib, void Function(int, ffi.Pointer) fn) + : this._( + lib._newBlock1( + (_dartFuncListenerTrampoline ??= ffi.NativeCallable< + ffi.Void Function(ffi.Pointer<_ObjCBlock>, + ffi.Int32, ffi.Pointer)>.listener( + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_closureTrampoline) + ..keepIsolateAlive = false) + .nativeFunction + .cast(), + _ObjCBlock_ffiVoid_CelestAuthErrorCode_Uint8_registerClosure( + (int arg0, ffi.Pointer arg1) => fn(arg0, arg1))), + lib); + static ffi.NativeCallable< + ffi.Void Function( + ffi.Pointer<_ObjCBlock>, ffi.Int32, ffi.Pointer)>? + _dartFuncListenerTrampoline; + + void call(int arg0, ffi.Pointer arg1) => _id.ref.invoke + .cast< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer<_ObjCBlock> block, ffi.Int32 arg0, + ffi.Pointer arg1)>>() + .asFunction< + void Function(ffi.Pointer<_ObjCBlock>, int, + ffi.Pointer)>()(_id, arg0, arg1); +} + +abstract class CelestAuthErrorCode { + static const int CelestAuthErrorCodeUnknown = 0; + static const int CelestAuthErrorCodeUnsupported = 1; + static const int CelestAuthErrorCodeSerde = 2; +} diff --git a/packages/celest_auth/lib/src/platform/darwin/foundation.yaml b/packages/celest_auth/lib/src/platform/darwin/foundation.yaml index 1ed2416a..d9d065a0 100644 --- a/packages/celest_auth/lib/src/platform/darwin/foundation.yaml +++ b/packages/celest_auth/lib/src/platform/darwin/foundation.yaml @@ -68,13 +68,13 @@ files: name: NSValue c:objc(cs)Protocol: name: Protocol - "objcBlock: 1wxmkb 1wxmkb* 4cylau": - name: ObjCBlock_ffiVoid_ffiVoid_ffiUnsignedLong - "objcBlock: 1wxmkb 71xoam bool*": + "objcBlock: 6jqrbq*? 4okcll 7e5sfs": + name: ObjCBlock_ObjCObject_NSError_NSString + "objcBlock: 837566 7e5sfs bool*": name: ObjCBlock_ffiVoid_NSString_bool - "objcBlock: 1wxmkb 71xoam? fju8rp fju8rp bool*": + "objcBlock: 837566 7e5sfs? 6vbbr9 6vbbr9 bool*": name: ObjCBlock_ffiVoid_NSString_NSRange_NSRange_bool - "objcBlock: 1wxmkb fk756t* 4cylau": + "objcBlock: 837566 837566* 9z3bt8": + name: ObjCBlock_ffiVoid_ffiVoid_ffiUnsignedLong + "objcBlock: 837566 9ufx6z* 9z3bt8": name: ObjCBlock_ffiVoid_ffiUnsignedShort_ffiUnsignedLong - "objcBlock: 3e9m14*? 37mnn9 71xoam": - name: ObjCBlock_ObjCObject_NSError_NSString diff --git a/packages/celest_auth/pubspec.yaml b/packages/celest_auth/pubspec.yaml index 27532bd8..542f5cb5 100644 --- a/packages/celest_auth/pubspec.yaml +++ b/packages/celest_auth/pubspec.yaml @@ -37,3 +37,9 @@ flutter: platforms: android: ffiPlugin: true + ios: + ffiPlugin: true + sharedDarwinSource: true + macos: + ffiPlugin: true + sharedDarwinSource: true diff --git a/packages/celest_core/lib/celest_core.dart b/packages/celest_core/lib/celest_core.dart index f8f4d967..93d91421 100644 --- a/packages/celest_core/lib/celest_core.dart +++ b/packages/celest_core/lib/celest_core.dart @@ -2,7 +2,9 @@ library; /// Auth +export 'src/auth/auth_client.dart'; export 'src/auth/auth_protocol.dart'; +export 'src/auth/passkey_exception.dart'; export 'src/auth/passkey_types.dart'; /// Exceptions diff --git a/packages/celest_core/lib/src/auth/auth_client.dart b/packages/celest_core/lib/src/auth/auth_client.dart new file mode 100644 index 00000000..9e0e3828 --- /dev/null +++ b/packages/celest_core/lib/src/auth/auth_client.dart @@ -0,0 +1,97 @@ +import 'dart:convert'; + +import 'package:celest_core/celest_core.dart'; +import 'package:http/http.dart' as http; + +final class AuthClient implements AuthProtocol { + AuthClient({ + required this.baseUri, + http.Client? httpClient, + }) : _client = httpClient ?? http.Client(); + + final http.Client _client; + final Uri baseUri; + + @override + late final PasskeyClient passkeys = PasskeyClient( + baseUri: baseUri, + httpClient: _client, + ); +} + +final class PasskeyClient implements PasskeyProtocol { + PasskeyClient({ + required this.baseUri, + http.Client? httpClient, + }) : _client = httpClient ?? http.Client(); + + final http.Client _client; + final Uri baseUri; + + String? token; + + Future> _send( + String path, + Map json, + ) async { + final uri = baseUri.resolve(path); + final resp = await _client.post( + uri, + body: jsonEncode(json), + headers: { + 'content-type': 'application/json', + if (token case final token?) 'authorization': 'Bearer $token', + }, + ); + if (resp.statusCode != 200) { + throw http.ClientException( + '${resp.statusCode}: ${resp.body}', + uri, + ); + } + final body = jsonDecode(resp.body) as Map; + if (body['cork'] case final String cork) { + token = cork; + } + return body; + } + + @override + Future requestRegistration({ + required PasskeyRegistrationRequest request, + }) async { + final response = await _send('/_auth/passkeys/register', request.toJson()); + return PasskeyRegistrationOptions.fromJson(response); + } + + @override + Future verifyRegistration({ + required PasskeyRegistrationResponse registration, + }) async { + await _send( + '/_auth/passkeys/register/verify', + registration.toJson(), + ); + } + + @override + Future requestAuthentication({ + required PasskeyAuthenticationRequest request, + }) async { + final response = await _send( + '/_auth/passkeys/authenticate', + request.toJson(), + ); + return PasskeyAuthenticationOptions.fromJson(response); + } + + @override + Future verifyAuthentication({ + required PasskeyAuthenticationResponse authentication, + }) async { + await _send( + '/_auth/passkeys/authenticate/verify', + authentication.toJson(), + ); + } +} diff --git a/packages/celest_core/lib/src/auth/auth_protocol.dart b/packages/celest_core/lib/src/auth/auth_protocol.dart index 72a2ad9d..1a771c2b 100644 --- a/packages/celest_core/lib/src/auth/auth_protocol.dart +++ b/packages/celest_core/lib/src/auth/auth_protocol.dart @@ -9,6 +9,7 @@ abstract interface class PasskeyProtocol { required PasskeyRegistrationRequest request, }); + /// Verifies the registration response and returns the user auth token. Future verifyRegistration({ required PasskeyRegistrationResponse registration, }); @@ -17,6 +18,7 @@ abstract interface class PasskeyProtocol { required PasskeyAuthenticationRequest request, }); + /// Verifies the authentication response and returns the user auth token. Future verifyAuthentication({ required PasskeyAuthenticationResponse authentication, }); diff --git a/packages/celest_auth/lib/src/client/passkeys/passkey_exception.dart b/packages/celest_core/lib/src/auth/passkey_exception.dart similarity index 100% rename from packages/celest_auth/lib/src/client/passkeys/passkey_exception.dart rename to packages/celest_core/lib/src/auth/passkey_exception.dart diff --git a/packages/celest_core/lib/src/auth/passkey_types.dart b/packages/celest_core/lib/src/auth/passkey_types.dart index 3b1ecfe2..571b05b7 100644 --- a/packages/celest_core/lib/src/auth/passkey_types.dart +++ b/packages/celest_core/lib/src/auth/passkey_types.dart @@ -31,7 +31,8 @@ final class PasskeyRegistrationRequest { displayName: displayName, authenticatorSelection: json['authenticatorSelection'] != null ? AuthenticatorSelectionCriteria.fromJson( - json['authenticatorSelection'] as Map, + (json['authenticatorSelection'] as Map) + .cast(), ) : null, ); @@ -73,8 +74,9 @@ final class PasskeyRegistrationOptions { required this.userName, String? userDisplayName, this.timeout = const Duration(minutes: 5), - this.authenticatorSelection, - this.publicKeyCredentialParameters, + this.authenticatorSelection = + const AuthenticatorSelectionCriteria.passkey(), + this.publicKeyCredentialParameters = const [], }) : assert( _isValidDomain(rpId), 'Invalid rpId (must be a valid domain): $rpId', @@ -105,12 +107,14 @@ final class PasskeyRegistrationOptions { timeout: Duration(milliseconds: (json['timeout'] as num).toInt()), authenticatorSelection: json['authenticatorSelection'] != null ? AuthenticatorSelectionCriteria.fromJson( - json['authenticatorSelection'] as Map, + (json['authenticatorSelection'] as Map) + .cast(), ) - : null, - publicKeyCredentialParameters: (json['pubKeyCredParams'] as List?) - ?.cast>() - .map(PublicKeyCredentialParameter.fromJson) + : const AuthenticatorSelectionCriteria.passkey(), + publicKeyCredentialParameters: (json['pubKeyCredParams'] as List? ?? + const []) + .cast>() + .map((json) => PublicKeyCredentialParameter.fromJson(json.cast())) .toList(), ); } @@ -124,8 +128,8 @@ final class PasskeyRegistrationOptions { final String userDisplayName; final Uint8List challenge; final Duration timeout; - final AuthenticatorSelectionCriteria? authenticatorSelection; - final List? publicKeyCredentialParameters; + final AuthenticatorSelectionCriteria authenticatorSelection; + final List publicKeyCredentialParameters; Map toJson() => { 'rp': { @@ -139,12 +143,9 @@ final class PasskeyRegistrationOptions { }, 'challenge': base64RawUrl.encode(challenge), 'timeout': timeout.inMilliseconds, - if (authenticatorSelection case final authenticatorSelection?) - 'authenticatorSelection': authenticatorSelection.toJson(), - if (publicKeyCredentialParameters - case final publicKeyCredentialParameters?) - 'pubKeyCredParams': - publicKeyCredentialParameters.map((el) => el.toJson()).toList(), + 'authenticatorSelection': authenticatorSelection.toJson(), + 'pubKeyCredParams': + publicKeyCredentialParameters.map((el) => el.toJson()).toList(), }; @override @@ -158,11 +159,11 @@ final class PasskeyRegistrationOptions { ..writeln(' userDisplayName: $userDisplayName,') ..writeln(' challenge: ${base64RawUrl.encode(challenge)},') ..writeln(' timeout: $timeout,'); - if (authenticatorSelection case final authenticatorSelection?) { + if (authenticatorSelection case final authenticatorSelection) { buffer.writeln(' authenticatorSelection: $authenticatorSelection,'); } if (publicKeyCredentialParameters - case final publicKeyCredentialParameters?) { + case final publicKeyCredentialParameters) { buffer.writeln(' publicKeyCredentialParameters: ['); for (final el in publicKeyCredentialParameters) { buffer.writeln(' $el,'); @@ -178,7 +179,7 @@ final class PasskeyRegistrationResponse { const PasskeyRegistrationResponse({ required this.id, required this.rawId, - required this.clientData, + required this.clientDataJson, required this.attestationObject, this.transports, this.publicKeyAlgorithm, @@ -202,13 +203,9 @@ final class PasskeyRegistrationResponse { return PasskeyRegistrationResponse( id: id, rawId: base64RawUrl.decode(rawId), - clientData: PasskeyClientData.fromJson( - jsonDecode( - utf8.decode(base64RawUrl.decode(clientDataJson)), - ) as Map, - ), + clientDataJson: base64RawUrl.decode(clientDataJson), attestationObject: base64RawUrl.decode(attestationObject), - transports: response['transports'] as List?, + transports: (response['transports'] as List?)?.cast(), publicKeyAlgorithm: response['publicKeyAlgorithm'] != null ? COSEAlgorithmIdentifier._( response['publicKeyAlgorithm'] as int, @@ -233,7 +230,7 @@ final class PasskeyRegistrationResponse { /// The credential ID in its raw binary form. final Uint8List rawId; String get type => 'public-key'; - final PasskeyClientData clientData; + final Uint8List clientDataJson; final Uint8List attestationObject; final List? transports; final COSEAlgorithmIdentifier? publicKeyAlgorithm; @@ -249,11 +246,7 @@ final class PasskeyRegistrationResponse { 'rawId': base64RawUrl.encode(rawId), 'type': type, 'response': { - 'clientDataJSON': base64RawUrl.encode( - utf8.encode( - jsonEncode(clientData.toJson()), - ), - ), + 'clientDataJSON': base64RawUrl.encode(clientDataJson), 'attestationObject': base64RawUrl.encode(attestationObject), if (transports != null) 'transports': transports, if (publicKeyAlgorithm != null) @@ -272,20 +265,7 @@ final class PasskeyRegistrationResponse { ..writeln('PasskeyRegistrationResponse(') ..writeln(' id: $id,') ..writeln(' rawId: $rawId,') - ..writeln(' clientData: PasskeyClientData(') - ..writeln(' challenge: ${base64RawUrl.encode(clientData.challenge)},') - ..writeln(' crossOrigin: ${clientData.crossOrigin},') - ..writeln(' origin: ${clientData.origin},') - ..writeln(' type: ${clientData.type},'); - if (clientData.tokenBinding case final tokenBinding?) { - buffer - ..writeln(' tokenBinding: PasskeyClientDataTokenBinding(') - ..writeln(' id: ${base64RawUrl.encode(tokenBinding.id)},') - ..writeln(' status: ${tokenBinding.status},') - ..writeln(' ),'); - } - buffer - ..writeln(' ),') + ..writeln(' clientDataJson: ${utf8.decode(clientDataJson)},') ..writeln( ' attestationObject: ${base64RawUrl.encode(attestationObject)},') ..writeln(' transports: ${transports?.join(', ')},') @@ -439,7 +419,7 @@ final class PasskeyClientData { type: type, tokenBinding: json['tokenBinding'] != null ? PasskeyClientDataTokenBinding.fromJson( - json['tokenBinding'] as Map, + (json['tokenBinding'] as Map).cast(), ) : null, ); @@ -535,24 +515,24 @@ final class PasskeyDescriptor { 'id': final String id, }) { return PasskeyDescriptor( - id: base64RawUrl.decode(id), - transports: (json['transports'] as List?)?.cast() ?? const [], + id: id, + transports: (json['transports'] as List?)?.cast() ?? const [], ); } throw FormatException('Invalid passkey descriptor: $json'); } - final Uint8List id; + final String id; final List transports; Map toJson() => { - 'id': base64RawUrl.encode(id), + 'id': id, 'transports': transports, }; @override String toString() => 'PasskeyDescriptor(' - 'id: ${base64RawUrl.encode(id)}, ' + 'id: $id, ' 'transports: ${transports.join(', ')})'; } @@ -762,16 +742,33 @@ final class PasskeyAuthenticationResponse { /// See: /// - https://w3c.github.io/webauthn/#sctn-alg-identifier /// - https://www.iana.org/assignments/cose/cose.xhtml#algorithms -extension type const COSEAlgorithmIdentifier._(int algId) implements int { - static const List defaultSupported = [ +extension type const COSEAlgorithmIdentifier._(int _) implements int { + factory COSEAlgorithmIdentifier(int algId) { + if (!values.contains(algId)) { + throw ArgumentError.value( + algId, + 'algId', + 'Unsupported COSE algorithm identifier.', + ); + } + return COSEAlgorithmIdentifier._(algId); + } + + static const List prioritized = [ // In first position to encourage authenticators to use this over ES256 edDsa, es256, + es512, + rsaPss256, + rsaPss384, + rsaPss512, rs256, + rs384, + rs512, + rsSha1, // ignore: deprecated_member_use_from_same_package ]; - static const List prioritized = [ - // In first position to encourage authenticators to use this over ES256 + static const List values = [ edDsa, es256, es512, diff --git a/packages/celest_core/lib/src/auth/user.dart b/packages/celest_core/lib/src/auth/user.dart new file mode 100644 index 00000000..1e8fb1d4 --- /dev/null +++ b/packages/celest_core/lib/src/auth/user.dart @@ -0,0 +1 @@ +final class User {} diff --git a/packages/celest_core/lib/src/exception/cloud_exception.dart b/packages/celest_core/lib/src/exception/cloud_exception.dart index 6f3d7ebd..19f5998c 100644 --- a/packages/celest_core/lib/src/exception/cloud_exception.dart +++ b/packages/celest_core/lib/src/exception/cloud_exception.dart @@ -20,6 +20,16 @@ class BadRequestException implements CloudException { String toString() => 'BadRequestException: $message'; } +final class UnauthorizedException implements CloudException { + const UnauthorizedException([this.message = 'Unauthorized']); + + @override + final String message; + + @override + String toString() => 'UnauthorizedException: $message'; +} + /// {@template celest_core.exception.internal_server_exception} /// An exception thrown by a Cloud Function when an unrecoverable internal error /// occurs. diff --git a/packages/celest_core/pubspec.yaml b/packages/celest_core/pubspec.yaml index a6bb16a4..9e4b011a 100644 --- a/packages/celest_core/pubspec.yaml +++ b/packages/celest_core/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: collection: ^1.18.0 + http: ">=0.13.0 <2.0.0" meta: ^1.10.0 dev_dependencies: