ionic native storage does not work on iOS - ios

I use Ionic 3 on one of my projects with an authentication system. I use native storage when the user wants to connect. It works on Android but on iOS, it redirects me to the login screen even using platform.ready (). I saw that several people were a similar problem but no answer, so I wanted to know if someone was facing the same problem and if he found a solution. Here is my code:
this.plt.ready().then(() => {
this.nativeStorage.setItem('userStorage', { stayConnected: (typeof this.stayConnected == "undefined" || this.stayConnected == false ? '' : 'stayConnected'), userId: (result as any).id, userLogin: (result as any).login })
.then(
() => {
this.loader.dismiss();
this.navCtrl.setRoot(HomePage);
},
error => {
this.loader.dismiss();
this.presentToast(this.languageLogin.error, 3000, "bottom");
}
)
},
error => {
this.loader.dismiss();
this.presentToast(this.languageLogin.error, 3000, "bottom");
});
thank you for your answers.

I would put 2 function storeUser() and getUser() into the same provider UserService like belows
Then add UserService to the constructor of any pages required.
It works for both IOS, Android and web
import {Storage} from '#ionic/storage';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class UserService {
constructor(private storage: Storage){}
public storeUser(userData): void {
this.storage.set('userData', userData);
}
public getUser(): Observable<any>
return Observable.fromPromise(this.storage.get('userData').then((val) => {
return !!val;
}));
}

Yes, I have faced issues while using ionic native storage plugins. So I turned to javascript Window localStorage Property and it's working completely fine.
Syntax for SAVING data to localStorage:
localStorage.setItem("key", "success");
Syntax for READING data from localStorage:
var lastname = localStorage.getItem("key");
Syntax for REMOVING data from localStorage:
localStorage.removeItem("key");
and now you can write your code with this property, like this -
if (lastname == "success"){
this.navCtrl.setRoot(HomePage);
} else{
alert("Not matched")
}

You are inside a platform.ready(), which is good. The storage package also has a .ready() that you may want to leverage, which specifically checks if storage itself is ready. If this runs at startup there is a decent chance storage is initializing.
Also, this starts to get into some crazy promise chaining messiness. I'd suggest diving into async/await. Something like the (untested) code below.
try{
await this.plt.ready();
await this.nativeStorage.ready();
let stayConnectedValue = (this.stayConnected) ? 'stayConnected' : '';
await this.nativeStorage.setItem('userStorage', { stayConnected: stayConnectedValue , userId: (result as any).id, userLogin: (result as any).login });
this.navCtrl.setRoot(HomePage);
}
catch(err){
this.presentToast(this.languageLogin.error, 3000, "bottom");
}
finally{
this.loader.dismiss();
}

Related

Angular / Ionic mobile app ios does not fetch from Firebase using angularfire

I am trying to test a little Ionic/Angular sample app on an iOS Emulator.
On the web, all the requests to firestore using angularfire work perfectly fine.
Somehow if I try to execute the same app on the emulator, it keeps loading for the response of the request (if it was a empty response it would say that no results could be retrieved).
What is going on? Do i need to set something specifically for the Emulator to work and perform requests to Firestore?
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
import { Capacitor } from '#capacitor/core';
import { initializeAuth, indexedDBLocalPersistence } from 'firebase/auth';
import { getAuth } from 'firebase/auth';
const firebaseApp = initializeApp({
apiKey: process.env.VUE_APP_FIREBASE_API_KEY,
authDomain: process.env.VUE_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.VUE_APP_FIREBASE_DATABASE_URL,
projectId: process.env.VUE_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.VUE_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId:
process.env.VUE_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.VUE_APP_FIREBASE_APP_ID,
});
function whichAuth() {
let auth
if (Capacitor.isNativePlatform()) {
auth = initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence
})
} else {
auth = getAuth()
}
return auth
}
export const auth = whichAuth()
const db = getFirestore();
export const auth = whichAuth();
export { firebaseApp, db };
Then in your component, cal your method like this await signInAnonymously(auth);. Don't forget to import the auth we exported at the top.
[Edit: updated with instructions Firebase JS SDK version 9 (modular)]
This error occurs because Firebase Auth incorrectly detects its environment as a normal browser environment and tries to load remote Google APIs, which results in the error you see in the console:
TypeError: undefined is not an object (evaluating 'gapi.iframes.getContext')
Fortunately, Firebase Auth already has logic to handle running in Cordova/Ionic apps, you just need to tell it which platform it's on.
For Firebase JS SDK version 9 (modular)
Simply import the Cordova Firebase Auth implementation:
import { getAuth } from 'firebase/auth';
For Firebase JS SDK <9 or the compatibility modules (auth/compat)
In capacitor.config set server: { iosScheme: "ionic" }:
// capacitor.config.json
{
"server": {
"iosScheme": "ionic"
}
}
There's a check in the auth/compat library here which, when it sees the URL scheme "ionic://", uses its Ionic/Cordova loading logic, and otherwise falls back to normal browser logic which fails with the error above.
Recent versions of Capacitor changed the URL scheme to "capacitor://" which fails this test but you can override it in your capacitor.config file (see the config option iosScheme).
(See also #alistairheath's comment here).
Been struggling a lot with this issue too but I managed to fix it. For those who need help here's my code.
You can delete all Firebase related imports from app.module.ts since this solution only uses Firebase.
The packages rxfire and #angular/fire can be removed from your package.json. The only dependency I have is "firebase": "^9.6.1".
I used observables for the getObject and list functions since that's what I'm used to and I didn't want to rewrite my original code.
import { Injectable } from '#angular/core';
import { Capacitor } from '#capacitor/core';
import { environment } from '#environment';
import { initializeApp } from 'firebase/app';
import { Auth, getAuth, indexedDBLocalPersistence, initializeAuth, signInWithCustomToken } from 'firebase/auth';
import { Database, getDatabase, onValue, orderByChild, query, ref } from 'firebase/database';
import { Observable, Observer, from } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class FirebaseService {
private readonly database: Database;
private readonly auth: Auth;
constructor() {
const firebaseApp = initializeApp(environment.firebase);
if (Capacitor.isNativePlatform()) {
initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence
});
}
this.database = getDatabase(firebaseApp);
this.auth = getAuth(firebaseApp);
}
connectFirebase(firebaseToken) {
return from(signInWithCustomToken(this.auth, firebaseToken));
}
disconnectFirebase() {
return from(this.auth.signOut());
}
getObject<T>(path: string): Observable<T> {
return new Observable((observer: Observer<T>) => {
const dbRef = ref(this.database, path);
const listener = onValue(dbRef, snapshot => {
const data = snapshot.val();
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
public list<T>(path: string, orderChildBy?: string): Observable<Array<T>> {
return new Observable<Array<T>>((observer: Observer<Array<T>>) => {
const dbRef = ref(this.database, path);
const dbReference = !orderChildBy ? dbRef : query(dbRef, orderByChild(orderChildBy));
const listener = onValue(dbReference, snapshot => {
const data = Object.values<T>(snapshot.val() || {});
console.log(path, data);
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
}
For those who can't see the error message thrown by firebase try the following command in your Safari console to see the error.
window.location.reload()
The real problem: firebase-js-sdk on mobile iOS assumes google API (gapi) exists on the window, even when it isn't used.
I found a work around: Mock window.gapi before using firebase auth login:
window['gapi'] = {
load: (name: string) => Promise.resolve(),
iframes: {
getContext: () => {
return {
iframe: {
contentWindow: {
postMessage: (message: any) => {
console.log("gapi iframe message:", message);
}
}
}
}
}
}
} as any;

React native app through Expo, getting firestore permission error:

This is my first post here so please let me know if I'm not posting this correctly.
I keep getting the following error in the debug logs of my react native Expo app on the iOS simulator when I have an authenticated user trying to retrieve a firestore document:
FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
Here is firebase.js config file:
import "firebase/firestore";
import "firebase/storage";
import * as firebase from 'firebase';
// Initialize Firebase
const firebaseConfig = {
apiKey: ... //removed for this post, but it is correct and validated
};
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
const auth = firebase.auth();
export { auth };
export default db;
Here is my App.js:
import React, { useEffect, useState } from 'react';
import db, { auth } from './firebase';
const getUserData = async(uid) => {
try {
const doc = await db.collection('users').doc(uid).collection('info').doc(uid).get();
if (doc.exists) {
console.log(doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No user info was found for the authenticated user");
}
} catch(err) {
console.log(err);
}
};
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
if (authUser) {
//user is logged in
getUserData(authUser.uid); //retrieve the user's profile data
} else {
//user is logged out
auth.signOut();
}
});
}, []);
My security rules shouldn't be the problem because it works for my web react app with the same logic and user, and the get request is only sent when there is a uid because the user is authenticated. I've printed out the uid after onAuthStateChanged and it is the correct uid.
//Security Rules in Firestore
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function signedInAndSameUser(uid) {
return request.auth != null && request.auth.uid == uid;
}
match /users/{uid} {
allow read: if request.auth != null;
match /private/{privateId} {
allow read: if signedInAndSameUser(privateId);
}
}
I've seen similar posts that recommended to downgrade to firebase#4.6.2 but I also ran into issues and couldn't get it to work. I'm wondering if firebase still hasn't fixed this issue even after version 8 (In react native app (through Expo) using firestore permissions - request.auth is always null)
This is my current firebase and expo version in my package.json:
//package.json
"expo": "~41.0.1",
"firebase": "8.2.3",
Thank you so much if you can help, I've been stuck on this issue for many hours and can't seem to understand why this works in my react.js web app, but the same logic, user, and security rules won't work in my react native Expo iOS app.

angular-oidc library code flow without discovery document

we have to implement the oauth2 code flow in our Angular application. We have used until now the implicit flow with no problems, we are using this library https://github.com/manfredsteyer/angular-oauth2-oidc. Now, for the code flow we don't have any discovery document available, so the library cannot move on with the flow. Is there any possibility to configure the URLs for the code flow manually? We are using version 8.0.4 of the library and our Angular version is 7.
Thanks!
Authorization code Legacy flow (without pkce) configure mannually without discovery document - manfredsteyer / angular-oauth2-oidc.
They posted a solution at: https://github.com/manfredsteyer/angular-oauth2-oidc/issues/1051.
Some details from jeroenheijmans
*It's possible, but you need to configure everything manually then.
Skip the loadDiscoveryDocument... parts and instead configure
everything in that place, then continue otherwise as you normally
would.
In #1051 I think the same question was asked - https://github.com/manfredsteyer/angular-oauth2-oidc/issues/1051*
Based off my sample you could roughly do something like this:
private configureWithoutDisovery(): Promise<void> {
// configure the library here, by hand, per your IDS settings
}
public runInitialLoginSequence(): Promise<void> {
return this.configureWithoutDisovery()
.then(() => this.oauthService.tryLogin())
.then(() => {
if (this.oauthService.hasValidAccessToken()) {
return Promise.resolve();
}
return this.oauthService.silentRefresh()
.then(() => Promise.resolve())
.catch(result => {
const errorResponsesRequiringUserInteraction = [ 'interaction_required', 'login_required', 'account_selection_required', 'consent_required' ];
if (result && result.reason && errorResponsesRequiringUserInteraction.indexOf(result.reason.error) >= 0) {
console.warn('User interaction is needed to log in, we will wait for the user to manually log in.');
return Promise.resolve();
}
return Promise.reject(result);
});
})
.then(() => {
this.isDoneLoadingSubject$.next(true);
// optionally use this.oauthService.state to route to original location
})
.catch(() => this.isDoneLoadingSubject$.next(true));
}

Request from Ember front to Rails back is not happening

I am implementing a front-end in ember 1.13 with a Rails back-end and having the following problem:
After the user is authenticated, I don't seem to be able to retrieve the user's record from the back-end. The browser debugger does not even show a request being made. This is code:
// app/services/session-user.js
import Ember from 'ember';
const { inject: { service }, RSVP } = Ember;
export default Ember.Service.extend({
session: service('session'),
store: service(),
loadCurrentUser() {
currentUser: {
var userId = this.get('user_id');
if (!Ember.isEmpty(userId)) {
return this.get('store').findAll('user', userId);
}
}
}
});
There is a login controller which handles the authentication. But the code for getting the data is in the applications's route:
// app/routes/application.js
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
const { service } = Ember.inject;
export default Ember.Route.extend(ApplicationRouteMixin, {
sessionUser: service('session-user'),
beforeModel() {
if (this.session.isAuthenticated) {
return this._loadCurrentUser();
}
},
sessionAuthenticated() {
this._loadCurrentUser();
},
_loadCurrentUser() {
return this.get('sessionUser').loadCurrentUser();
},
});
For extra measure I am defining the session store:
// app/session-stores/application.js
import Adaptive from 'ember-simple-auth/session-stores/adaptive';
export default Adaptive.extend();
If there are files I should post, please let me know.
Any hints will be highly appreciated as I am rather new to ember. I have spent several hours researching without luck, as things seem to have changed quite a lot throughout versions.
Look at your service code.
var userId = this.get('user_id');
if (!Ember.isEmpty(userId)) {
return this.get('store').findAll('user', userId);
}
I don't see code that you provided in question where you setting up user_id variable. So if user_id not defined then if statement won't get executed because of !.

How to implement XPCOM component (nsIContentPolicy) in bootstrapped Firefox extension

I have a bootstrapped extension for Firefox.
And now I want to implement nsIContentPolicy XPCOM component.
I wrote a component module code.
And now I want to register this component.
The reason I want to register component is that I want to add my component to nsICategoryManager.addCategoryEntry with "content-policy" category.
var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
//console.error("Running interceptor");
function Interceptor()
}
Interceptor.prototype = {
classDescription: "DeferredTo HTTP requests Interceptor",
classID: "{B5B3D9A0-08FC-11E3-8253-5EF06188709B}",
contractID: "#deferredto.com/Interceptor;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy]),
shouldLoad : function dt_shouldLoad(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeTypeGuess, aExtra) {
console.log("dt_shouldLoad");
if (contentLocation.scheme != "http" && contentLocation.scheme != "https")
return Ci.nsIContentPolicy.ACCEPT;
let result = Ci.nsIContentPolicy.ACCEPT;
// we should check for TYPE_SUBDOCUMENT as well if we want frames.
if ((Ci.nsIContentPolicy.TYPE_DOCUMENT == aContentType) &&
SOME_REGULAR_EXPRESSION.test(aContentLocation.spec)) {
// do stuff here, possibly changing result.
}
return result;
},
shouldProcess: function ILO_shouldProcess() Ci.nsIContentPolicy.ACCEPT,
_xpcom_categories: [
{ category: "content-policy", service: true }
],
classInfo: XPCOMUtils.generateCI(
{classID: Components.ID("{B5B3D9A0-08FC-11E3-8253-5EF06188709B}"),
contractID: "#deferredto.com/Interceptor;1",
classDescription: "Interceptor implements nsIContentPolicy to block images that are not yet at screen #DeferredTo",
interfaces: [
Ci.nsIContentPolicy,
],
flags: Ci.nsIClassInfo.SINGLETON})
}
var components = [Interceptor];
var NSGetFactory = XPCOMUtils.generateNSGetFactory([Interceptor]);
Questions:
Is it possible to register the component from bootstrapped extension?
Is it possible to register the component from restartless extension?
Is it possible to use nsICategoryManager.addCategoryEntry "content-policy" without
component?
How to register the component in bootstrapped extension or somehow add
new "content-policy" category entry?
I've added to harness-options.js
"requirements": {
"sdk/page-mod": "sdk/page-mod",
"sdk/self": "sdk/self",
"chrome": "chrome"},
That is how I try to import module:
var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://deferredto/lib/interceptor.js");
I' ve tried many paths ))) But none works. resource entry in chrome.manifest file does not allowed for bootstrapped extensions. The path to component module file is:
resources/deferredto/lib/interceptor.js
Adblock Plus, which is restartless but not using the SDK, registers an nsIContentPolicy implementation at runtime, just like your SDK would. There are probably a few SDK add-ons registering components at run time, but I don't know any open source ones that I would recommend to look at off the top of my head.
A few points regarding that ABP implementation and what to change to make it work with the SDK:
The category manager is available via Cc["#mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager).
The component registrar should be available by requiring also components from the chrome module and then components.manager.getService(Ci.nsIComponentRegistrar).
As Adblock Plus, you must unregister your component yourself on unload.
The unload part is unfortunely also a bit tricking, since you cannot synchronously unregister your component and the category entry, due to Bug 753687. Adblock Plus therefore does it async using Util.runAsync, which just dispatches a runnable (event, if you like) to the main thread. I don't think you can use any SDK stuff here, as the SDK will clean up before any async code gets a chance to run, so you'd need to use a low-level XPCOM runnable (or timer) yourself.
Your code will register your component at runtime. You won't touch harness-options or anything like that.
(I also implemented a generic component register function myself, but that again is not SDK code, and would need to be adapted to run in the SDK, just like the ABP one. It is also very similar to the ABP one.)
Now my nsIContentPolicy sdk-based component look like this. File interceptor.js:
'use strict';
var { Class } = require('sdk/core/heritage');
var xpcom = require('sdk/platform/xpcom');
var { Cc, Ci, Cu, Cm } = require('chrome');
var categoryManager = Cc["#mozilla.org/categorymanager;1"]
.getService(Ci.nsICategoryManager);
// nsIDOMNode
const TYPE_DOCUMENT_NODE = Ci.nsIDOMNode.DOCUMENT_NODE;
/// Interceptor
var contractId = "#deferredto.com/Interceptor;1";
var Interceptor = Class({
extends: xpcom.Unknown,
interfaces: [ 'nsIContentPolicy' ],
get wrappedJSObject() this,
shouldLoad : function dt_shouldLoad(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
let result = Ci.nsIContentPolicy.ACCEPT;
return result;
},
shouldProcess: function () Ci.nsIContentPolicy.ACCEPT
});
var factory = xpcom.Factory({
contract: contractId,
Component: Interceptor,
unregister: false // see https://bugzilla.mozilla.org/show_bug.cgi?id=753687
});
/// unload
var unload = require("sdk/system/unload");
unload.when(function() {
function trueUnregister() {
categoryManager.deleteCategoryEntry("content-policy", contractId, false);
try {
console.log("xpcom.isRegistered(factory)=" + xpcom.isRegistered(factory));
console.log("trueUnregister");
xpcom.unregister(factory);
console.log("xpcom.isRegistered(factory)=" + xpcom.isRegistered(factory));
} catch (ex) {
Cu.reportError(ex);
}
}
if ("dispatch" in Cu) {
console.log('"dispatch" in Cu');
Cu.dispatch(trueUnregister, trueUnregister);
} else {
console.log('"dispatch" not! in Cu');
Cu.import("resource://gre/modules/Services.jsm");
Services.tm.mainThread.dispatch(trueUnregister, 0);
}
});
//xpcom.register(factory);
var interceptor = Cc[contractId].createInstance(Ci.nsIContentPolicy);
categoryManager.deleteCategoryEntry("content-policy", contractId, false);
categoryManager.addCategoryEntry("content-policy", contractId, contractId, false, true);
And you can use it from sdk like this:
var interceptor = require("./interceptor");

Resources