I have created an Ionic app which uses Firebase Authentication. I'm trying to authenticate using Google. This is a part of my code trying to achieve the same.
$cordovaOauth.google("//removed_client_id", ["https://www.googleapis.com/auth/userinfo.profile"]).then(function (result) {
var credentials = firebase.auth.GoogleAuthProvider.credential(result.id_token);
console.log(JSON.stringify(result));
console.log(JSON.stringify(credentials));
firebase.auth().signInWithCredential(credentials).then(function (authData) {
console.log(JSON.stringify(authData));
}
}
The problem is I'm not getting any results in 'authData' as fields like displayName, photoUrl, email are null. What am I doing wrong?
Related
A year ago we have deployed an app (created with flutter) in the appstore, it has been downloaded and used frequently. The app simply enhanced the views of the website.
So far, we have been using the basic firebase authentication to store the users email and password :
try {
await firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
} on FirebaseAuthException catch (signUpError) {
if (signUpError.code == 'email-already-in-use') {
try {
await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return null;
} on FirebaseAuthException catch (loginError) {
if (loginError.code == 'error-wrong-password')
throw PasswordChangedException();
throw LoginException(errorCode: loginError.code);
}
}
With those email and password, we signed the user in on mentionned website and displayed the data.
(The problem often occured was, that if the user changes the password on the website, our app would not work anymore, because the pw would not match our firebase auth pw.)
However we are now in contact with the website and would now have access to the API via OIDC authentication.
The question is now how we merge firebase authentication and the authentication of the website via OIDC?
In my view, firebase authentication (email/pw), or just the access of firestore, should be dependent on the authentication via OIDC, that if I change the password (via website), the firebase authentication would still work. How can one implement that behaviour?
I am using expo-facebook to integrate a Facebook login using expo and firebase. Everything looks to be working and I log into Facebook but get an OAuthException once I authenticate using Facebook as follows:
Unsuccessful debug_token response from Facebook: {"error":{"message":"(#100) The App_id in the input_token did not match the Viewing App","type":"OAuthException","code":100
I have gone through a lot of issues on Stack Overflow, GitHub and looked at expo documentation as well but to no avail.
I have configured the app id and secrets from Facebook into firebase as required as well as set up the OAuth redirect URI to my Facebook app configuration. The code I have put together to setup the login is as follows:
const signInWithFacebook = async () => {
try {
// const { type, token } = await Facebook.logInWithReadPermissionsAsync(
// facebookAppId,
// {
// permissions: ["public_profile"],
// }
// );
const appId = Constants.manifest.extra.facebook.appId;
const permissions = ["public_profile"]; // Permissions required, consult Facebook docs
await Facebook.initializeAsync({
appId: appId,
});
const { type, token } = await Facebook.logInWithReadPermissionsAsync({
permissions: permissions,
});
console.log(type);
console.log(token);
if (type === "success") {
await firebase
.auth()
.setPersistence(firebase.auth.Auth.Persistence.LOCAL);
const credential = firebase.auth.FacebookAuthProvider.credential(token);
const facebookProfileData = await firebase
.auth()
.signInWithCredential(credential);
//this.onLoginSuccess.bind(this);
console.log(facebookProfileData);
}
} catch ({ message }) {
console.log(message);
alert(`Facebook Login Error: ${message}`);
}
};
I have also setup the relevant configurations in the app.json as follows:
"expo":{
"facebookScheme": "fb123243435566",
"facebookAppId": "123243435566",
"facebookDisplayName": "myapp"
}
The only aspect I am not sure about is where to grab the facebookScheme. Currenltly I have assumed it's fb+AppID. the documenatation mentioned here https://docs.expo.dev/versions/latest/sdk/facebook/
isn't clear. It states:
Configure app.json.
Add the field facebookScheme with your Facebook login redirect URL scheme found here under "4. Configure Your info.plist." It should look like "fb123456". If you do not do this, Facebook will not be able to redirect to your app after logging in.
But I am not sure how to grab that facebookScheme id. I suspect this is where the issue is as expo states that.
Expo Go from the Android Play Store will use the Facebook App ID that you provide, however, all Facebook API calls in the Expo Go from the iOS App Store will use Expo's own Facebook App ID. This is due to underlying configuration limitations.
so I am assuming the facebookScheme is some kind of workaround.
Although I am not sure if it's a working around for the ios standalone app or the expo managed.
Try this:
const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync(appId, {
permissions: [‘public_profile’, ‘email’],
behavior: Platform.OS === ‘android’ ? ‘web’ : ‘system’,
});
it should specify for the app to open a web browser on login
here's the ref:
https://forums.expo.dev/t/how-to-fix-standalone-android-expo-facebook-login/23922
im using this tutorial:
https://firebase.google.com/docs/auth/admin/create-custom-tokens#using_a_service_account_id
to create a node.js function (deployed to google cloud functions) to authenticate my users. the function is super simple:
const admin = require('firebase-admin');
admin.initializeApp({
serviceAccountId: 'authenticator#igibo-b0b27.iam.gserviceaccount.com'
});
exports.authenticate = (req, res) => {
let pass;
let uid;
if (req.query) {
if (req.query.v == 3) {
pass = req.query.p;
uid = req.query.u;
}
admin.auth().createCustomToken(uid)
.then(function(customToken) {
res.status(200).send(customToken);
return customToken;
})
.catch(function(error) {
console.error("Error creating custom token:" + JSON.stringify(error));
res.status(400).send(error);
});
} else {
console.error("EMPTY to authentication");
res.end();
}
};
but im getting this annoying error:
{"code":"auth/insufficient-permission","message":"Permission iam.serviceAccounts.signBlob is required to perform this operation on service account projects/-/serviceAccounts/authenticator#igibo-b0b27.iam.gserviceaccount.com.; Please refer to https://firebase.google.com/docs/auth/admin/create-custom-tokens for more details on how to use and troubleshoot this feature."}
in the very same tutorial it says i must go to IAM and adjust some roles for the service account WHICH I DID but still getting this error.
this is a absolutelly simple task and shouldn't being such a hassle...
what i am forgetting? the id is correct! the role is correct! the code is correct!
what is wrong?
Firebase mentions about this error on its docs:
https://firebase.google.com/docs/auth/admin/create-custom-tokens#failed_to_determine_service_account
You must initialize your app correctly through a JSON config file.
A simple fix would be:
Go to
https://console.cloud.google.com/iam-admin/iam?project=PROJECT_NAME
Edit your default service account.
Add the role Service Account
Token Creator
In a few minutes your project will be able to create signed tokens.
I am super hopeful someone can help me - I'm kind of stuck.
I'm happily using firebase auth with Microsoft AD. My AuthProvider is firebase.auth.OAuthProvider('microsoft.com').
When I call firebase.auth().signInWithPopup() with that provider, everything works GREAT. I can pick out the accessToken from the resulting UserCredential and access Microsoft Graph api's no problem (yay!).
Firebase persists and renews the authentication and my app gets the expected callback via onAuthStateChanged with the new firebase.User when the user returns to my SPA later (also yay!).
The bad news (where I'm stuck) is: how do I get the Microsoft Graph accessToken in this flow (e.g. when the user returns to my app later)? I don't want them to have to re-authenticate with another popup (yech).
Basically, how do I go from a valid firebase.User to a MS Graph accessToken when the user returns?
Thanks so so much for any help!
Firebase Auth only focuses on authentication only. They will return the OAuth access token on sign in success via UserCredential but will discard the Microsoft OAuth refresh token and not store any OAuth credential associated with the provider. So you have no way to get a new access token afterwards. If you have a good reason for Firebase Auth to manage OAuth access tokens, please file an official feature request.
UPDATE/answer: so it turns out to be simpler than I thought:
The basic idea is to authenticate (re-authenticate) using firebase and use the same clientID for silent microsoft authentication. However, you must supply a loginHint
parameter to the microsoft auth, even if you were previously authorized. loginHint can
be the email address for the firebase user...
In that scenario, the authentication is shared and you won't need to popup a second sign-in for the "microsoft half" of the process - the firebase auth works fine.
I ended up using microsoft's MSAL library (https://github.com/AzureAD/microsoft-authentication-library-for-js)... something like this:
const graphDebug = false;
const msalLogger = new Logger(msalLogCallback, { level: LogLevel.Error });
export async function graphClient(loginHint: string) {
const msal = new UserAgentApplication({
// gotcha: MUST set the redirectUri, otherwise get weird errors when msal
// tries to refresh an expired token.
auth: { clientId: CLIENT_ID, redirectUri: window.location.origin },
system: { logger: msalLogger },
// TODO: should we set cache location to session/cookie?
});
/**
* Create an authprovider for use in subsequent graph calls. Note that we use
* the `aquireTokenSilent` mechanism which works because firebase has already
* authenticated this user once, so we can share the single sign-on.
*
* In order for that to work, we must pass a `loginHint` with the user's
* email. Failure to do that is fatal.
*/
const authProvider: AuthProvider = callback => {
msal
.acquireTokenSilent({ scopes: SCOPES, loginHint })
.then(result => {
callback(null, result.accessToken);
})
.catch(err => callback(err, null));
};
const client = Client.init({
authProvider,
debugLogging: graphDebug,
});
return client;
}
When you are using signInWithPopup, the result object contains the credentials you are looking for.
firebase.auth().signInWithPopup(provider)
.then(function(result) {
// User is signed in.
// IdP data available in result.additionalUserInfo.profile.
// OAuth access token can also be retrieved:
// result.credential.accessToken
// OAuth ID token can also be retrieved:
// result.credential.idToken
})
.catch(function(error) {
// Handle error.
});
Hope this helps.
If you look deep enough you should find msal access token in firebase response under (firebaseAuth.currentUser as zzx).zzj()
We have an App that uses firebase auth. Now we would like to send the user from the app to our website in order for him to perform some actions.
Is it somehow possible to automatically login the user when he comes from the App? He has already provided his login credentials and we have the auth object from firebase. Can we pass a token or somthing to our website and have the firebase SDK on the site log him automatically?
The common way to do this right now is as follows:
After the user signs in in one website, get the ID token via currentUser.getIdToken()
Post the ID token to your backend to serve the destination website.
On your backend, using Admin SDK, get the ID token, verify it, check the auth_time to make sure it is a recent login to minimize any window of attack. If everything checks out, mint a custom token via admin sdk createCustomToken.
Return the custom token to the website.
On the client side, sign in with custom token: signInWithCustomToken and the same user is now signed in on the website.
I was able to do this a bit easier, without having my own backend server to create a custom token, as long as you are using Google auth. It might work for other providers but here's how I did it with Google auth on Android:
Android:
activity.startActivityForResult(
googleSignInClient.signInIntent,
object : ActivityWithResultListener.OnActivityResultListener {
override fun onActivityResult(resultCode: Int, data: Intent?) {
if (data != null) {
val credential = GoogleAuthProvider.getCredential(account.idToken!!, null)
idToken = account.idToken!!
// Then pass the token to your webview via a JS interface
}
)
Web:
const idTokenFromApp = AndroidBridge.getAuthToken(); // This is the idToken from the Android code above
if (idTokenFromApp) {
// from https://firebase.google.com/docs/auth/web/google-signin
// Build Firebase credential with the Google ID token.
// https://firebase.google.com/docs/reference/js/v8/firebase.auth.GoogleAuthProvider#static-credential
const credential = firebase.auth.GoogleAuthProvider.credential(idTokenFromApp);
// Sign in with credential from the Google user.
firebase.auth.signInWithCredential(credential).catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
logError(`Error logging in: ${errorCode} ${errorMessage} token: ${idTokenFromApp}`, error);
});
}