Inconsistent Contacts.getPermissions() response with #capacitor-community/contacts plugin - capacitor

In my Angular 13 Ionic 6 app, I am attempting to fetch all contacts for the user, in a component:
import { Contacts } from '#capacitor-community/contacts';
ngOnInit() {
Contacts.getPermissions().then((response) => {
console.log('Contacts permission response: ', response);
if (response.granted) {
console.log('Granted permissions for contacts');
this.dialogs.openLoader('Your contacts are coming up...');
Contacts.getContacts().then((result) => {
this.foundContacts = true;
console.log('Got contacts result: ', result);
this.contacts = result.contacts;
this.selectedContactName = '';
this.dialogs.closeLoader();
});
}
});
}
The response coming back from the getPermissions() promise is inconsistent:
Upon initial run of the app, when the permissions dialog opens for the user, it returns this (console.log output):
Contacts permission response: {contacts: 'granted'}
Re-openning the component without the permissions dialog box opening, it looks different:
Contacts permission response: {granted: true}
In the first case, the IF test (response.granted) fails, which is not the expected behavior.
What is happening here?

It’s a known issue that has been reported on their issue tracker
https://github.com/capacitor-community/contacts/issues/57

Related

Facebook Connection Failed to GameSparks Server in Unity

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
{
...
}

Ionic 2 - Storage always empty until reloading the app

I am currently building an application with Ionic 2 and using the Storage plugin to hold my values which are pretty much just an API Token and user profile since the application pulls all data from an API.
I am testing the application via ionic serve because no native functions are used but now I am facing the problem that every time I store a value in the Storage the value is not accessible until I reload the app which is kind of annoying because after the user logs in he gets redirected to a page that requires the API token which is not available until I reload the app so the whole thing gets stuck in a loop.
Ionic Storage is using IndexedDB in the browser where I can see that the values have been stored when I check them with Chrome Developer tools.
I have been trying to figure out the issue but can't find any reason why the storage values are not available until reloading the app.
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { HttpClientService } from './http-client-service';
import 'rxjs/add/operator/map';
#Injectable()
export class AuthService {
constructor(public events: Events, public storage: Storage, public http: HttpClientService) {
//
}
login(user) {
var response = this.http.post('login', {
email: user.email,
password: user.password,
});
response.subscribe(data => {
this.storage.set('api_token', data.token);
console.log('raw : ' + data.token); // shows the api token
this.storage.get('api_token').then((value) => {
console.log('storage : '+ value); // is empty...
});
});
return response;
};
}
Edit: I managed to track down the issue to the storage running async which results in the token not being added to the headers.
createAuthorizationHeader(headers: Headers) {
// this does add the header in time
localStorage.setItem('api_token', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vYXBpLndpaHplLmRldi9sb2dpbiIsImlhdCI6MTQ4MTE4MzQyOCwiZXhwIjoxNDgxMTg3MDI4LCJuYmYiOjE0ODExODM0MjgsImp0aSI6IjdlNTE1WUEwWmE4NWc2QjUiLCJzdWIiOiIxIiwidXNlciI6eyJpZCI6MX19.T4KpqgCB8xU79vKyeLG4CJ0OHLpVI0j37JKIBJ_0CC4');
headers.append('Authorization', 'Bearer ' + localStorage.getItem('api_token'));
// this does not add the header in time
return this.storage.get('api_token').then((value) => {
headers.append('Authorization', 'Bearer ' + value);
});
}
getHeaders(path) {
let headers = new Headers();
headers.set('Accept', 'application/json');
headers.set('Content-Type', 'application/json');
if(!this.isGuestRoute(path)) {
this.createAuthorizationHeader(headers);
}
return new RequestOptions({ headers: headers });
}
get(path: string) {
return this._http.get(this.actionUrl + path, this.getHeaders(path))
.map(res => res.json())
.catch(this.handleError);
}
Alright, looked in the ionic docs and I do understand why you put them both underneath eachother since they also display it like that in the docs.
But Storage.set(key, value) :
Returns:
Promise that resolves when the value is set
This means that you cannot use it the way you are using it (hence why they added a comment with //or ....
Since resolving a Promise is asynchronous.
If you want to use the value like you're currently using it (which seems a bit odd but probably for you to test if the value is set correctly) you should use
this.storage.set('api_token', data.token).then(() => {
this.storage.get('api_token').then((value) => {
console.log('storage : '+ value); // is empty...
});
});
console.log('raw : ' + data.token); // shows the api token
If you would like some more information about why this happens, check out this SO answer (I prefer second one) Asynchronous vs synchronous execution, what does it really mean?

Facebook4, Firebase, getting null email

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.

Firebase automatic login after reloading Ionic app/page

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);
});
}
});

Azure Mobile Service: 500 Error but it's actually working?

I've got an Azure Mobile Service with a custom API. I have tested this API in the past from iOS and it seems to work fine. I am now testing this API on Android. This is the API method in question:
exports.post = function(request, response) {
var body = request.body;
var email = body.email;
var tables = request.service.tables;
var users = tables.getTable('User');
users.where({ email: email }).read({
success: function (userList) {
if (userList.length === 0) {
response.send(200, { Status: 'Error', Error: 'Email not found.' });
} else {
var user = userList[0];
var providerId = user.ObjectId;
var accounts = tables.getTable('Account');
accounts.where({ User: providerId }).read({
success: function (accountList) {
if (accountList.length === 0) {
response.send(200, { Status: 'Error', Error: 'Internal server error.' });
} else {
var account = accountList[0];
var mssql = request.service.mssql;
var sql = "EXEC [db].[usp_RequestPasswordReset] ?;";
mssql.query(sql, [account.id], {
success: function (results) {
console.log(results);
var codeRow = results[0];
if (codeRow == undefined) {
console.log("codeRow is undefined");
} else {
console.log(codeRow);
}
var code = codeRow.Code;
response.send(200, { Status: 'Success', Message: 'Please check your email for further instructions.', Code: code });
sendEmail(email, user.Name, code);
}
});
}
}
});
}
}
});
};
Now, sendEmail is a separate function that sends an email using Azure's SendGrid feature.
What is really perplexing me is that all of the code appears to be working fine.
The stored procedure executes just fine.
The database is updated exactly as I would expect.
The email comes through the SendGrid service exactly as expected.
The console.log messages that I have in the code display the expected values.
The only thing that is funky is that the call is returning a "500: Internal Server Error" error.
This is true both in my Android client and also in the API log on the Azure Management Portal.
The error message I am getting is telling me that var code = codeRow.Code; is trying to access 'Code' of 'undefined'. But it's not undefined.
Going back and checking my iOS client against this produces the same results.
Everything works fine except for the message returned to the user.
To be clear, the error code is 500, not 200, since it's possible for my code to return an "Internal Server Error" message.
Also, I am very sure that my mssql.query success block is firing, based on the console log messages and the outcome.
So, what gives?
mssql.query can call your callback more than once depending on what's in your stored procedure. You can define a variable outside your callback, e.g.
var callbackReceived = false;
and then in your callback, only send a response for the call that actually receives the updated record:
if (callbackReceived === false && results && results.length > 0) {
callbackReceived = true;
// continue as before
}
See also this question answered by one of the Azure developers:
Azure mobile service custom API calling SQL SP multiple times