Skip to content

feat: Facebook auth re-authentication to allow deleting user #1256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

russellwheatley
Copy link
Member

@russellwheatley russellwheatley commented May 15, 2025

I've made comments in the PR so it is a bit clearer what is going on.

@@ -6,7 +6,7 @@ protocol EmailPasswordOperationReauthentication {
}

extension EmailPasswordOperationReauthentication {
func reauthenticate() async throws -> AuthenticationToken {
@MainActor func reauthenticate() async throws -> AuthenticationToken {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a number of API @MainActor to stop Xcode compiler error for passing User around which is non-sendable at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM, you could add TODOs here since User will eventually become Sendable but there's a strong argument for just keeping this API @MainActor regardless.

public var phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)?
private var googleProvider: (any GoogleProviderAuthUIProtocol)?
private var facebookProvider: (any FacebookProviderAuthUIProtocol)?
private var phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are private now because they shouldn't be accessed, should use safeGoogleProvider, etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd rather have the private apis be named unsafeGoogleProvider and the public ones plainly googleProvider

case let phone as PhoneAuthProviderAuthUIProtocol:
phoneAuthProvider = phone
providers.append(provider)
default:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now assigns providers correctly without having to manually set + call register().

let operation = EmailPasswordDeleteUserOperation(passwordPrompt: passwordPrompt)
try await operation(on: user)
} else if providerId == "facebook.com" {
try await safeFacebookProvider.deleteUser(user: user)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit tricky deleting user with Facebook re-authentication but I found the best way was to have it on the provider itself which makes sense to me anyway.

facebookProvider = FacebookProviderAuthUI(scopes: scopes)
register(provider: facebookProvider!)
FacebookProviderAuthUI.configureSharedInstance(scopes: scopes)
register(provider: FacebookProviderAuthUI.shared)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to having a private init() for Facebook provider and making it a singleton which is kept on shared as there really should only be one instance of it anyway. I can roll this back if you don't like.

self.scopes = scopes ?? kDefaultFacebookScopes
rawNonce = CommonUtils.randomNonce()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this logic so it recreates every time for limited login otherwise reauthentication will fail as the credential is the same.

private var rawNonce: String?
private var shaNonce: String?
// Needed for reauthentication
var isLimitedLogin: Bool = true
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need this to track the initial login for re-authentication.

Comment on lines -77 to -80
// showCanceledAlert = true
case let .failed(error):
continuation.resume(throwing: error)
// errorMessage = authService.string.localizedErrorMessage(for: error)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errorMessage and alert are handled upstream, removed here.

@russellwheatley russellwheatley marked this pull request as ready for review May 15, 2025 13:36
Copy link
Contributor

@morganchen12 morganchen12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with the singleton behavior. Had a few comments, otherwise LGTM.

@@ -6,7 +6,7 @@ protocol EmailPasswordOperationReauthentication {
}

extension EmailPasswordOperationReauthentication {
func reauthenticate() async throws -> AuthenticationToken {
@MainActor func reauthenticate() async throws -> AuthenticationToken {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM, you could add TODOs here since User will eventually become Sendable but there's a strong argument for just keeping this API @MainActor regardless.

public var phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)?
private var googleProvider: (any GoogleProviderAuthUIProtocol)?
private var facebookProvider: (any FacebookProviderAuthUIProtocol)?
private var phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd rather have the private apis be named unsafeGoogleProvider and the public ones plainly googleProvider

@@ -119,7 +131,7 @@ public final class AuthService {
get throws {
guard let provider = facebookProvider else {
throw AuthServiceError
.notConfiguredProvider("`FacebookProviderSwift` has not been configured")
.notConfiguredProvider("`FacebookProviderAuthUI` has not been configured")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not just be a fatalError given consumers won't have conditional configuration of providers?

@@ -109,7 +119,7 @@ public class FacebookProviderAuthUI: FacebookProviderAuthUIProtocol {
if let idToken = AuthenticationToken.current {
let credential = OAuthProvider.credential(withProviderID: providerId,
idToken: idToken.tokenString,
rawNonce: rawNonce)
rawNonce: rawNonce!)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not immediately clear from the code that this optional will never be nil here, so maybe add a comment explaining why it's safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants