Firebase automatically signs out from the user session on iOS app - ios

I am struggling with Firebase authentication in my app. I have enabled the database persistence using:
[FIRDatabase database].persistenceEnabled = YES;
After the user logs in (email/password authentication), I am expecting the session to remain active until I explicitly signout:. To handle the changes in authorization, I set up the following (with some debugging code to check out the behavior based on the application state changes):
[[FIRAuth auth] addAuthStateDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) {
ZLog(YES, #"Signed in %#\n\n\n>>>>\n\n\n", user.uid);
[user getTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
ZLog(NO, #"Token %#\n\n\n####\n\n\n", token);
}];
} else {
ZLog(YES, #"Not Signed In\n\n\n>>>>\n\n\n");
FIRUser *user = [[FIRAuth auth] currentUser];
ZLog(YES, #"The user is %#", (user != nil) ? user : #"nil");
}
}];
everything seems to be ok even after reinstalling the app from Xcode while debugging. However as soon as the app goes in "Inactive state" (e.g. by pressing the Home button). I get the "Sign out" in my listener block. This does not happen if I just switch to another app when my app goes to "background" as observed by applicationDidEnterBackground.
What could be wrong? The documentation (https://firebase.google.com/docs/database/ios/offline-capabilities) suggests that the session should be saved across app restarts (which implies the cases like applicationWillResignActive and applicationDidBecomeActive, I assume).
I would not like to re-authenticate (with the safely saved email/password credentials) on every such change (and it would also defeat the 'offline' capabilities claim). Or would the re-authentication also work in 'offline' mode?
Going crazy trying to solve this - any clues would be helpful! Thx.

Related

Check if user is connected to his Apple Account, if not, ask him to connect to it

Is it possible to check if the user is connected to his Apple Account ? And if he is not, ask him to connect ?
Yes it is possible to get Apple account (iCloud) status by checking if the ubiquityIdentityToken object in NSFileManager is set. You can use something like this:
Swift 4
var iCloudAvailability: Bool {
return FileManager.default.ubiquityIdentityToken != nil
}
Swift 3
var icloudStatus: Bool {
return NSFileManager.defaultManager().ubiquityIdentityToken != nil ? true : false
}
Value of true would indicate that user is logged in, and false that user is not logged in, so you can present some kind of view to ask user to connect.
From the Apple documentation:
When iCloud is currently available, this property contains an opaque object representing the identity of the current user. If iCloud is unavailable for any reason or there is no logged-in user, the value of this property is nil. Accessing the value of this property is relatively fast so you can check the value at launch time from your app’s main thread.
If you are checking for iCloud services then from CloudKit you can use CKContainer class to get user account status as below
CKContainer *container = [CKContainer defaultContainer];
[container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
//Here goes your code to handle the account status
}];
iOS 10.x swift 4.0 Based on the Said Sikira answer
func icloudStatus() -> Bool?{
return FileManager.default.ubiquityIdentityToken != nil ? true : false
}

Why does FBSDKAccessToken.currentAccessToken() returns nil on my device but works fine in the iOS simulator?

In my IOS9 application I use the Facebook SDK 4.7.0 graph api to search for public places.
I use the FBSDKApplicationDelegate in my apps delegate which I believe provides me with valid app access token (since I don't use any user specific data an application login is sufficient for me).
When I call FBSDKAccessToken.currentAccessToken() in my ViewController to check if the token is valid I always succeed in the simulator but not when deployed on the real device.
Any suggestions?
I have followed the new ios9 guide and provided the additional info in my plist.
Since my application is still in development my Facebook companion app (which holds my app id and secret) are still not available for general public. Could this be a problem?
for checking facebook permission..& give a permission...if permission exist then automatically get accesstoken other wise ask for login...
For Swift
var login: FBSDKLoginManager = FBSDKLoginManager()
login.logInWithReadPermissions(["public_profile", "email"], handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if (error != nil)
{
//Process error
}
else if result.isCancelled
{
//Handle cancellations
}
else
{
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
if result.grantedPermissions.contains("email"){
//Do work
}
}
})
For Objective c:
check Permission like this. following code use ..
if ([[FBSDKAccessToken currentAccessToken]hasGranted:#"email"])
{
// add your coding here after login call this block automatically.
}
else
{
//login code **//if accesstoken expired...then call this block**
FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];
[loginManager logInWithReadPermissions:#[#"public_profile", #"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)
}];
}

How do I store & access a Twitter Fabric login session (iOS/Swift)?

I'm able to log in to Twitter through my app using this Twitter Fabric code:
let logInButton = TWTRLogInButton(logInCompletion: {
(session: TWTRSession!, error: NSError!) in
// play with Twitter session
if (session != nil) {
println("signed in as \(session.userName)");
self.TWUsernameLabel.text = "Logged in as #" + session.userName
} else {
println("error: \(error.localizedDescription)");
}
})
When I click the login button, it prompts me to approve the login and then logs me in, or it knows I already approved the login and it logs me in. This works like a charm and took all of ten minutes to set up. Amazing.
I already have an email-based login to access the app. I'd like to store a user's logged in Twitter account in that same database, so when a user logs in with their email, I already know their Twitter (if they have logged in before) and they don't need to log in again. The reason I do the email login is because Twitter is an important feature in my app, but not a total requirement.
The issue is that I have no idea how to access session outside of when the button is clicked and logInCompletion fires, and I don't know what variables to store upon initial login/check upon using the app.
I've read through the Twitter Fabric documentation numerous times, but it isn't written in swift, so it's pretty confusing. Any ideas? Thanks
If there is currently an active session you should be able to access it just like the docs say to
Twitter.sharedInstance().session()
If the user isn't logged in that method will return nil. If you want to know if someone is already authenticated just check to see if that method returns a value or not.
Twitter kit automatically present a login screen if user has not logged in (no session exist..).
But, before calling any future API, just check whether session is valid or not. In some APIs, you may required to pass this session too.
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController *shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString *email, NSError *error) {
NSLog(#"Email %# | Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
} else {
// Handle user not signed in (e.g. attempt to log in or show an alert)
}
}
You even don't need to worry about storing and managing access token and access token secret.
To access the authToken, userName or the userID for the logged in user session in Swift 4 is:
Twitter.sharedInstance().logIn(completion: { (session, error) in
if (session != nil) {
print(session!.authToken)
print(session!.userName)
print(session!.userID)
} else {
print(error?.localizedDescription)
}
})

iOS - programatically rejecting permission to contacts (after user has accepted)

Is there a way to programatically reject/disable/disallow/reject permissions to access the calendar?
I'm using the following code to ask for / determine permissions:
//request access
[[MyCalendarUtil sharedManager] requestAccess:^(BOOL granted, NSError *error) {
/* This code will run when uses has made his/her choice */
if(error)
{
// display error message here
_calendarLabel.text = #"Calendar OFF";
}
else
if(!granted)
{
// display access denied error message here
_calendarLabel.text = #"Calendar OFF";
}
else
{
// access granted
_calendarLabel.text = #"Calendar ON";
}
}];
Can I disable the permission later on upon a user pressing a button ... ?
No way, its impossible. Take it granted. I will give 1 Million dollars even if you find a private API. :)
Its a privacy concern.
All you can do is, programmatically launching your application's preferences in the Settings App, and again its users wish to disable or enable the access.

Handling multiple Facebook user logins in an iOS app

I have an iOS application that can have multiple users and I want to integrate Facebook into it. I would like to cache each user's login credentials. The users will have an application login so each user will only be able to use there own Facebook creds. Facebook's documentation states that you can manage multiple user's session tokens by overriding the FBSessionTokenCachingStrategy object here:
https://developers.facebook.com/docs/ios/login-tutorial/
I am using the local caching method and I have it working where a user logs into my application and then if they have a related Facebook token I log them into there session. Otherwise I prompt them to enter there Facebook credentials using Facebook's API that redirects the user to the Facebook web login page.
[FBSession openActiveSessionWithReadPermissions:#[#"basic_info"]
allowLoginUI:YES
completionHandler:
^(FBSession *session, FBSessionState state, NSError *error)
{
[self loginToFacebookEnd:session state:state error:error];
}];
The above method works great for the FIRST USER. The second user that goes to login gets redirected to the Facebook web login page as expected, but the first user is now logged in through safari and the second user is now logged in with the first users credentials.
I want to be able to either not have the user stay logged in on safari, OR be able to use something like the embedded web dialog. According to the Facebook API docs however, the embedded dialog however doesn't seem to let me load the users token after the app restarts:
https://developers.facebook.com/docs/ios/login-tutorial/#control
"Embedded WebView Login Dialog Disadvantage: People have to fill in
their login credentials every time they go through the login flow."
Also, calling the following method clears out my token cache when the token was saved from an Embedded WebView:
FBSession *session = [[FBSession alloc] initWithAppID:nil
permissions:#[#"basic_info"]
urlSchemeSuffix:nil
tokenCacheStrategy:tokenCaching];
Does anyone know how to support more than one user logging into Facebook from an iOS app without the first user's credentials getting cached in Safari?
UPDATE 1
Ok, I know it is possible to handle multiple users using the Embedded WebView now, because one of the sample programs Facebook includes with the SDK (SwitchUserSample) does it. Something seems to be wrong with loading my FBSession, as a lot of the values are null, though I am calling exactly the same code as the example. Does anyone know what would cause a session to restore like this:
<FBSession: 0x16d9f3a0, state: FBSessionStateCreated, loginHandler: 0x0, appID: 658013034244859, urlSchemeSuffix: , tokenCachingStrategy:<FBSessionTokenCachingStrategy: 0x16d56190>, expirationDate: (null), refreshDate: (null), attemptedRefreshDate: 0001-12-30 00:00:00 +0000, permissions:(null)>
If I use any behavior other than FBSessionLoginBehaviorForcingWebView, then I get this as my deserialized token:
<FBSession: 0x155823d0, state: FBSessionStateOpen, loginHandler: 0x73364, appID: 658013034244859, urlSchemeSuffix: , tokenCachingStrategy:<FBSessionTokenCachingStrategy: 0x15577c90>, expirationDate: 2014-02-15 20:01:50 +0000, refreshDate: 2013-12-17 20:01:51 +0000, attemptedRefreshDate: 0001-12-30 00:00:00 +0000, permissions:(
"user_birthday",
"user_friends"
)>
UPDATE 2
I have identified that it is definitely something to do with how my token is getting cached. I have copied over the methods the Facebook example is using but I am still getting null values for my dates in the FBSession. Here are the caching methods I am calling:
+ (FBSessionTokenCachingStrategy*)createCachingStrategyForSlot:(int)slot {
FBSessionTokenCachingStrategy *tokenCachingStrategy = [[FBSessionTokenCachingStrategy alloc]
initWithUserDefaultTokenInformationKeyName:[NSString stringWithFormat:#"SUUserTokenInfo%d", slot]];
return tokenCachingStrategy;
}
+ (FBSession*)createSessionForSlot:(int)slot {
FBSessionTokenCachingStrategy *tokenCachingStrategy = [self createCachingStrategyForSlot:slot];
FBSession *session = [[FBSession alloc] initWithAppID:nil
permissions:#[#"basic_info",#"user_birthday"]
urlSchemeSuffix:nil
tokenCacheStrategy:tokenCachingStrategy];
return session;
}
And here is how I am calling it:
+ (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI
{
BOOL openSessionResult = NO;
FBSession *session = [self createSessionForSlot:1];
// If showing the login UI, or if a cached token is available, then open the session.
if (allowLoginUI || session.state == FBSessionStateCreatedTokenLoaded)
{
// For debugging purposes log if cached token was found
if (session.state == FBSessionStateCreatedTokenLoaded)
{
NSLog(#"Cached token found.");
}
// Set the active session
[FBSession setActiveSession:session];
// If the session state is not any of the two "open" states when the button is clicked
if (FBSession.activeSession.state != FBSessionStateOpen && FBSession.activeSession.state != FBSessionStateOpenTokenExtended)
{
// Open the session.
[session openWithBehavior:FBSessionLoginBehaviorForcingWebView
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error)
{
[SocialInteraction loginToFacebookEnd:session state:state error:error];
}];
}
// Return the result - will be set to open immediately from the session
// open call if a cached token was previously found.
openSessionResult = session.isOpen;
}
return openSessionResult;
}
The frustrating part is that if I change the login behavior from FBSessionLoginBehaviorForcingWebView to FBSessionLoginBehaviorWithFallbackToWebView, then the token loads correctly! But, this behavior won't work for my application. There must be some other setting I am missing that would let the embedded WebView cache its token.
I'm moving this from comment to answer so I can type more:
If the state of your FBSession instance is FBSessionStateCreated then you wouldn't have the expirationDate properties etc. From my experience it has to be effectively open (FBSessionStateCreatedTokenLoaded, FBSessionStateOpen, FBSessionStateOpenTokenExtended). Does your FBSession object have an accessToken associated with it? If it does, then go ahead and try to open the session and see if the status changes and you get values where it used to be NULL.
To open the FBSession when I have a valid accessToken I usually use:
[FBSession openActiveSessionWithAllowLoginUI:NO];
UPDATE 1:
Ok so this is kind of a workaround to the web view - if you use Safari, you can clear the user's cookies using the code below. I would probably search the cookie's domain for Facebook.com or fb.com (probably requires some testing as to what the FB cookie domain is):
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies]) {
[storage deleteCookie:cookie];
}
[[NSUserDefaults standardUserDefaults] synchronize];

Resources