How to implement Firebase Microsoft OAuth in iOS Swift? - ios

I'm fairly new to Swift and Firebase and I'm trying to get the Microsoft OAuth provider working as described here:
https://firebase.google.com/docs/auth/ios/microsoft-oauth?authuser=0
My App is stable when run, but the MSLoginTapped button does nothing and when debugging I'm only able to see that provider.getCredentialWith doesn't appear to run because I do not receive my print statement.
There seemed to be multiple errors in the documentation, so I doctored it up as best possible and submitted a request for review of the documentation.
Firebase Documentation snippet:
provider.getCredentialWithUIDelegate(nil) { credential, error in
if error != nil {
// Handle error.
}
if credential != nil {
Auth().signInAndRetrieveData(with: credential) { authResult, error in
if error != nil {
// Handle error.
}
// User is signed in.
// IdP data available in authResult.additionalUserInfo.profile.
// OAuth access token can also be retrieved:
// credential.accessToken
}
}
}
Problem 1: provider.getCredentialWithUIDelegate is not recognized.
Problem 2: Auth().signInAndRetrieveData is not recognized.
========== ========== ========== ========== ==========
Here's what I'm working with (that's NOT working):
class LoginViewController: UIViewController {
var ref : DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func MSLoginTapped(_ sender: UIButton) {
let provider = OAuthProvider(providerID: "microsoft.com")
provider.getCredentialWith(nil) { (credential, error) in
if error != nil {
// Handle error.
print("Failed to retreive credential.")
return
}
if credential != nil {
Auth.auth().signInAndRetrieveData(with: credential!) { authResult, error in
if error != nil {
// Handle error.
}
// User is signed in.
// IdP data available in authResult.additionalUserInfo.profile.
// OAuth access token can also be retrieved:
// credential.accessToken
}
} else {
print("Credential is nil.")
}
}
}
}

Ran into the same problem.
let provider = OAuthProvider(providerID: "microsoft.com")
provider is a local variable, getCredentialWith is async and ARC will get rid of provider. Make it global scope.
Firebase does not show up sign page for microsoft.com in Swift / iOS

Related

Firebase User re-authentication initialising error

When any user wants to update his/her password from app, i want to update that password of same FCM user and for that i have tried below code which define in official doc.
here is the screenshot of that
Here is my code
func authenticateAndUpdateFCMUserPassword(strNewPassword: String) {
let user = Auth.auth().currentUser
var credential: AuthCredential
// Prompt the user to re-provide their sign-in credentials
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Auth.auth().currentUser?.updatePassword(to: strNewPassword) { (error) in
if error != nil {
print("Error occur while updating password")
}
else {
print("Password Updated Successfully")
}
}
}
But in above code i'm facing below error at user?.reauthenticate line
Cannot convert value of type '(_) -> ()' to expected argument type
'AuthDataResultCallback?' (aka 'Optional<(Optional,
Optional) -> ()>')
so i re write that by below code
user?.reauthenticate(with: credential, completion: { (dataResult, errorr) in
if errorr != nil {
// An error happened.
} else {
// User re-authenticated.
}
})
but in above code i'm facing below error
Variable 'credential' used before being initialized
credential is defined only once and never used before this
will anyone please let me know what i'm doing wrong?
When I set my credential variable as below, the error went away for me:
var credential: AuthCredential = EmailAuthProvider.credential(withEmail: email, password: password)
This answer helped me: https://stackoverflow.com/a/52160137/9625899

Firebase does not close Microsoft sign page when finished in iOS

When using Microsoft as a provider in Firebase Auth, after signing in UIApplication:open:url is called with the redirection. But there is nothing in the documentation (https://firebase.google.com/docs/auth/ios/microsoft-oauth) about how to handle it and to get the sign in page to close.
For GoogleAuthProvider you would call GIDSignIn.sharedInstance().handle(url...) in UIApplication:open:url to handle the redirection, which closes the WebView and works great.
Can not find anything out there on how to handle the redirect.
var provider: OAuthProvider?
#IBAction func buttonTapped(_ sender: Any) {
provider = OAuthProvider(providerID: "microsoft.com")
provider?.customParameters = [
"prompt": "consent",
"login_hint": "",
]
provider?.getCredentialWith(nil ) { credential, error in
if error != nil {
// Handle error.
}
print(credential?.provider)
if let credential = credential {
Auth.auth().signInAndRetrieveData(with: credential)
{ (authResult, error) in
print(authResult?.additionalUserInfo?.profile)
print(authResult?.user.providerID)
}
} else {
}
}
}
I would expect after the user has signed in, the Microsoft SignIn page would close and getCredentialWith completion would be called.

Firebase delete account along with Database and Storage on iOS

I am trying to implement a function to delete current user's account on iOS. Account deletion works properly but the problem is that I cannot delete the account's data from Database and Storage when deleting an account.
"currentUser.delete" deletes the account but I think there is no authentication to delete its data from Database and Storage. Permission denied message shows up in the log. After running this function, I get to see the account is gone in Firebase Console Authentication page but data from Database and Storage persists.
Is this the correct way to delete an account?
I tried to delete data from Database and Storage before deleting the account. However, Firebase asks for re-authentication if session is more than 5 minutes old. Re-login shows empty data to the user before performing account deletion again so this is misleading and very confusing.
Please let me know how to remove data when deleting an account.
private func deleteAccount() {
guard let currentUser = Auth.auth().currentUser else {
return print("user not logged in")
}
currentUser.delete { error in
if error == nil {
// 1. Delete currentUser's data from Database. Permission denied
// 2. Delete currentUser's data from Storage. Permission denied
// present login screen (welcome page)
self.presentLoginScreen()
} else {
guard let errorCode = AuthErrorCode(rawValue: error!._code) else { return }
if errorCode == AuthErrorCode.requiresRecentLogin {
self.showMessage("Please re-authenticate to delete your account.", type: .error)
do {
try Auth.auth().signOut()
self.presentLoginScreen()
} catch {
print("There was a problem logging out")
}
}
}
}
}
Swift 5 | Firebase 8.11.0
To solve the problems that you've mentioned (delete the data before deleting the actual user and potentially get the AuthErrorCode.requiresRecentLogin error), you may use DispatchGroup and check the lastSignInDate, like this (just call deleteUserProcess()):
let deleteDataGroup = DispatchGroup()
func deleteUserProcess() {
guard let currentUser = Auth.auth().currentUser else { return }
deleteUserData(user: currentUser)
// Call deleteUser only when all data has been deleted
deleteDataGroup.notify(queue: .main) {
self.deleteUser(user: currentUser)
}
}
/// Remove data from Database & Storage
func deleteUserData(user currentUser: User) {
// Check if `currentUser.delete()` won't require re-authentication
if let lastSignInDate = currentUser.metadata.lastSignInDate,
lastSignInDate.minutes(from: Date()) >= -5 {
deleteDataGroup.enter()
Database.database().reference().child(userId).removeValue { error, _ in
if let error = error { print(error) }
self.deleteDataGroup.leave()
}
// Delete folders from Storage isn't possible,
// so list and run over all files to delete each one independently
deleteDataGroup.enter()
Storage.storage().reference().child(userId).listAll { list, error in
if let error = error { print(error) }
list.items.forEach({ file in
self.deleteDataGroup.enter()
file.delete { error in
if let error = error { print(error) }
self.deleteDataGroup.leave()
}
})
deleteDataGroup.leave()
}
}
}
/// Delete user
func deleteUser(user currentUser: User) {
currentUser.delete { error in
if let error = error {
if AuthErrorCode(rawValue: error._code) == .requiresRecentLogin {
reauthenticate()
} else {
// Another error occurred
}
return
}
// Logout properly
try? Auth.auth().signOut()
GIDSignIn.sharedInstance.signOut()
LoginManager().logOut()
// The user has been deleted successfully
// TODO: Redirect to the login UI
}
}
func reauthenticate() {
// TODO: Display some UI to get credential from the user
let credential = ... // Complete from https://stackoverflow.com/a/38253448/8157190
Auth.auth().currentUser?.reauthenticate(with: credential) { _, error in
if let error = error {
print(error)
return
}
// Reload user (to update metadata.lastSignInDate)
Auth.auth().currentUser?.reload { error in
if let error = error {
print(error)
return
}
// TODO: Dismiss UI
// Call `deleteUserProcess()` again, this time it will delete the user
deleteUserProcess()
}
}
}
The minuets function can be added in an extension to Date (thanks to Leo Dabus):
extension Date {
/// Returns the amount of minutes from another date
func minutes(from date: Date) -> Int {
return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
}
}
you can first make your specific user deleted and and its value through its UID then you can deleted user and take him to root view controller or login screen after deleting it.
// removing user data from firebase and its specific user id
let user = Auth.auth().currentUser
user?.delete { error in
if let error = error {
// An error happened.
print(error.localizedDescription)
} else {
Database.database().reference().child("users").child(user?.uid ?? "").removeValue()
self.navigationController?.popToRootViewController(animated: true)
// Account deleted and logout user
// do {
// try Auth.auth().signOut()
// take you to root
// self.navigationController?.popToRootViewController(animated: true)
}

How can I add phone authentication in firebase using swift?

So firebase recently supported phone auth, but the problem is that the documentation is in objective-c. And I have never done push notifications before. this is a link to the objc phone auth docs: https://firebase.google.com/docs/auth/ios/phone-auth
You can see an example in the official sample repo: https://github.com/firebase/quickstart-ios/blob/master/authentication/AuthenticationExampleSwift/MainViewController.swift#L161
The core process is like this:
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber) { (verificationID, error) in
if let error = error {
// TODO: show error
return
}
guard let verificationID = verificationID else { return }
verificationCode = // TODO: get SMS verification code from user.
if let verificationCode = verificationCode {
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID, verificationCode: verificationCode)
Auth.auth().signIn(with: credential) { (user, error) in
// TODO: handle sign in
}
} else {
// Verification code was empty
}
}
}
You'll need to plug in UI to prompt the user (thats in the sample), or take advantage of FirebaseUI to handle it all for you: https://github.com/firebase/FirebaseUI-iOS

Invalid API Key supplied using Firebase

I'm using Firebase Auth to allow users to sign up using Facebook. I have taken all the steps from here to implement sign up including adding GoogleService-Info.plist to my project.
I get the Facebook permission screen all fine but when the app hits
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
this error is returned: An invalid API Key was supplied in the request.
Can anyone help me with this please?
Thanks
Here is my function code to log in using Facebook.
#IBAction func signUpWithFacebook() {
let fbLogin = FBSDKLoginManager()
fbLogin.logInWithReadPermissions(["email"], fromViewController:self, handler: {
(result, error) -> Void in
if ((error) != nil) {
print("Process error")
} else if (result.isCancelled) {
print("Cancelled");
} else {
print("Logged in");
let accessToken = FBSDKAccessToken.currentAccessToken().tokenString
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(accessToken)
print(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
// ...
if let user = user{
print(user.displayName)
}
else{
if let error = error {
print(error.localizedDescription)
}
}
}
}
})
}
Solved it, for anyone that needs the solution in the future.
Sometimes API_KEY is missing from the GoogleService-Info.plist, this needs to be added.
The API Key can be found from Google API Console https://console.developers.google.com/
You can solve this by Downloading again the "GoogleService-Info.plist" file, under the Project Setting section on Firebase Console.
Ensure that the API_KEY is set on the new "GoogleService-Info.plist".

Resources