Firebase Auth across multiple projects with different Providers - firebase

I am looking for the Firebase to be authenticated seamlessly across multiple projects with different auth providers.
Example.
Suppose I have a website with 10 pages, with different data coming from 2 firebase projects ( project-1, project-2)
project-1 contains all the Users accounts
When I sign up/Login into firebase project (project-1). I am using Google/Phone/Password/Facebook. this creates users account if not exists.
Now I can see the data coming from the firebase (project-1).
Now few of my pages website want to access data from the project-2.
How to maintain the authentication state across the 2 projects so that, project-2 will get authenticated seamlessly with out prompting signup again. Probably with the same auth token which already created for project-1
I read this blog which was created by Ian Barber Working with multiple Firebase projects in an Android app. Which is only discussed about the google and android case only.
Thanks.

You can use a combination of firebase-admin on a server and a custom firebase token as described here.
Essentially, you can get a user's JWT token for your first app using the methods described in this answer. Then you could make a request to your own server that would validate that your JWT token originated from your first project and could then issue you a second JWT token you can use to authenticate with the second project.

I think you can do the following:
Configure an OIDC provider on project-2 that points out to the firebase auth from project-1 .
The issuer should be https://securetoken.google.com/<project-1-id>
The client id its the project-1-id
After you signIn on project-1 you can use the idToken from that app, to login with an OAuthProvider to the second app (project-2):
const provider = new OAuthProvider('oidc.project-1');
const credential = provider.credential({
idToken: 'idTokenFromProject1',
});
signInWithCredential(auth, credential)
idTokenFromProject1 can be fetched through getIdToken() method on the project-1 app

This is now easily possible with OIDC configuration (reference answer by Alenjadro Barone above )
Configure an OIDC provider on project-2 that points out to the firebase auth from project-1 .
The issuer should be https://securetoken.google.com/<project-1-id>
Then You can follow Code below . Its starter but definitely works : -
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
// Create secondary firebase app Instance
FirebaseApp secondaryApp = Firebase.app('secondary');
class Auth {
// default Firebase Auth app instance: PROJECT 1
final firebaseAuth = FirebaseAuth.instance;
// Secondary Firebase App Instance : PROJECT 2
FirebaseAuth secondaryFirebaseAuth =
FirebaseAuth.instanceFor(app: secondaryApp);
signin() async {
// Sign in method of your choice from PTOJECT 1
await firebaseAuth.signInWithEmailAndPassword(
email: "abc#abc.com",//registered user email
password: "password",//registered user password
);
// Declare OAuth Provider for PROJECT 2
// name of OIDC provider as set in Project
final provider = OAuthProvider('oidc.provider');
try {
// get IdToken for signedin user
final userIdToken = await firebaseAuth.currentUser!.getIdToken();
// Get access Token for User using IdTokenResult
final idTokenResult = await firebaseAuth.currentUser!.getIdTokenResult();
final userAccessToken = idTokenResult.token;
// Create OAuthCredentials with idToken & AccessToken
final credential = provider.credential(
idToken: userIdToken,
accessToken: userAccessToken,
);
// Sign in to Secondary app or PROJECT 2
await secondaryFirebaseAuth.signInWithCredential(credential);
} catch (e) {
if (kDebugMode) {
print("This is error $e");
}
}
}
}

Related

What Sign in method to use best?

We are having a flutter app (ios, android, web), where users are signed in via username & password.
We are also using google firebase since its powerful and easy to integrate.
The username and password mainly belongs to the website where we are gathering data at. (As example - If they use the website without the app, and they change the password, after that he wont be able to login to the app)
Now the mentionned websites host is giving us API access, login via OpenId to get the access token for the API. Because we are a safety risk since we store the passwort of the users too!
For the API access we dont really need to store Username and password of the user, since they are redundant anyway. But if we want to add a feature (for example message sending or further data storage) we need to have the user signed in into firebase.
Upt to now we are using for (first) signin the following snippet:
firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
and for already signed in users :
firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
Notice that similar credentials are also using to login on the API. (Since the user is there already registered)
How can we login on firebase with said information without asking twice for a password ond username (once for us, and once for the API) ?
We already tried :
await firebaseAuth.signInWithCustomToken(token)
with the jwl token from the OpenId, of course it did not work because the token did not contain the uid reference.
SOLUTION
Create a Firebase Cloud Function just like described in Firebase Cloud Functions.
Be aware that if you want to create a customtoken, the cloud functions need rights. On initializeApp(..)
admin.initializeApp({
serviceAccountId: '{App_Name}#appspot.gserviceaccount.com',
});
So the correct service account has to be selected, you also have to give him the rights to generate tokens. (See => Stackoverflow Question
The Cloud Function does then look the following way :
export const functionName= functions.https.onRequest(async (request, response) => {
const id = request.query.id;
const passcode = request.query.passcode; // not really needed
// add other passcodes for different authentications
if (passcode == "{COMPARE SOMETHING}") {
await admin.auth().createCustomToken(id).then((customToken) => {
response.status(200).send({
'id': id,
'customToken': customToken
});
}).catch((error) => {
response.status(500).send({
'ErrorMessage': "No token could be generated",
"Error": error
});
});
}
else {
response.status(500).send({
'ErrorMessage': "Passcode wrong"
});
}
});
On the other hand we have the code on the mobile app :
// Get JWT Token
Map<String, dynamic> jwtpayload =
Jwt.parseJwt(response_decoded['id_token']); // use import 'package:jwt_decode/jwt_decode.dart';
final queryParameters = {
'id': jwtpayload ['sub'],
'passcode': 'APassCode',
};
final uri = Uri.https('us-central1-{yourApp}.cloudfunctions.net',
'/{functionName}', queryParameters);
final cloud_function_api_call = await client.post(uri);
var decoded_cloud_function_api_call =
jsonDecode(cloud_function_api_call.body);
And at the end :
await firebaseAuth.signInWithCustomToken(
decoded_cloud_function_api_call['customToken']);
I hope it helps others facing a similar issue.

problem firebase.auth.Auth.Persistence.LOCAL

Hi every one I want to set Auth state persistence on firebase to local i'm working on flutter and I don't know how to do that
I found this on firebase web site
import { getAuth, setPersistence, signInWithEmailAndPassword, browserSessionPersistence } from "firebase/auth";
const auth = getAuth();
setPersistence(auth, browserSessionPersistence)
.then(() => {
// Existing and future Auth states are now persisted in the current
// session only. Closing the window would clear any existing state even
// if a user forgets to sign out.
// ...
// New sign-in will be persisted with session persistence.
return signInWithEmailAndPassword(auth, email, password);
})
.catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
});
but not sure how I do that on flutter if any one can help with that
As per the FlutterFire documentation
On native platforms such as Android & iOS, this behavior is not configurable and the user's authentication state will be persisted on-device between app restarts. The user can clear the apps cached data via the device settings which will wipe any existing state being stored.
If you're using Flutter for a web app, by default the auth state is stored in local storage. If you want to change this to session based or no persistence you would set that like this:
// Disable persistence on web platforms
await FirebaseAuth.instance.setPersistence(Persistence.NONE);

How to sign in with Apple and Google credentials in react-native and supabase

I have been trying to implement sign in with google and apple using the following libraries and supabase in bare react-native projects.
react-native-apple-authentication
react-native-google-signin
They work very well with firebase as described here using firebase sdk.
The concept is fairly simple, the native sdk for both apple and google sign return user credential after a successful sign in . Here is an example from https://rnfirebase.io/auth/social-auth
With google credential
import auth from '#react-native-firebase/auth';
import { GoogleSignin } from '#react-native-google-signin/google-signin';
async function onGoogleButtonPress() {
// Get the users ID token
const { idToken } = await GoogleSignin.signIn();
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
return auth().signInWithCredential(googleCredential);
}
With apple credential
import auth from '#react-native-firebase/auth';
import { appleAuth } from '#invertase/react-native-apple-authentication';
async function onAppleButtonPress() {
// Start the sign-in request
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: appleAuth.Operation.LOGIN,
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
});
// Ensure Apple returned a user identityToken
if (!appleAuthRequestResponse.identityToken) {
throw 'Apple Sign-In failed - no identify token returned';
}
// Create a Firebase credential from the response
const { identityToken, nonce } = appleAuthRequestResponse;
const appleCredential = auth.AppleAuthProvider.credential(identityToken, nonce);
// Sign the user in with the credential
return auth().signInWithCredential(appleCredential);
}
I have gone through the auth provided by supabase here supabase-js auth
The closest thing I can find related to the implementation above is the use of a refresh-token as shown here
Sign in using a refresh token
// An example using Expo's `AuthSession`
const redirectUri = AuthSession.makeRedirectUri({ useProxy: false });
const provider = 'google';
AuthSession.startAsync({
authUrl: `https://MYSUPABASEAPP.supabase.co/auth/v1/authorize?provider=${provider}&redirect_to=${redirectUri}`,
returnUrl: redirectUri,
}).then(async (response: any) => {
if (!response) return;
const { user, session, error } = await supabase.auth.signIn({
refreshToken: response.params?.refresh_token,
});
});
The example above uses a refreshToken parameter auth.sign() to create a session for a user who is already registered.
Correct me if am wrong. According to the auth documentation there is no method that allows one to create a user using an auth credential. This is because of how gotrue social auth is implemented. The only solution I can think of as now is to move the entire auth logic to the backend and handle it on the web. This is not elegant at all since it will have to involve ugly deep-linking or webview implementations to use supabase session and access token.
Given the scenario above, is there way of using native social sdk in react-native to create a new user account in supabase using a user credential without having to force them use an email link, password or web ? Any ideas , answers or corrections are welcome.
At the time of writing, Signing in with third party credentials is not implemented in gotrue(Module used for auth in supabase) yet. If you have a native app, flutter app and react-native app that needs social auth using credentials follow up on this WIP:PR. For further information refer to Proof Key for Code Exchange (PKCE).

Firebase signInWithCredentials from Microsoft Azure

Normally, I'd use the firebase.auth.OAuthProvider('microsoft.com') and pass it to firebase.auth().signInWithPopup() but in my case I need to log into Microsoft first using their library and pass the credentials back to firebase. (why? because I need their accessToken/refreshToken handling which isn't done by firebase).
Anyway, I can't seem to find a way to take the microsoft-supplied credentials and pass them successfully back to firebase...
here's what's not working for me:
export async function msLogin() {
const userAgent = new MSAL.UserAgentApplication({ auth: { clientId: CLIENT_ID, } });
const auth = await userAgent.loginPopup({ scopes: ['profile'] });
const provider = new firebase.auth.OAuthProvider('microsoft.com');
const idToken = auth.idToken.rawIdToken;
const credential = provider.credential(idToken);
await firebase.auth().signInWithCredential(credential); // **** THIS FAILS
// ... with "Invalid IdP response/credential: http://localhost?id_token=eyJ0eXAiO...0e9BeTObQ&providerId=microsoft.com"
The microsoft popup part works great and I'm logged in successfully there and able to issue API calls. But I can't get firebase to accept those credentials to log in on firebase. I've enabled the Microsoft OAuth provider in the Firebase Authentication panel and I'm sure the CLIENT_ID's match...
Maybe I'm constructing the OAuthCredential wrong?
Help greatly appreciated!
ps: this is a continuation of this SO post.

What is the logic behind this authentication in Firebase for Flutter?

I'm following the Firebase for Flutter Codelab and on the 8th step there's this _ensureLoggedIn() function:
final _googleSignIn = new GoogleSignIn();
final _auth = FirebaseAuth.instance;
Future<Null> _ensureLoggedIn() async {
GoogleSignInAccount user = _googleSignIn.currentUser;
if (user == null)
user = await _googleSignIn.signInSilently();
if (user == null) {
await _googleSignIn.signIn();
analytics.logLogin();
}
if (await auth.currentUser() == null) {
GoogleSignInAuthentication credentials =
await _googleSignIn.currentUser.authentication;
await auth.signInWithGoogle(
idToken: credentials.idToken,
accessToken: credentials.accessToken,
);
}
}
As a newbie to both Flutter and Firebase frameworks, i'm really struggling to understand the logic behind it: First we attempt to log the user with the GoogleSignIn package, and then regardless of what we will have in user we try to auth the user again with FirebaseAuth, which in turn will also use the GoogleSignIn.
Could you explain me why we do both?
My target is to have two separate screens for users who open my app - one for unauthorized/anonymous (which will have Log In and Register options), and one for authorized users that will see the normal app interface.
The login example in that Codelab seems poorly written, as the user can potentially cancel the non-silent signIn() and then googleSignIn.currentUser will be null when they try to access googleSignIn.currentUser.authentication. I think a better way to handle it is trigger the Google sign in and handle the Firebase auth in in the googleSignIn.onAuthStateChanged listener.
As for why both are used in that example: If you want to authenticate a user on Firebase with a Google account you have to provide an idToken and accessToken, which must be obtained from a valid Google login. So first, you have them sign in to their Google account (via googleSignIn) and use the tokens from that to authenticate with Firebase (via auth).
Using googleSignIn is only required if you want to authenticate with Firebase using a Google account; you can also use Firebase Auth with a username/password combination (which requires that the account be created on Firebase first) or with a token from a Facebook login, or some other OAuth token.

Resources