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

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.

Related

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).

I am new to flutter web, how to authenticate users using firebase phone authentication, is there a way to keep user logged in?

Here's a basic code for sign-in using firebase phone authentication, which can be understood from basic flutter-fire documentation, which doesn't explain practically how to implement firebase phone authentication in flutter web. Flutter Native mobile phone authentication is easy :)
Also, how to remember the signed user for certain days?
FirebaseAuth auth = FirebaseAuth.instance;
ConfirmationResult confirmationResult = await auth.signInWithPhoneNumber('+44 7123 123 456');
UserCredential userCredential = await confirmationResult.confirm('123456');
Users automatically stay signed in to Firebase. To pick up the user's authentication state when the app is restarted, check the documentation on authentication state, which has this handy example:
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
Also see the answer I just gave to: Anonymous User not detecting by firebase code error

Flutter How to Link Multiple Auth Providers to an Firebase Account?

I am using firebase in my flutter application. In my app user can sign in or register using 3 ways.
Email id and password.
Google
Facebook.
This is my Firebase setting.
This is my user collection.
Now my doubt is that how do I link multiple auth providers for a user having same email id ?
I am storing user's info into User collection with Uid. If I enable multiple account to multiple providers how can I store user's data in same document ?
I have search a lot on google but didn't find proper solution.
For registration with email ID I am using this.
final newUser = await _auth.createUserWithEmailAndPassword(
email: widget.email, password: widget.pass);
Then I am storing user's data into User collection. (In this I have one more form, from where I am getting other data).
Google sign in code
Future<User> signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
// Create a new credential
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
final UserCredential authResult = await _firebaseAuth.signInWithCredential(credential);
final User user = authResult.user;
print('authResult');
print(authResult);
return user;
}
Users are actually allowed to sign in to your app using multiple authentication providers by linking auth provider credentials to an existing user account.
You can follow the documentation here for the process of linking auth provider credentials to an existing user account:
I suggest to also visit the official documentation of Firebase for Flutter.

Is there a way to determine if a user is signed in via custom firebase account or google account in flutter?

I'm building a flutter app and I have options on my login page to create an account (which gets saved in firebase) or just sign in with Google (which also creates an account on firebase). On other pages, I have a logout button which only logs out of custom firebase account because it's calling this function:
Future<void> signOut() async {
return _firebaseAuth.signOut();
}
I also have this function for google sign out:
Future<void> signOutGoogle() async{
return googleSignIn.signOut();
}
variables declared at top:
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
FirebaseUser user;
Is there a way to determine if the user is signed in via Google or custom firebase? That way I'd be able to determine which function to call when the user clicks Logout.
Thanks in advance
You can access FirebaseUser property called providerData which type is List<UserInfo>. UserInfo has a providerId which is fe. google.com, facebook.com, password (email) or phone.
You can find those values looking up the code.
print(user.providerData[0].providerId) // -> fe. google.com

Firebase Auth across multiple projects with different Providers

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

Resources