@@ -12,8 +12,9 @@ import CountrySelectionUI
1212import PhoneNumberFormat
1313import DebugSettingsUI
1414import MessageUI
15+ import AuthenticationServices
1516
16- public final class AuthorizationSequencePhoneEntryController : ViewController , MFMailComposeViewControllerDelegate {
17+ public final class AuthorizationSequencePhoneEntryController : ViewController , MFMailComposeViewControllerDelegate , ASAuthorizationControllerDelegate , ASAuthorizationControllerPresentationContextProviding {
1718 private var controllerNode : AuthorizationSequencePhoneEntryControllerNode {
1819 return self . displayNode as! AuthorizationSequencePhoneEntryControllerNode
1920 }
@@ -22,6 +23,8 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
2223
2324 private let sharedContext : SharedAccountContext
2425 private var account : UnauthorizedAccount ?
26+ private let apiId : Int32
27+ private let apiHash : String
2528 private let isTestingEnvironment : Bool
2629 private let otherAccountPhoneNumbers : ( ( String , AccountRecordId , Bool ) ? , [ ( String , AccountRecordId , Bool ) ] )
2730 private let network : Network
@@ -52,6 +55,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
5255 }
5356 }
5457 public var loginWithNumber : ( ( String , Bool ) -> Void ) ?
58+ public var loginWithPasskey : ( ( AuthorizationPasskeyData , Bool ) -> Void ) ?
5559 var accountUpdated : ( ( UnauthorizedAccount ) -> Void ) ?
5660
5761 weak var confirmationController : PhoneConfirmationController ?
@@ -60,9 +64,11 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
6064
6165 private let hapticFeedback = HapticFeedback ( )
6266
63- public init ( sharedContext: SharedAccountContext , account: UnauthorizedAccount ? , countriesConfiguration: CountriesConfiguration ? = nil , isTestingEnvironment: Bool , otherAccountPhoneNumbers: ( ( String , AccountRecordId , Bool ) ? , [ ( String , AccountRecordId , Bool ) ] ) , network: Network , presentationData: PresentationData , openUrl: @escaping ( String ) -> Void , back: @escaping ( ) -> Void ) {
67+ public init ( sharedContext: SharedAccountContext , account: UnauthorizedAccount ? , countriesConfiguration: CountriesConfiguration ? = nil , apiId : Int32 , apiHash : String , isTestingEnvironment: Bool , otherAccountPhoneNumbers: ( ( String , AccountRecordId , Bool ) ? , [ ( String , AccountRecordId , Bool ) ] ) , network: Network , presentationData: PresentationData , openUrl: @escaping ( String ) -> Void , back: @escaping ( ) -> Void ) {
6468 self . sharedContext = sharedContext
6569 self . account = account
70+ self . apiId = apiId
71+ self . apiHash = apiHash
6672 self . isTestingEnvironment = isTestingEnvironment
6773 self . otherAccountPhoneNumbers = otherAccountPhoneNumbers
6874 self . network = network
@@ -187,6 +193,110 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
187193 } else {
188194 self . controllerNode. updateCountryCode ( )
189195 }
196+
197+ if #available( iOS 15 . 0 , * ) {
198+ Task { @MainActor [ weak self] in
199+ guard let self, let account = self . account else {
200+ return
201+ }
202+
203+ let decodeBase64 : ( String ) -> Data ? = { string in
204+ var string = string. replacingOccurrences ( of: " - " , with: " + " )
205+ . replacingOccurrences ( of: " _ " , with: " / " )
206+ while string. count % 4 != 0 {
207+ string. append ( " = " )
208+ }
209+ return Data ( base64Encoded: string)
210+ }
211+
212+ let engine = TelegramEngineUnauthorized ( account: account)
213+ let passkeyDataString = await engine. auth. requestPasskeyLoginData ( apiId: self . apiId, apiHash: self . apiHash) . get ( )
214+ guard let passkeyDataString, let passkeyData = passkeyDataString. data ( using: . utf8) else {
215+ return
216+ }
217+ guard let params = try ? JSONSerialization . jsonObject ( with: passkeyData) as? [ String : Any ] else {
218+ return
219+ }
220+ guard let pkDict = params [ " publicKey " ] as? [ String : Any ] else {
221+ return
222+ }
223+ guard let relyingPartyIdentifier = pkDict [ " rpId " ] as? String else {
224+ return
225+ }
226+ guard let challengeBase64 = pkDict [ " challenge " ] as? String else {
227+ return
228+ }
229+ guard let challengeData = decodeBase64 ( challengeBase64) else {
230+ return
231+ }
232+
233+ let platformProvider = ASAuthorizationPlatformPublicKeyCredentialProvider ( relyingPartyIdentifier: relyingPartyIdentifier)
234+ let platformKeyRequest = platformProvider. createCredentialAssertionRequest ( challenge: challengeData)
235+ let authController = ASAuthorizationController ( authorizationRequests: [ platformKeyRequest] )
236+ authController. delegate = self
237+ authController. presentationContextProvider = self
238+ authController. performRequests ( )
239+ }
240+ }
241+ }
242+
243+ public func authorizationController( controller: ASAuthorizationController , didCompleteWithAuthorization authorization: ASAuthorization ) {
244+ Task { @MainActor [ weak self] in
245+ guard let self, let account = self . account else {
246+ return
247+ }
248+
249+ let encodeBase64URL : ( Data ) -> String = { data in
250+ var string = data. base64EncodedString ( )
251+ string = string
252+ . replacingOccurrences ( of: " + " , with: " - " )
253+ . replacingOccurrences ( of: " / " , with: " _ " )
254+ string = string. replacingOccurrences ( of: " = " , with: " " )
255+ return string
256+ }
257+
258+ if #available( iOS 17 . 0 , * ) {
259+ if let credential = authorization. credential as? ASAuthorizationPlatformPublicKeyCredentialAssertion {
260+ guard let clientData = String ( data: credential. rawClientDataJSON, encoding: . utf8) else {
261+ return
262+ }
263+ guard let userHandle = String ( data: credential. userID, encoding: . utf8) else {
264+ return
265+ }
266+ let passkey = AuthorizationPasskeyData (
267+ id: encodeBase64URL ( credential. credentialID) ,
268+ clientData: clientData,
269+ authenticatorData: credential. rawAuthenticatorData,
270+ signature: credential. signature,
271+ userHandle: userHandle
272+ )
273+ self . loginWithPasskey ? ( passkey, self . controllerNode. syncContacts)
274+
275+ /*if let clientData = String(data: credential.rawClientDataJSON, encoding: .utf8), let attestationObject = credential.rawAttestationObject {
276+ let passkey = await component.context.engine.auth.requestCreatePasskey(id: encodeBase64URL(credential.credentialID), clientData: clientData, attestationObject: attestationObject).get()
277+ if let passkey {
278+ if self.passkeysData == nil {
279+ self.passkeysData = []
280+ self.passkeysData?.insert(passkey, at: 0)
281+ }
282+ self.state?.updated(transition: .immediate)
283+ }
284+ }*/
285+ let _ = account
286+ let _ = credential
287+ }
288+ }
289+ }
290+ }
291+
292+ public func authorizationController( controller: ASAuthorizationController , didCompleteWithError error: any Error ) {
293+ }
294+
295+ public func presentationAnchor( for controller: ASAuthorizationController ) -> ASPresentationAnchor {
296+ guard let windowScene = self . view. window? . windowScene else {
297+ preconditionFailure ( )
298+ }
299+ return ASPresentationAnchor ( windowScene: windowScene)
190300 }
191301
192302 public func updateCountryCode( ) {
0 commit comments