Back when I was using Parse, it seemed like the SDK would store the session data locally and the user didn't have to log in again after refreshing the page (or exiting the mobile app). This doesn't seem to be the case with Firebase/Angularfire; everytime I refresh my web page, the authentication data gets lots. This seems like really basic and important functionality that I would be surprised the awesome people at Firebase haven't implemented. Am I missing something?
For completeness; here is my code in app.run():
// ASG june 2016 - Upgrade firebase SDK
firebase.initializeApp(FirebaseConfig);
// login as anonymous if not already logged in
var currentUser = $firebaseAuth().$getAuth();
if (currentUser) {
console.log("Signed in as:", currentUser);
} else {
console.log("Not logged in; going to log in as anonymous");
$firebaseAuth().$signInAnonymously().then(function(authData) {
console.log("Signed in anonymously as:", authData.uid);
}).catch(function(error) {
console.error("Anonymous authentication failed:", error);
});
}
// register the on auth callback
$firebaseAuth().$onAuthStateChanged(function(authData) {
if (authData) {
console.log("Logged in as:", authData.uid);
if(typeof($rootScope.userProfile) == "undefined"){
$rootScope.userProfile = FirebaseProfileService.getUserProfile(authData.uid, false);
}
}
});
Firebase absolutely presists authData and has a lot of goodness you hardly find elsewhere. Therefore, I don't see any logical reason behind calling getAuth() here as you can easily get the authData from the onAuthStateChanged listener. In this case, you may need to remove your operations of getting currentUser using getAuth() and move your ELSE condition to onAuthStateChanged if you want to sign up users as anonymous in case no AuthData was found, and then you will be good to go, I hope. Hope that works.
The auth token is persisted between page/app reloads. But it will often need to get a new refresh token when the page reloads, which requires a round-trip to the Firebase servers. Since this takes time, the initial getAuth() may return null while that process is ongoing.
var currentUser;
$firebaseAuth().$onAuthStateChanged(function(authData) {
if (authData) {
console.log("Logged in as:", authData.uid);
currentUser = authData.currentUser;
if(typeof($rootScope.userProfile) == "undefined"){
$rootScope.userProfile = FirebaseProfileService.getUserProfile(authData.uid, false);
}
}
else {
console.log("Not logged in; going to log in as anonymous");
currentUser = null;
$firebaseAuth().$signInAnonymously().catch(function(error) {
console.error("Anonymous authentication failed:", error);
});
}
});
Related
So I have a weird bug that I can't seem to track down. I'm using firebase functions on the backend and SwiftUI. My login flow goes like this:
User logs in from loginView. The loginView then uses a callback to pass a user to move on to the next View after a user logs in.
After this a user is passed to the View where it calls the firebase functions.
The problem is that every once in a while a user fails authentication. This doesn't happen every time and usually happens when a user has not logged in for 12 hours or more. I thought it may have been a race condition at first but after further investigation decided that it wasn't given the fact that it's using a callback.
Has anyone else experienced anything similar? If so is there any way to fix this?
I have tried making my firebase function stay warm by setting minimum instances to 1 because I initially thought it may be a cold start issue but this hasn't helped.
Any help would be greatly appreciated. Thanks.
On the frontend the code is pulling like so:
FirebaseAuthService().signIn(email: email, password: password) { result, error in
if (error?.occurred) != nil {
self.errorMessage = error!.details
self.state = .failed
self.showErrorAlert = true
return
}
if (localAuthEnabled) {
...... This piece of code works
FirebaseFirestoreService().fetchUser(userID: result?.user.uid ?? "", completion: { user, error
....... This piece of code works.
}
})
}
User is then taken to another view AFTER logging in
This view pulls from 5 or so firebase functions asynchronously (but the user is already logged in by this point). The function that it fails at is as follows
self.function.httpsCallable("api-report").call() { (result, error) in
... It is at times it gives me an auth error inside of this function.
}
I am using this to log out whenever a user put the app in the background or hits the log out button:
func signOut() -> Bool {
do {
try Auth.auth().signOut()
self.session = nil
return true
} catch let err as NSError {
print("Error signing out: %#", err)
return false
}
}
on the backend the report call does the following with the report. This is a large function. I have only added the call to show whats going on.
exports.handler = async (data, context) => {
if (!context.auth) {
console.log("report error context");
console.log(context);
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'by authenticated users.', 'User must be authenticated');
}
}
It seems the call to your backend may be happening just as/after the user is logged out, or their token is being refreshed. If it's the token being refreshed, the client should just retry the call in cases such as this.
You could also check whether the token is about to expire, and force a refresh when that is the case, or delay calling the Cloud Function until the token has refreshed.
I'm setting up the Facebook Connection to GameSparks server with FacebookConnectRequest using Facebook SDK for Unity. However, I'm getting an error response with key "accessToken" and value "NOTAUTHENTICATED". The details of the error is "The system was unable to authenticate the token.".
I have tried to reimport Facebook and GameSparks SDK in Unity. Change the some initialization of the Facebook and GameSpark in the code. However, I could not come up with a solution.
public void ConnectPlayerViaFacebook()
{
ChangeCurrentText("Connecting Facebook With Server...");
Debug.Log("Connecting Facebook With GameSparks...");// first check if FB is ready, and then login //
// if it's not ready we just init FB and use the login method as the callback for the init method //
if (!FB.IsInitialized)
{
ChangeCurrentText("Initializing Facebook...");
Debug.Log("Initializing Facebook...");
FB.Init(ConnectGameSparksToGameSparks, null);
}
else
{
FB.ActivateApp();
ConnectGameSparksToGameSparks();
}
}
///<summary>
///This method is used as the delegate for FB initialization. It logs in FB
/// </summary>
private void ConnectGameSparksToGameSparks()
{
if (FB.IsInitialized)
{
FB.ActivateApp();
Debug.Log("Logging into Facebook...");
ChangeCurrentText("Logging into Facebook...");
var perms = new List<string>() { "public_profile", "email" };
FB.LogInWithReadPermissions(perms, (result) =>
{
if (FB.IsLoggedIn)
{
ChangeCurrentText("Logged in, Connecting Server via Facebook...");
new FacebookConnectRequest()
.SetAccessToken(AccessToken.CurrentAccessToken.TokenString)
.SetDoNotCreateNewPlayer(false)
.SetDoNotLinkToCurrentPlayer(false)
.SetSwitchIfPossible(false)
.SetSyncDisplayName(true)
.Send((fbauth_response) =>
{
if (!fbauth_response.HasErrors)
{
...
}
else
{
Debug.Log(fbauth_response.Errors.JSON.ToString());
ChangeCurrentText("Server Authentication with Facebook Failed!" + fbauth_response.Errors.JSON.ToString());
}
});
}
else
{
Debug.LogWarning("Facebook Login Failed!" + result.Error.ToString());
ChangeCurrentText("Facebook Login Failed!" + result.Error.ToString());
}
});
}
else
{
ConnectPlayerViaFacebook(); // If we are still not connected, then try to process again
}
}
I want to remove the error response of the FacebookConnectRequest of the GameSparks request.
Thanks to derHugo's advice I have solved the problem. For some reasons the acces token is broken before the FacebookConnectionRequest. To prevent any undesirable situation about access token it needs to be manually refreshed. To do it, FB.Mobile.RefreshCurrentAccessToken needs to be used before FacebookConnectionRequest.
The explanation of that function is as follows: It may be desireable to manually refresh the current access token granted to the application by the user in order to retrieve up-to-date permissions, and extend the expiration date, if extension is possible. Use FB.Mobile.RefreshCurrentAccessToken to accomplish this. (source)
if (FB.IsLoggedIn)
{
FB.Mobile.RefreshCurrentAccessToken(callback =>
{
...
});
ChangeCurrentText("Logged in, Connecting Server via Facebook...");
new FacebookConnectRequest()
.SetAccessToken(AccessToken.CurrentAccessToken.TokenString)
.SetDoNotCreateNewPlayer(false)
.SetDoNotLinkToCurrentPlayer(false)
.SetSwitchIfPossible(false)
.SetSyncDisplayName(true)
.Send((fbauth_response) =>
{
...
});
}
else
{
...
}
We are in the process of building a web based application, using React and making use of a REST based server. The question that came up is how to best manage a previously authenticated connection becoming unauthenticated and whether there are any React or React/Redux design patterns for this?
Previous designs had used sessions, but since the REST server is meant to be stateless, the current REST server design will probably look at follows:
POST /auth/authenticate --> Authenticate and provides a token
DELETE /auth/token --> Invalidates the token, black-list token
PUT /auth/token --> Renews token, if still valid
GET /auth/token --> Indicates if token is still valid
On the client side the current draft design is to wrap the fetch function with our own function for dealing with checking the response state and then either calling a client side service for invalidating state and redirecting to login page or doing the same thing with Redux. We are also looking to add an interval timer to check the token we have and then automatically do the same thing if the 'exp' field is present and past.
The function we have at this point is (it does not take care of revalidation of token, at this point, to prevent an activate 'session' expiring):
function handleFetchResponse(fetchResponse) {
let newResponse;
try {
return fetchResponse.then((response) => {
newResponse = response;
const contentType = response.headers.get('content-type');
if (contentType && contentType.indexOf("application/json") !== -1) {
return response.json();
} else if (response.status === 200) {
return response.blob();
} else {
if (response.status === 401) {
forceLogout();
}
throw new RequestError(response.statusText, response.status);
}
}).then((responseBody) => {
if (newResponse.status === 401) {
forceLogout();
return;
} else if (newResponse.status !== 200) {
if (responseBody) {
throw new RequestError(
responseBody.details ||
newResponse.statusText,
newResponse.status
);
}
throw new RequestError(newResponse.statusText, newResponse.status);
}
return responseBody;
});
} catch (error) {
console.error('handleFetchResponse:error', error);
return Promise.resolve(err)
.then((err) => {
throw err;
})
}
}
It would be called via:
return handleFetchResponse( fetch(...) );
Is this an acceptable design or are they ways to improve on this?
I have an app built with ionic and firebase. I am using facebook4 cordova plug in to log in with facebook. When they log in, I get the name and email address of the user.. However in some devices, I am getting null emails addresses. When I go to auth section in firebase, I see their email address as (-) empty although the displayname is returning correct. Here is my code.
facebookConnectPlugin.login(['email', 'public_profile', 'user_friends'], //first argument is an array of scope permissions
function (userData) {
if (userData.authResponse) {
facebookConnectPlugin.api('me/?fields=email,name,first_name,last_name', ["public_profile"],
function (inforesult) {
facebookConnectPlugin.getAccessToken(function (token) {
//alert("Token: " + token);
var credential = firebase.auth.FacebookAuthProvider.credential(token);
firebase.auth().signInWithCredential(credential).then(function (result) {
alert(JSON.stringify(result)); // the email field is null.
$scope.myprofile = result;
}).catch(function (error) {
// Handle Errors here.
alert(error.message);
/ ...
});
});
});
}
},
function (error) {
alert(error);
}
)
Is there some kind of permissions I am missing?
They may not have their email approved, or they login with their mobile phones. You canĀ“t be sure that every user got an email.
I'm using CakePHP 2.3.1 and this plugin: https://github.com/webtechnick/CakePHP-Facebook-Plugin to implement a facebook authentication. I followed this screen cast http://tv.cakephp.org/video/webtechnick/2011/01/12/nick_baker_--_facebook_integration_with_cakephp but it doesn't work, I can't receive the Facebook user information. I mean $this->Connect->user() always return null.
First make sure your user as granted access to your app. Then make a call to retrieve personal details through the api(/me) => $this->Connect->user()
Personally, I use this
public function beforeFilter(){
if($this->Connect->FB->getUser() != 0){
$this->set('facebookUser', $this->Connect->user());
}
Initiating ConnectComponent will populate (or not) the Auth Session. If no id is to be found, redirect user to a login dialog.
Call this method from beforefilter..and save the post data received in some variable like fb_data here.
protected function _valdiateFbRequest() {
if (!isset($this->request->data['signed_request'])) {
// not a valid request from fb
// throw exception or handle however you want
return;
}
$this->signedRequest = $this->request->data['signed_request'];
$this->fb_data=$this->Connect->registrationData(); //save fb data
unset($this->request->data['signed_request']);
if (empty($this->request->data)) {
$this->Security->csrfCheck = false;
}
// validate the request
}