I wanna check if the user has still a valid session, before I present the Home View controller of my app. I use the latest Firebase API. I think if I use the legacy, I'll be able to know this.
Here's what I did so far:
I posted my question on Slack community of Firebase, no one is answering. I found this one, but this is for Android: https://groups.google.com/forum/?hl=el#!topic/firebase-talk/4HdhDvVRqHc
I tried reading the docs of Firebase for iOS, but I can't seem to comprehend it: https://firebase.google.com/docs/reference/ios/firebaseauth/interface_f_i_r_auth
I tried typing in Xcode like this:
FIRApp().currentUser()
FIRUser().getCurrentUser()
But I can't seem to find that getCurrentUser function.
if FIRAuth.auth().currentUser != nil {
presentHome()
} else {
//User Not logged in
}
For updated SDK
if Auth.auth().currentUser != nil {
}
Updated answer
Solution for latest Firebase SDK - DOCS
// save a ref to the handler
private var authListener: AuthStateDidChangeListenerHandle?
// Check for auth status some where
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
authListener = Auth.auth().addStateDidChangeListener { (auth, user) in
if let user = user {
// User is signed in
// let the user in?
if user.isEmailVerified {
// Optional - check if the user verified their email too
// let the user in?
}
} else {
// No user
}
}
}
// Remove the listener once it's no longer needed
deinit {
if let listener = authListener {
Auth.auth().removeStateDidChangeListener(authListener)
}
}
Original solution
Solution in Swift 3
override func viewDidLoad() {
super.viewDidLoad()
FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
if user != nil {
self.switchStoryboard()
}
}
}
Where switchStoryboard() is
func switchStoryboard() {
let storyboard = UIStoryboard(name: "NameOfStoryboard", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ViewControllerName") as UIViewController
self.present(controller, animated: true, completion: nil)
}
Source
Solution in Swift 4
override func viewDidLoad() {
super.viewDidLoad()
setupLoadingControllerUI()
checkIfUserIsSignedIn()
}
private func checkIfUserIsSignedIn() {
Auth.auth().addStateDidChangeListener { (auth, user) in
if user != nil {
// user is signed in
// go to feature controller
} else {
// user is not signed in
// go to login controller
}
}
}
if Auth.auth().currentUser?.uid != nil {
//user is logged in
}else{
//user is not logged in
}
While you can see if there is such a user using Auth.auth().currentUser, this will only be telling you if there was a user authenticated, regardless of whether that users account still exists or is valid.
Complete Solution
The real solution to this should be using Firebase's re-authentication:
open func reauthenticate(with credential: AuthCredential, completion: UserProfileChangeCallback? = nil)
This assures (upon the launch of the application) that the previously signed in / authenticated user still in fact is and can be authenticated through Firebase.
let user = Auth.auth().currentUser // Get the previously stored current user
var credential: AuthCredential
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
override func viewDidLoad() {
FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
// 2
if user != nil {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "Home")
self.present(vc!, animated: true, completion: nil)
}
}
}
Source: https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2
An objective-c solution would be (iOS 11.4):
[FIRAuth.auth addAuthStateDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) {
// your logic
}
}];
All the provided answers only check on currentUser. But you could check the auth session by simple user reload like below:
// Run on the background thread since this is just a Firestore user reload, But you could also directly run on the main thread.
DispatchQueue.global(qos: .background).async {
Auth.auth().currentUser?.reload(completion: { error in
if error != nil {
DispatchQueue.main.async {
// Authentication Error
// Do the required work on the main thread if necessary
}
} else {
log.info("User authentication successfull!")
}
})
}
Related
i just want to update authenticate email address of current user. i have tried lot's of solution like updateEmail method of firebase but it not work !! if any one know then please tell me how can i achieved this Thanks in advance !!
#IBAction func btnResetEmailClick(_ sender: UIButton) {
let auth = Auth.auth()
guard let email = self.txtEmailAddress.text ?? auth.currentUser?.email else { return }
// email that i have to update with current user email
auth.currentUser?.updateEmail(to: (auth.currentUser?.email)!, completion: { (error) in
if error == nil{
}else{
}
})
}
To change the email address the user has to be logged in recently i would suggest doing this:
var credential: AuthCredential
#IBAction func changeEmail() {
if let user = Auth.auth().currentUser {
// re authenticate the user
user.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
user.updateEmail(to: "email") { (error) in
// email updated
}
}
}
}
}
This is effective method to solve it.
let user = Auth.auth().currentUser
user?.updateEmail(to: "email") { error in
if error != nil {
// An error happened
} else {
// Email updated.
}
}
I want to dismiss a ViewController based on changes in the Firebase currentUser parameter isEmailVerified. Basically, if the user logs in to the app and hasn't verified her email, this VC will be presented and my plan is to have it stay there until she verifies it, in which case Firebase automatically sets isEmailVerified to true and the VC should be dismissed. So far I have attached a listener to recognize changes in the currentUser in but it doesn't seem to work. What am I doing wrong?
override func viewDidLoad() {
super.viewDidLoad()
// Create listener
let _ = Auth.auth().addStateDidChangeListener { (auth, user) in
if user?.isEmailVerified == true {
self.presentingViewController?.dismiss(animated: true, completion: nil)
}
}
}
[[FIRAuth auth] signInWithEmail:emailTxt.text
password:pwdStr
completion:^(FIRUser *user, NSError *error) {
if(error == nil){
if (user.isEmailVerified) {
// present different VC
// if user is not isEmailVerified is not true show alert to user
please verify email in order to proceed
}
} withCancelBlock:^(NSError * _Nonnull error) {
// dismiss view
}];
Here you can see what state user is with email verification and depending show the View Controller or do nothing
Otherwise after above method use a timer method inside else condition of email verification to fire below method and check user verified email or not
func checkIfTheEmailIsVerified(){
FIRAuth.auth()?.currentUser?.reload(completion: { (err) in
if err == nil{
if FIRAuth.auth()!.currentUser!.isEmailVerified{
// Your code
} else {
print("It aint verified yet")
}
} else {
print(err?.localizedDescription)
}
})
}
When I delete an account on my Firebase console, the user stills logged in to my app, I use this code to keep the user logged.
How can I log out the user when I delete his account?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
//check if user is logged in
if FIRAuth.auth()?.currentUser != nil {
//if user if logged in
let vc = self.storyboard?.instantiateViewController(withIdentifier: "mainVC")
self.present(vc!, animated: false, completion: nil)
}
}
But I don't know how to check if the account that is using the user is valid or not (if the account continues at the firebase console or not) before the "automatic log in".
Hope someone can help me out!!!
Thanks in advance!
We have similar implementation, so hopefully this solves your problem.
if let currentUser = Auth.auth().currentUser {
currentUser.getIDTokenForcingRefresh(true) { error in
if let error = error {
// log out
} else {
// go in
}
}
} else {
// log in
}
This forces Auth to communicate with Firebase to get a new authentication token, which would fail if the user was deleted.
The Kotlin implementation would be :
firebaseAuth.currentUser?.getIdToken(true)
firebaseAuth.currentUser == null
I have a custom button that I use for Facebook login, and it was working fine until recently. The access token was cached and the next time the user launched the app, the continue button was displayed in its place.
Recently however the marked line returns nil regardless of whether the user has previously logged in. I'm at a loss as to why - I haven't made any code changes in this part of the app?
Occasionally the login will fail with the following error also:
Error Domain=com.facebook.sdk.login Code=308 "(null)"
Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
if (FBSDKAccessToken.currentAccessToken() == nil){ // <<<< ALWAYS RETURNS NIL
self.continueButton.hidden = true
} else {
self.loginButton.hidden = true
self.notYouButton.hidden = false
}
}
#IBAction func loginPressed(sender: AnyObject) {
let permissions = ["user_about_me","user_relationships","user_birthday","user_location","user_status","user_posts", "user_photos"]
let login = FBSDKLoginManager()
login.logInWithReadPermissions(permissions, handler: {
(FBSDKLoginManagerLoginResult result, NSError error) -> Void in
if(error == nil){
self.loginButton.hidden = true
self.continueButton.hidden = false
self.notYouButton.hidden = false
self.notYouButton.enabled = false
//self.performSelector("showBrowse", withObject: nil, afterDelay: 1.0)
} else {
print(error)
}
})
}
EDIT: On further testing it seems that calling FBSDKAccessToken.currentAccessToken() is returning nil if called in viewDidLoad(), but if I call it from a button press it returns the Facebook token as expected.
override func viewDidLoad() {
super.viewDidLoad()
if let token = FBSDKAccessToken.currentAccessToken() {
print (token)
} else {
print ("no token") <<<<< RETURNS
}
}
#IBAction func buttonPressed(sender: AnyObject) {
if let token = FBSDKAccessToken.currentAccessToken() {
print (token) <<<<< RETURNS
} else {
print ("no token")
}
}
It turns out that there was a problem in my appDelegate where I was setting up a custom View Controller. I reverted the code to use storyboards and the issue was resolved - not a resolution per se for anyone with similar issues but it's enough for me to get on.
I'm trying to get user information using a custom facebook login with the Facebook SDK for iOS. I successfully log in the user but I don't know how to get access to the user information.
This is my login function:
func facebookLogin() {
if (FBSession.activeSession().state == FBSessionState.Open || FBSession.activeSession().state == FBSessionState.OpenTokenExtended)
{
// Close the session and remove the access token from the cache
// The session state handler (in the app delegate) will be called automatically
FBSession.activeSession().closeAndClearTokenInformation()
}
else
{
// Open a session showing the user the login UI
// You must ALWAYS ask for public_profile permissions when opening a session
FBSession.openActiveSessionWithReadPermissions(["public_profile", "email"], allowLoginUI: true, completionHandler: {
(session:FBSession!, state:FBSessionState, error:NSError!) in
self.sessionStateChanged(session, state: state, error: error)
})
}
}
Then when the session is open the function sessionStateChanged is called:
func sessionStateChanged(session:FBSession, state:FBSessionState, error:NSError?){
if ((error) != nil){
NSLog("Error")
FBSession.activeSession().closeAndClearTokenInformation()
}
else{
if (state == FBSessionState.Open){
//I would like to get the user token or FBGraphUser here but i don't know how
}
}
if (state == FBSessionState.Closed || state == FBSessionState.ClosedLoginFailed){
NSLog("Session Clossed")
}
if (FBErrorUtility.shouldNotifyUserForError(error) == true){
NSLog("Something went wrong")
}
else{
if (FBErrorUtility.errorCategoryForError(error) == FBErrorCategory.UserCancelled){
NSLog("User cancelled login")
}
else if (FBErrorUtility.errorCategoryForError(error) == FBErrorCategory.AuthenticationReopenSession){
NSLog("Current session is no valid")
}
}
}
Ran into the exact same problem today! I'm not sure if this is the ideal way of doing things, but this is the workaround that my colleague showed me:
FBSession.openActiveSessionWithReadPermissions(["public_profile"], allowLoginUI: false) { session, state, error in
let request = FBRequest(session: FBSession.activeSession(), graphPath: "/me")
request.startWithCompletionHandler({ (connection, result, error) -> Void in
println(result)
let birthday = result.valueForKey("birthday") as String
println("\(birthday)")
})
self.fbSessionStateChanged(session, state: state, error: error)
}
Please forgive the ugly code, I'm still very much experimenting with Swift. Hope it helps!
You can use this:
Add FBLoginViewDelegate to the class
Declare this
#IBOutlet var fbLoginView : FBLoginView!
Connect this var (fbLoginView) with a new view in your storyboard. This new view can be hidden so it wont has any effect but helping you to track the information
In your viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
//facebook login
self.fbLoginView.delegate = self
self.fbLoginView.readPermissions = ["public_profile", "email"]
}
And add this function
func loginViewFetchedUserInfo(LoginView : FBLoginView!, user: FBGraphUser){
println("User name: \(user.name)")
//THE USER WAS LOGGED
}
When the user is authenticated by facebook, returns to loginViewFetchedUserInfo method automatically by using the FBLoginViewDelegate and you can get the information with the "user" variable