Skip to content

Commit 8f1c901

Browse files
author
Isaac
committed
Passkeys
1 parent d8b0878 commit 8f1c901

File tree

18 files changed

+262
-95
lines changed

18 files changed

+262
-95
lines changed

Telegram/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ associated_domains_fragment = "" if telegram_bundle_id not in official_bundle_id
496496
<string>applinks:t.me</string>
497497
<string>applinks:*.t.me</string>
498498
<string>webcredentials:t.me</string>
499+
<string>webcredentials:telegram.org</string>
499500
</array>
500501
"""
501502

submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
324324
accountManager: self.sharedContext.accountManager,
325325
account: self.account,
326326
passkey: passkey,
327+
foreignDatacenter: nil,
327328
forcedPasswordSetupNotice: { value in
328329
guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else {
329330
return nil
@@ -332,7 +333,14 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
332333
},
333334
syncContacts: syncContacts
334335
)
335-
|> deliverOnMainQueue).startStrict(next: { _ in
336+
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
337+
guard let self else {
338+
return
339+
}
340+
if result.updatedAccount !== self.account {
341+
self.account = result.updatedAccount
342+
self.inAppPurchaseManager = InAppPurchaseManager(engine: .unauthorized(self.engine))
343+
}
336344
}, error: { [weak self, weak controller] error in
337345
Queue.mainQueue().async {
338346
if let strongSelf = self, let controller {

submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
157157
strongSelf.account = account
158158
strongSelf.accountUpdated?(account)
159159
}
160+
self.controllerNode.retryPasskey = { [weak self] in
161+
guard let self else {
162+
return
163+
}
164+
self.loadAndPresentPasskey(force: true)
165+
}
160166

161167
if let (code, name, number) = self.currentData {
162168
self.controllerNode.codeAndNumber = (code, name, number)
@@ -194,7 +200,11 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
194200
self.controllerNode.updateCountryCode()
195201
}
196202

197-
if #available(iOS 15.0, *) {
203+
self.loadAndPresentPasskey(force: false)
204+
}
205+
206+
private func loadAndPresentPasskey(force: Bool) {
207+
if #available(iOS 16.0, *) {
198208
Task { @MainActor [weak self] in
199209
guard let self, let account = self.account else {
200210
return
@@ -235,7 +245,11 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
235245
let authController = ASAuthorizationController(authorizationRequests: [platformKeyRequest])
236246
authController.delegate = self
237247
authController.presentationContextProvider = self
238-
authController.performRequests()
248+
if force {
249+
authController.performRequests()
250+
} else {
251+
authController.performRequests(options: [.preferImmediatelyAvailableCredentials])
252+
}
239253
}
240254
}
241255
}
@@ -290,6 +304,12 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
290304
}
291305

292306
public func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: any Error) {
307+
if (error as NSError).domain == "com.apple.AuthenticationServices.AuthorizationError" && (error as NSError).code == 1001 {
308+
self.controllerNode.updateDisplayPasskeyLoginOption()
309+
if let validLayout = self.validLayout {
310+
self.containerLayoutUpdated(validLayout, transition: .immediate)
311+
}
312+
}
293313
}
294314

295315
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {

submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import TelegramAnimatedStickerNode
1515
import SolidRoundedButtonNode
1616
import AuthorizationUtils
1717
import ManagedAnimationNode
18+
import Markdown
1819

1920
private final class PhoneAndCountryNode: ASDisplayNode {
2021
let strings: PresentationStrings
@@ -312,7 +313,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
312313
private let managedAnimationNode: ManagedPhoneAnimationNode
313314
private let titleNode: ASTextNode
314315
private let titleActivateAreaNode: AccessibilityAreaNode
315-
private let noticeNode: ASTextNode
316+
private let noticeNode: ImmediateTextNode
316317
private let noticeActivateAreaNode: AccessibilityAreaNode
317318
private let phoneAndCountryNode: PhoneAndCountryNode
318319
private let contactSyncNode: ContactSyncNode
@@ -323,6 +324,8 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
323324
private let tokenEventsDisposable = MetaDisposable()
324325
var accountUpdated: ((UnauthorizedAccount) -> Void)?
325326

327+
var retryPasskey: (() -> Void)?
328+
326329
private let debugAction: () -> Void
327330

328331
var currentNumber: String {
@@ -405,7 +408,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
405408
self.titleActivateAreaNode = AccessibilityAreaNode()
406409
self.titleActivateAreaNode.accessibilityTraits = .staticText
407410

408-
self.noticeNode = ASTextNode()
411+
self.noticeNode = ImmediateTextNode()
409412
self.noticeNode.maximumNumberOfLines = 0
410413
self.noticeNode.isUserInteractionEnabled = true
411414
self.noticeNode.displaysAsynchronously = false
@@ -443,6 +446,23 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
443446
self.addSubnode(self.managedAnimationNode)
444447
self.contactSyncNode.isHidden = true
445448

449+
self.noticeNode.highlightAttributeAction = { attributes in
450+
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] {
451+
return NSAttributedString.Key(rawValue: "URL")
452+
} else {
453+
return nil
454+
}
455+
}
456+
self.noticeNode.tapAttributeAction = { [weak self] attributes, _ in
457+
guard let self else {
458+
return
459+
}
460+
if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] as? String {
461+
self.retryPasskey?()
462+
}
463+
}
464+
self.noticeNode.linkHighlightColor = theme.list.itemAccentColor.withAlphaComponent(0.2)
465+
446466
self.phoneAndCountryNode.selectCountryCode = { [weak self] in
447467
self?.selectCountryCode?()
448468
}
@@ -484,7 +504,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
484504
super.didLoad()
485505

486506
self.titleNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.debugTap(_:))))
487-
#if DEBUG
507+
#if DEBUG && false
488508
self.noticeNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.debugQrTap(_:))))
489509
#endif
490510
}
@@ -555,6 +575,28 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
555575
let _ = self.phoneAndCountryNode.processNumberChange(number: self.phoneAndCountryNode.phoneInputNode.number)
556576
}
557577

578+
func updateDisplayPasskeyLoginOption() {
579+
//TODO:localize
580+
if self.account == nil {
581+
return
582+
}
583+
let attributedText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString("Enter your phone number\nor [log in using Passkey >](passkey)", attributes: MarkdownAttributes(
584+
body: MarkdownAttributeSet(font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor),
585+
bold: MarkdownAttributeSet(font: Font.semibold(17.0), textColor: self.theme.list.itemPrimaryTextColor),
586+
link: MarkdownAttributeSet(font: Font.regular(17.0), textColor: self.theme.list.itemAccentColor),
587+
linkAttribute: { url in
588+
return ("URL", url)
589+
}
590+
)))
591+
let chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: self.theme.list.itemAccentColor)
592+
593+
if let range = attributedText.string.range(of: ">"), let chevronImage {
594+
attributedText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedText.string))
595+
}
596+
597+
self.noticeNode.attributedText = attributedText
598+
}
599+
558600
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
559601
var insets = layout.insets(options: [])
560602
insets.top = layout.statusBarHeight ?? 20.0
@@ -576,7 +618,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
576618

577619
let noticeInset: CGFloat = self.account == nil ? 32.0 : 0.0
578620

579-
let noticeSize = self.noticeNode.measure(CGSize(width: min(274.0 + noticeInset, maximumWidth - 28.0), height: CGFloat.greatestFiniteMagnitude))
621+
let noticeSize = self.noticeNode.updateLayout(CGSize(width: min(274.0 + noticeInset, maximumWidth - 28.0), height: CGFloat.greatestFiniteMagnitude))
580622
let proceedHeight = self.proceedNode.updateLayout(width: maximumWidth - inset * 2.0, transition: transition)
581623
let proceedSize = CGSize(width: maximumWidth - inset * 2.0, height: proceedHeight)
582624

submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
501501
arguments.openTwoStepVerification(data)
502502
})
503503
case let .passkeys(_, text, value):
504-
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: UIImage(bundleImageName: "Settings/Menu/TwoStepAuth")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
504+
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: UIImage(bundleImageName: "Settings/Menu/Passkeys")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
505505
arguments.openPasskeys()
506506
})
507507
case let .messageAutoremoveTimeout(_, text, value):

submodules/TelegramCore/Sources/Account/Account.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,13 @@ public func accountWithId(accountManager: AccountManager<TelegramAccountManagerT
340340
}
341341
}
342342

343-
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: 2, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
343+
#if DEBUG
344+
let initialDatacenterId: Int = 1
345+
#else
346+
let initialDatacenterId: Int = 2
347+
#endif
348+
349+
return initializedNetwork(accountId: id, arguments: networkArguments, supplementary: supplementary, datacenterId: initialDatacenterId, keychain: keychain, basePath: path, testingEnvironment: beginWithTestingEnvironment, languageCode: localizationSettings?.primaryComponent.languageCode, proxySettings: proxySettings, networkSettings: networkSettings, phoneNumber: nil, useRequestTimeoutTimers: useRequestTimeoutTimers, appConfiguration: appConfig)
344350
|> map { network -> AccountResult in
345351
return .unauthorized(UnauthorizedAccount(accountManager: accountManager, networkArguments: networkArguments, id: id, rootPath: rootPath, basePath: path, testingEnvironment: beginWithTestingEnvironment, postbox: postbox, network: network, shouldKeepAutoConnection: shouldKeepAutoConnection))
346352
}

0 commit comments

Comments
 (0)