Open spoxies opened 3 years ago
You managed to find a solution? from my side, I could only use the web code on Android and the plugin code on iOS.
@helpmetheroadassistanceapp Yes I just stared with finalising the Android app thus I could not ignore this issue any longer. So I came across my own posts (and I over looked you question), I just found out how it is possible and assuming this is a wide spread issue, I'm gonna reply months later for those who come across.
In short both the plugin and the web layer expose the signInUserWithCustomToken
/ signInWithCustomToken
. It is easy to generate such a token on firebase functions with an already logged in user.
The resulting token you pass to the either one (web or native) you have not logged in to yet. With this custom token you can login as the same user no matter what initial authentication method you had chosen.
What you do is expose an endpoint in firebase functions e.g:
Exposing an endpoint in Firebase functions:
firebase.json:
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "{/*,/**}",
"function": "getApp"
}
]
},
"functions": {
"source": "functions/",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run"
]
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"storage": {
"rules": "storage.rules"
},
"database" : {
"rulesFile": "firestore.rules"
}
}
functions/index.js:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require('express');
const app = express();
// https://github.com/firebase/firebase-admin-node/pull/285
admin.initializeApp({
serviceAccountId: [the-service-account-that-has-generate-custom-token-permission]
});
api.myAuth_get = require('./api/my/auth/get').http_get;
api.myAuth_options = require('./api/my/auth/options').http_options;
// HTTP :: /my/auth
app.get('/my/auth', (req, res) => {
res.set('Content-Type', 'application/json');
return api.myAuth_get(req, res);
});
app.options('/my/auth', (req, res) => {
res.set('Content-Type', 'application/json');
return api.myAuth_options(req, res);
});
exports.getApp = functions.https.onRequest(app);
functions//api/my/auth/options.js:
// == api.myAuth_options ===================================
exports.http_options = (req, res) => {
// Set CORS headers
res.set({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'DELETE,GET,PATCH,POST,PUT',
'Access-Control-Allow-Headers': 'Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With',
});
res.status(200).send();
return;
};
functions//api/my/auth/get.js:
// == api.myAuth_get ===================================
const admin = require('firebase-admin');
exports.http_get = (req, res) => {
// Set CORS headers
res.set({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'DELETE,GET,PATCH,POST,PUT',
'Access-Control-Allow-Headers': 'Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With',
});
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer '))) {
res.status(403).send('Unauthorized');
return;
} else {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
}
return admin.auth().verifyIdToken(idToken)
.then((decodedIdToken) => {
return admin.auth().createCustomToken(decodedIdToken.uid);
}).then((customToken) => {
if (customToken) {
res.status(200).send({'customToken': customToken});
return;
}
res.status(404).send({
'success': false,
});
}).catch((error) => {
console.log('Error while creating a Firebase ID token:', error);
res.status(400).send({ error: 'Invalid request' });
});
};
So assuming you have logged in on the weblayer (e.g with phoneAuth), you call the above endpoint to retrieve a custom token.
The example bellow shows you could intermix passing credentials from FirebasePlugin.verifyPhoneNumber
to the weblayer firebase.auth().signInWithCredential
and from that retrieve the custom token from 'Admin Functions' an pass it to FirebasePlugin.signInUserWithCustomToken
.
Of course it would make more sense to do FirebasePlugin.verifyPhoneNumber
> FirebasePlugin.signInWithCredential
> [retrieve-custom-token-from-admin-functions] > firebase.auth().signInWithCustomToken
The APP:
window.FirebasePlugin.verifyPhoneNumber(function (credential) {
// if instant verification is true use the code that we received from the firebase endpoint,
// otherwise ask user to input verificationCode:
var code = (credential.instantVerification) ? credential.code : false;
var verificationCredential = credential;
if (!code) {
showSmsInput(verificationCredential);
return;
}
validateCredential(credential);
return;
}, console.error, {timeOutDuration : 120, requireSmsValidation : true});
function showSmsInput(){
// Show some UI
...
// Bind to some submit event
$$(document).on('submit', function(){
var currentInput = $$("input#phonenumber_sms_code").val();
validateCredential(app.data.verificationCredential, currentInput);
});
}
}
function validateCredential(credential, code) {
var webCredential = firebase.auth.PhoneAuthProvider.credential(
credential.verificationId,
code || credential.code
);
// Sign in weblayer
return firebase.auth().signInWithCredential(webCredential)
.then((auth) => {
if(!auth || !auth.user){
return;
}
// Retrieve a custom token from your endpoint
// using a XHR request
return auth.user.getIdToken().then(function (idToken) {
return app.request.promise({
url: [your - exposed - admin - endpoint] / my / auth,
method: 'GET',
dataType: 'json',
headers: { 'Authorization': `Bearer ${idToken}` },
}).then(function (customTokenData) {
// Sign in to the native layer
window.FirebasePlugin.signInUserWithCustomToken(
customTokenData.customToken,
console.log,
console.warn
);
});
});
});
}
@dpa99c Would the above (but reworked a bit), be something that you like to have added to the docs 'Achieve multilayer login' ? If so I'll write a bit and propose a PR.
@spoxies Yes, any additional documentation to assist other plugin users for common use cases like this would be most welcome and appreciated.
Just here to say that I encountered this problem recently and the proposed solution worked on iOS for me. Really appreciate you adding this information.
What:
Extending the documentation by showing how to use
cordova-firebasex-plugin
in conjunction with theFirebase Web JS SDK
.Why:
The
cordova-firebasex-plugin
does things that theFirebase Web JS SDK
just cannot expose (like auth/notifications/crashalytics) in a hybrid environment. On the other hand the plugin isn't a full substitute of all (essential) features that theWeb JS SDK
exposes.Since
Firebase Web JS SDK
is already committed to cordova compatibility I would (advise a developer that uses firebase and cordova to) use theWeb JS SDK
for everything else where it does not needs to interact with the Native layer. Also keeping thisplugin
up with things that theFirebase Web JS SDK
can support seems an everlasting/draining chase for the contributor(s).Since version 8.0.0. the auth has been reworked. Meaning it was not possible/advised to pass credentials to the
Web JS SDK
and login. There seem to be issues/struggles (https://github.com/dpa99c/cordova-plugin-firebasex/issues/176, https://github.com/dpa99c/cordova-plugin-firebasex/issues/433, https://github.com/dpa99c/cordova-plugin-firebasex/issues/435) derived from this change. However PR's making cross layer login possible are already posted (https://github.com/dpa99c/cordova-plugin-firebasex/pull/633, https://github.com/dpa99c/cordova-plugin-firebasex/pull/621). Documenting how to make use of these PR's does make sense.Why (not)/ why warn:
'Unfortunately' there is overlap like some Firebase Firestore functions that are (also) added in
cordova-plugin-firebasex
. Having theFirebase Web JS SDK
(optionally put there by the app developer) as well, means that in this example there are multiple approaches to retrieve data. That might lead to mixing (native) variables andWeb JS SDK
variables.How:
Adding (a version of) the suggestion bellow in the documentation:
Passing authentication to the Firebase JavaScript SDK
Experimental: Passing authentication to the Firebase JavaScript SDK is possible for some Firebase Authentication operations that are exposed by this plugin, but this is highly experimental. Please note that mixing variables from the native (plugin) and the JavaScript SDK should be avoided as it to will lead to unexpected behaviour.
authenticateUserWithApple (Android/iOS)
Passing authentication unsupported (See https://github.com/dpa99c/cordova-plugin-firebasex/issues/551)
authenticateUserWithGoogle (Android/iOS)
Possible since version 13.0.0
verifyPhoneNumber (Android*/iOS) - w/o instantVerification
NOTE: As of version 8.0.0 passing authentication is no longer possible if
instantVerification: true
. (See https://github.com/dpa99c/cordova-plugin-firebasex/issues/176)Other
The above is based upon my current and limited understanding of this project and should by not be taken as truth.
At least within the iOS version of the plugin it is possible to duplicate the verification credentials for use with the plugin and web js layer. This isn't tested on Android and probably a bad idea in general.
~
Sources
https://github.com/dpa99c/cordova-plugin-firebasex/pull/633 https://github.com/dpa99c/cordova-plugin-firebasex/commit/78bff37626fecd92ddc5de189de94e9864d87b6c