1 import AuthenticationServices 2 import ABI49_0_0ExpoModulesCore 3 4 // A set of pending requests used to retain them until the callback is called 5 var pendingRequests = Set<AppleAuthenticationRequest>() 6 7 final class AppleAuthenticationRequest: NSObject, ASAuthorizationControllerPresentationContextProviding, ASAuthorizationControllerDelegate { 8 typealias AuthenticationResponse = [AnyHashable: Any] 9 typealias AuthenticationRequestCallback = (AuthenticationResponse?, Exception?) -> Void 10 11 let options: AppleAuthenticationRequestOptions 12 var callback: AuthenticationRequestCallback? 13 var authController: ASAuthorizationController? 14 15 init(options: AppleAuthenticationRequestOptions) { 16 self.options = options 17 } 18 19 func performRequest(callback: @escaping AuthenticationRequestCallback) { 20 self.callback = callback 21 22 do { 23 let appleIdProvider = ASAuthorizationAppleIDProvider() 24 let request = appleIdProvider.createRequest() 25 26 request.requestedScopes = try scopesFromInts(options.requestedScopes) 27 request.requestedOperation = try operationFromInt(options.requestedOperation) 28 29 request.user = options.user 30 request.state = options.state 31 request.nonce = options.nonce 32 33 authController = ASAuthorizationController(authorizationRequests: [request]) 34 authController?.presentationContextProvider = self 35 authController?.delegate = self 36 37 // Retain pending request 38 pendingRequests.insert(self) 39 40 authController?.performRequests() 41 } catch { 42 self.callback?(nil, error as? Exception) 43 self.callback = nil 44 } 45 } 46 47 // MARK: - ASAuthorizationControllerDelegate 48 49 func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { 50 let credential = authorization.credential as? ASAuthorizationAppleIDCredential 51 let response: AuthenticationResponse = [ 52 "fullName": nameComponentsToDict(credential?.fullName), 53 "email": credential?.email, 54 "user": credential?.user, 55 "realUserStatus": realUserStatusToInt(credential?.realUserStatus), 56 "state": credential?.state, 57 "authorizationCode": dataToString(credential?.authorizationCode), 58 "identityToken": dataToString(credential?.identityToken) 59 ] 60 61 callback?(response, nil) 62 callback = nil 63 64 // Release pending request 65 pendingRequests.remove(self) 66 } 67 68 func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { 69 if let error = error as? ASAuthorizationError { 70 log.error("Apple authorization failed: \(error.localizedDescription)") 71 callback?(nil, exceptionForAuthorizationError(error)) 72 } else { 73 callback?(nil, RequestUnknownException()) 74 } 75 callback = nil 76 77 // Release pending request 78 pendingRequests.remove(self) 79 } 80 81 // MARK: - ASAuthorizationControllerPresentationContextProviding 82 83 func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { 84 guard let window = UIApplication.shared.keyWindow else { 85 fatalError("Unable to present authentication modal because UIApplication.shared.keyWindow is not available") 86 } 87 return window 88 } 89 } 90