I set up my app to be able to send Apple Notifications using firebase and I verified that it works using the console. Now I want to do phone authentication which is built on top of APN.
So I wrote this:
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber) { verificationID, error in
if error != nil {
print("Verification code not sent \(error!)")
} else {
print ("Successful.")
}
And I get:
Error Domain=FIRAuthErrorDomain Code=17999 "An internal error has occurred, print and inspect the error details for more information." UserInfo={NSUnderlyingError=0x170046db0 {Error Domain=FIRAuthInternalErrorDomain Code=3 "(null)" UserInfo={FIRAuthErrorUserInfoDeserializedResponseKey={
code = 500;
message = "<null>";
}}}, error_name=ERROR_INTERNAL_ERROR, NSLocalizedDescription=An internal error has occurred, print and inspect the error details for more information.}
Any idea? Should I file a bug against firebase?
I am using iOS SDK 4.0.0 (latest zip I could find.)
UPDATE:
I disabled method swizzling by adding FirebaseAppDelegateProxyEnabled
to info.plist
and set it to NO
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Pass device token to auth.
Auth.auth().setAPNSToken(deviceToken, type: .prod)
}
Tested with latest Firebase iOS SDK i.e. 4.0.0 and Xcode 8.3
Firstly , remove this key FirebaseAppDelegateProxyEnabled
from info.plist. This is not needed.
Now in AppDelegate.swift add following functions
import Firebase
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate , UNUserNotificationCenterDelegate{
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
FirebaseApp.configure()
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Pass device token to auth.
let firebaseAuth = Auth.auth()
//At development time we use .sandbox
firebaseAuth.setAPNSToken(deviceToken, type: AuthAPNSTokenType.sandbox)
//At time of production it will be set to .prod
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let firebaseAuth = Auth.auth()
if (firebaseAuth.canHandleNotification(userInfo)){
print(userInfo)
return
}
}*
Send a verification code to the user's phone:
In the class where you want to integrate Phone Authentication write :
Note : I have added +91
as its country code for India. You can add country code according to your region.
PhoneAuthProvider.provider().verifyPhoneNumber("+919876543210") { (verificationID, error) in
if ((error) != nil) {
// Verification code not sent.
print(error)
} else {
// Successful. User gets verification code
// Save verificationID in UserDefaults
UserDefaults.standard.set(verificationID, forKey: "firebase_verification")
UserDefaults.standard.synchronize()
//And show the Screen to enter the Code.
}
Sign in the user with the verification code:
let verificationID = UserDefaults.standard.value(forKey: "firebase_verification")
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID! as! String, verificationCode: self.txtEmailID.text!)
Auth.auth().signIn(with: credential, completion: {(_ user: User, _ error: Error?) -> Void in
if error != nil {
// Error
}else {
print("Phone number: \(user.phoneNumber)")
var userInfo: Any? = user.providerData[0]
print(userInfo)
}
} as! AuthResultCallback)
Double check that the app bundle ID in Xcode matches the bundle ID in Firebase exactly. And by exactly, make sure their case matches - Xcode likes to use mixed case by default for the app name part of the bundle ID.
If you end up changing the bundle ID in Xcode, make sure to manually delete the provisioning profile for the app before generating a new one in Xcode, or it will repeatedly fail (Apple apparently ignores case on profile names).
In my case it was the apns token type that was wrong:
Auth.auth().setAPNSToken(deviceToken, type: AuthAPNSTokenType.prod)
should have been:
Auth.auth().setAPNSToken(deviceToken, type: AuthAPNSTokenType.sandbox)
Well, in my case I have sending wrong self.verificationID
to FIRAuthCredential
.
If you are having this error, then please print your verificationID
and check, is that the same one you are sending to FIRAuthCredential
.
Here is my code in objC
:
[[FIRPhoneAuthProvider provider] verifyPhoneNumber:self.phoneNumberTextField.text
UIDelegate:nil
completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
NSLog(@"error %@", error.localizedDescription);
return;
}
NSLog(@"verificationID %@", verificationID);
self.verificationID = [NSString stringWithFormat:@"%@", verificationID];
// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// [defaults setObject:verificationID forKey:@"authVerificationID"];
// NSString *verificationID = [defaults stringForKey:@"authVerificationID"];
// Sign in using the verificationID and the code sent to the user
// ...
}];
I accidentally send wrong verificationID here :
self.verificationID = [NSString stringWithFormat:@"verificationID",];
Right one is this :
self.verificationID = [NSString stringWithFormat:@"%@", verificationID];
And then I send it to FIRAuthCredential
like this :
FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider]
credentialWithVerificationID:self.verificationID
verificationCode:self.pinCodeTextField.text];
[[FIRAuth auth] signInWithCredential:credential
completion:^(FIRUser *user, NSError *error) {
if (error) {
NSLog(@"error %@", error);
return;
}
NSLog(@"Success");
// User successfully signed in. Get user data from the FIRUser object
// ...
}];
Which return success
successfully. Hope it will help to others.