Skip to content

Commit a6d6884

Browse files
authored
App section (#12420)
1 parent fe9184f commit a6d6884

File tree

4 files changed

+244
-8
lines changed

4 files changed

+244
-8
lines changed

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/AuthMenu.swift

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,19 @@ enum AuthMenu: String {
3232
case custom
3333
case initRecaptcha
3434
case customAuthDomain
35-
36-
/// More intuitively named getter for `rawValue`.
35+
case getToken
36+
case getTokenForceRefresh
37+
case addAuthStateChangeListener
38+
case removeLastAuthStateChangeListener
39+
case addIdTokenChangeListener
40+
case removeLastIdTokenChangeListener
41+
case verifyClient
42+
case deleteApp
43+
44+
// More intuitively named getter for `rawValue`.
3745
var id: String { rawValue }
3846

39-
/// The UI friendly name of the `AuthMenu`. Used for display.
47+
// The UI friendly name of the `AuthMenu`. Used for display.
4048
var name: String {
4149
switch self {
4250
case .settings:
@@ -71,11 +79,27 @@ enum AuthMenu: String {
7179
return "Initialize reCAPTCHA Enterprise"
7280
case .customAuthDomain:
7381
return "Set Custom Auth Domain"
82+
case .getToken:
83+
return "Get Token"
84+
case .getTokenForceRefresh:
85+
return "Get Token Force Refresh"
86+
case .addAuthStateChangeListener:
87+
return "Add Auth State Change Listener"
88+
case .removeLastAuthStateChangeListener:
89+
return "Remove Last Auth State Change Listener"
90+
case .addIdTokenChangeListener:
91+
return "Add ID Token Change Listener"
92+
case .removeLastIdTokenChangeListener:
93+
return "Remove Last ID Token Change Listener"
94+
case .verifyClient:
95+
return "Verify Client"
96+
case .deleteApp:
97+
return "Delete App"
7498
}
7599
}
76100

77-
/// Failable initializer to create an `AuthMenu` from it's corresponding `name` value.
78-
/// - Parameter rawValue: String value representing `AuthMenu`'s name or type.
101+
// Failable initializer to create an `AuthMenu` from its corresponding `name` value.
102+
// - Parameter rawValue: String value representing `AuthMenu`'s name or type.
79103
init?(rawValue: String) {
80104
switch rawValue {
81105
case "Settings":
@@ -110,7 +134,24 @@ enum AuthMenu: String {
110134
self = .initRecaptcha
111135
case "Set Custom Auth Domain":
112136
self = .customAuthDomain
113-
default: return nil
137+
case "Get Token":
138+
self = .getToken
139+
case "Get Token Force Refresh":
140+
self = .getTokenForceRefresh
141+
case "Add Auth State Change Listener":
142+
self = .addAuthStateChangeListener
143+
case "Remove Last Auth State Change Listener":
144+
self = .removeLastAuthStateChangeListener
145+
case "Add ID Token Change Listener":
146+
self = .addIdTokenChangeListener
147+
case "Remove Last ID Token Change Listener":
148+
self = .removeLastIdTokenChangeListener
149+
case "Verify Client":
150+
self = .verifyClient
151+
case "Delete App":
152+
self = .deleteApp
153+
default:
154+
return nil
114155
}
115156
}
116157
}
@@ -172,9 +213,24 @@ extension AuthMenu: DataSourceProvidable {
172213
return Section(headerDescription: header, items: [item])
173214
}
174215

216+
static var appSection: Section {
217+
let header = "APP"
218+
let items: [Item] = [
219+
Item(title: getToken.name),
220+
Item(title: getTokenForceRefresh.name),
221+
Item(title: addAuthStateChangeListener.name),
222+
Item(title: removeLastAuthStateChangeListener.name),
223+
Item(title: addIdTokenChangeListener.name),
224+
Item(title: removeLastIdTokenChangeListener.name),
225+
Item(title: verifyClient.name),
226+
Item(title: deleteApp.name),
227+
]
228+
return Section(headerDescription: header, items: items)
229+
}
230+
175231
static var sections: [Section] {
176232
[settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection,
177-
customAuthDomainSection]
233+
customAuthDomainSection, appSection]
178234
}
179235

180236
static var authLinkSections: [Section] {

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/Extensions.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ extension User: DataSourceProvidable {
6666
// MARK: - UIKit Extensions
6767

6868
public extension UIViewController {
69+
func displayInfo(title: String, message: String, style: UIAlertController.Style) {
70+
let alert = UIAlertController(title: title, message: message, preferredStyle: style)
71+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
72+
73+
DispatchQueue.main.async { // Ensure UI updates on the main thread
74+
self.present(alert, animated: true, completion: nil)
75+
}
76+
}
77+
6978
func displayError(_ error: Error?, from function: StaticString = #function) {
7079
guard let error = error else { return }
7180
print("ⓧ Error in \(function): \(error.localizedDescription)")

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
// For Sign in with Facebook
1616
import FBSDKLoginKit
17-
import FirebaseAuth
17+
@testable import FirebaseAuth
1818

1919
// [START auth_import]
2020
import FirebaseCore
@@ -33,6 +33,8 @@ private let kFacebookAppID = "ENTER APP ID HERE"
3333

3434
class AuthViewController: UIViewController, DataSourceProviderDelegate {
3535
var dataSourceProvider: DataSourceProvider<AuthMenu>!
36+
var authStateDidChangeListeners: [AuthStateDidChangeListenerHandle] = []
37+
var IDTokenDidChangeListeners: [IDTokenDidChangeListenerHandle] = []
3638

3739
override func loadView() {
3840
view = UITableView(frame: .zero, style: .insetGrouped)
@@ -95,6 +97,30 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
9597

9698
case .customAuthDomain:
9799
performCustomAuthDomainFlow()
100+
101+
case .getToken:
102+
getUserTokenResult(force: false)
103+
104+
case .getTokenForceRefresh:
105+
getUserTokenResult(force: true)
106+
107+
case .addAuthStateChangeListener:
108+
addAuthStateListener()
109+
110+
case .removeLastAuthStateChangeListener:
111+
removeAuthStateListener()
112+
113+
case .addIdTokenChangeListener:
114+
addIDTokenListener()
115+
116+
case .removeLastIdTokenChangeListener:
117+
removeIDTokenListener()
118+
119+
case .verifyClient:
120+
verifyClient()
121+
122+
case .deleteApp:
123+
deleteApp()
98124
}
99125
}
100126

@@ -316,6 +342,142 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
316342
present(prompt, animated: true)
317343
}
318344

345+
private func getUserTokenResult(force: Bool) {
346+
guard let currentUser = Auth.auth().currentUser else {
347+
print("Error: No user logged in")
348+
return
349+
}
350+
351+
currentUser.getIDTokenResult(forcingRefresh: force, completion: { tokenResult, error in
352+
if error != nil {
353+
print("Error: Error refreshing token")
354+
return // Handle error case, returning early
355+
}
356+
357+
if let tokenResult = tokenResult, let claims = tokenResult.claims as? [String: Any] {
358+
var message = "Token refresh succeeded\n\n"
359+
for (key, value) in claims {
360+
message += "\(key): \(value)\n"
361+
}
362+
self.displayInfo(title: "Info", message: message, style: .alert)
363+
} else {
364+
print("Error: Unable to access claims.")
365+
}
366+
})
367+
}
368+
369+
private func addAuthStateListener() {
370+
weak var weakSelf = self
371+
let index = authStateDidChangeListeners.count
372+
print("Auth State Did Change Listener #\(index) was added.")
373+
let handle = Auth.auth().addStateDidChangeListener { [weak weakSelf] auth, user in
374+
guard weakSelf != nil else { return }
375+
print("Auth State Did Change Listener #\(index) was invoked on user '\(user?.uid ?? "nil")'")
376+
}
377+
authStateDidChangeListeners.append(handle)
378+
}
379+
380+
private func removeAuthStateListener() {
381+
guard !authStateDidChangeListeners.isEmpty else {
382+
print("No remaining Auth State Did Change Listeners.")
383+
return
384+
}
385+
let index = authStateDidChangeListeners.count - 1
386+
let handle = authStateDidChangeListeners.last!
387+
Auth.auth().removeStateDidChangeListener(handle)
388+
authStateDidChangeListeners.removeLast()
389+
print("Auth State Did Change Listener #\(index) was removed.")
390+
}
391+
392+
private func addIDTokenListener() {
393+
weak var weakSelf = self
394+
let index = IDTokenDidChangeListeners.count
395+
print("ID Token Did Change Listener #\(index) was added.")
396+
let handle = Auth.auth().addIDTokenDidChangeListener { [weak weakSelf] auth, user in
397+
guard weakSelf != nil else { return }
398+
print("ID Token Did Change Listener #\(index) was invoked on user '\(user?.uid ?? "")'.")
399+
}
400+
IDTokenDidChangeListeners.append(handle)
401+
}
402+
403+
func removeIDTokenListener() {
404+
guard !IDTokenDidChangeListeners.isEmpty else {
405+
print("No remaining ID Token Did Change Listeners.")
406+
return
407+
}
408+
let index = IDTokenDidChangeListeners.count - 1
409+
let handle = IDTokenDidChangeListeners.last!
410+
Auth.auth().removeIDTokenDidChangeListener(handle)
411+
IDTokenDidChangeListeners.removeLast()
412+
print("ID Token Did Change Listener #\(index) was removed.")
413+
}
414+
415+
func verifyClient() {
416+
AppManager.shared.auth().tokenManager.getTokenInternal { token, error in
417+
if token == nil {
418+
print("Verify iOS Client failed.")
419+
return
420+
}
421+
let request = VerifyClientRequest(
422+
withAppToken: token?.string,
423+
isSandbox: token?.type == .sandbox,
424+
requestConfiguration: AppManager.shared.auth().requestConfiguration
425+
)
426+
427+
Task {
428+
do {
429+
let verifyResponse = try await AuthBackend.call(with: request)
430+
431+
guard let receipt = verifyResponse.receipt,
432+
let timeoutDate = verifyResponse.suggestedTimeOutDate else {
433+
print("Internal Auth Error: invalid VerifyClientResponse.")
434+
return
435+
}
436+
437+
let timeout = timeoutDate.timeIntervalSinceNow
438+
do {
439+
let credential = await AppManager.shared.auth().appCredentialManager
440+
.didStartVerification(
441+
withReceipt: receipt,
442+
timeout: timeout
443+
)
444+
445+
guard credential.secret != nil else {
446+
print("Failed to receive remote notification to verify App ID.")
447+
return
448+
}
449+
450+
let testPhoneNumber = "+16509964692"
451+
let request = SendVerificationCodeRequest(
452+
phoneNumber: testPhoneNumber,
453+
codeIdentity: CodeIdentity.credential(credential),
454+
requestConfiguration: AppManager.shared.auth().requestConfiguration
455+
)
456+
457+
do {
458+
_ = try await AuthBackend.call(with: request)
459+
print("Verify iOS client succeeded")
460+
} catch {
461+
print("Verify iOS Client failed: \(error.localizedDescription)")
462+
}
463+
}
464+
} catch {
465+
print("Verify iOS Client failed: \(error.localizedDescription)")
466+
}
467+
}
468+
}
469+
}
470+
471+
func deleteApp() {
472+
AppManager.shared.app.delete { success in
473+
if success {
474+
print("App deleted successfully.")
475+
} else {
476+
print("Failed to delete app.")
477+
}
478+
}
479+
}
480+
319481
// MARK: - Private Helpers
320482

321483
private func configureDataSourceProvider() {

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/SettingsViewController.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,15 @@ extension AuthSettings: DataSourceProvidable {
272272

273273
func appCredentialString() -> String {
274274
if let credential = AppManager.shared.auth().appCredentialManager.credential {
275+
let message = "receipt: \(credential.receipt)\nsecret: \(credential.secret)"
276+
277+
showPromptWithTitle("Clear App Credential?", message: message,
278+
showCancelButton: true) { userPressedOK, _ in
279+
if userPressedOK {
280+
AppManager.shared.auth().appCredentialManager.clearCredential()
281+
}
282+
}
283+
}
275284
let truncatedReceipt = truncatedString(string: credential.receipt, length: 13)
276285
let truncatedSecret = truncatedString(string: credential.secret ?? "", length: 13)
277286
return "\(truncatedReceipt)/\(truncatedSecret)"

0 commit comments

Comments
 (0)