From 8c1d7cf5a708bd4b1afcb33504d599e4c5328ee9 Mon Sep 17 00:00:00 2001
From: Piotr Szydelko
Date: Mon, 9 Jan 2023 11:51:01 +0100
Subject: [PATCH] [firebase_auth] Add Firebase Auth plugin
---
.github/labeler.yml | 2 +
.github/recipe.yaml | 1 +
README.md | 3 +
packages/firebase_auth/.gitignore | 30 ++
packages/firebase_auth/CHANGELOG.md | 3 +
packages/firebase_auth/LICENSE | 29 ++
packages/firebase_auth/README.md | 18 +
packages/firebase_auth/analysis_options.yaml | 4 +
packages/firebase_auth/example/.gitignore | 46 ++
packages/firebase_auth/example/README.md | 13 +
.../firebase_auth/example/assets/github.png | Bin 0 -> 1571 bytes
.../example/lib/animated_error.dart | 51 ++
packages/firebase_auth/example/lib/auth.dart | 491 ++++++++++++++++++
.../example/lib/auth_service.dart | 261 ++++++++++
packages/firebase_auth/example/lib/main.dart | 103 ++++
.../firebase_auth/example/lib/profile.dart | 267 ++++++++++
.../firebase_auth/example/lib/sms_dialog.dart | 65 +++
packages/firebase_auth/example/pubspec.yaml | 36 ++
.../firebase_auth/example/tizen/.exportMap | 5 +
.../firebase_auth/example/tizen/.gitignore | 9 +
.../example/tizen/Runner.csproj.user | 6 +
.../firebase_auth/example/tizen/inc/runner.h | 6 +
.../example/tizen/project_def.prop | 30 ++
.../example/tizen/shared/res/ic_launcher.png | Bin 0 -> 1443 bytes
.../firebase_auth/example/tizen/src/runner.cc | 18 +
.../example/tizen/tizen-manifest.xml | 12 +
.../lib/firebase_auth_tizen.dart | 370 +++++++++++++
.../lib/src/confirmation_result.dart | 28 +
.../lib/src/firebase_auth_user.dart | 208 ++++++++
.../src/firebase_auth_user_credential.dart | 30 ++
.../firebase_auth/lib/src/multi_factor.dart | 7 +
.../lib/src/recaptcha_verifier.dart | 96 ++++
.../lib/src/utils/desktop_utils.dart | 109 ++++
packages/firebase_auth/pubspec.yaml | 32 ++
packages/firebase_core/pubspec.yaml | 2 +-
35 files changed, 2390 insertions(+), 1 deletion(-)
create mode 100644 packages/firebase_auth/.gitignore
create mode 100644 packages/firebase_auth/CHANGELOG.md
create mode 100644 packages/firebase_auth/LICENSE
create mode 100644 packages/firebase_auth/README.md
create mode 100644 packages/firebase_auth/analysis_options.yaml
create mode 100644 packages/firebase_auth/example/.gitignore
create mode 100644 packages/firebase_auth/example/README.md
create mode 100644 packages/firebase_auth/example/assets/github.png
create mode 100644 packages/firebase_auth/example/lib/animated_error.dart
create mode 100644 packages/firebase_auth/example/lib/auth.dart
create mode 100644 packages/firebase_auth/example/lib/auth_service.dart
create mode 100644 packages/firebase_auth/example/lib/main.dart
create mode 100644 packages/firebase_auth/example/lib/profile.dart
create mode 100644 packages/firebase_auth/example/lib/sms_dialog.dart
create mode 100644 packages/firebase_auth/example/pubspec.yaml
create mode 100644 packages/firebase_auth/example/tizen/.exportMap
create mode 100644 packages/firebase_auth/example/tizen/.gitignore
create mode 100644 packages/firebase_auth/example/tizen/Runner.csproj.user
create mode 100644 packages/firebase_auth/example/tizen/inc/runner.h
create mode 100644 packages/firebase_auth/example/tizen/project_def.prop
create mode 100644 packages/firebase_auth/example/tizen/shared/res/ic_launcher.png
create mode 100644 packages/firebase_auth/example/tizen/src/runner.cc
create mode 100644 packages/firebase_auth/example/tizen/tizen-manifest.xml
create mode 100644 packages/firebase_auth/lib/firebase_auth_tizen.dart
create mode 100644 packages/firebase_auth/lib/src/confirmation_result.dart
create mode 100644 packages/firebase_auth/lib/src/firebase_auth_user.dart
create mode 100644 packages/firebase_auth/lib/src/firebase_auth_user_credential.dart
create mode 100644 packages/firebase_auth/lib/src/multi_factor.dart
create mode 100644 packages/firebase_auth/lib/src/recaptcha_verifier.dart
create mode 100644 packages/firebase_auth/lib/src/utils/desktop_utils.dart
create mode 100644 packages/firebase_auth/pubspec.yaml
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 9c3ec5028..fefb330b6 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -9,6 +9,8 @@
- packages/connectivity_plus/**/*
"p: device_info_plus":
- packages/device_info_plus/**/*
+"p: firebase_auth":
+ - packages/firebase_auth/**/*
"p: firebase_core":
- packages/firebase_core/**/*
"p: flutter_app_badger":
diff --git a/.github/recipe.yaml b/.github/recipe.yaml
index 1513a1e8d..bcb7d434f 100644
--- a/.github/recipe.yaml
+++ b/.github/recipe.yaml
@@ -27,6 +27,7 @@ plugins:
google_sign_in: []
image_picker: []
firebase_core: []
+ firebase_auth: []
tizen_log: []
tizen_notification: []
wearable_rotary: []
diff --git a/README.md b/README.md
index e0c6f09f2..a6a71a2c9 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,8 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina
| [**connectivity_plus_tizen**](packages/connectivity_plus) | [connectivity_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/connectivity_plus) (1st-party) | [![pub package](https://img.shields.io/pub/v/connectivity_plus_tizen.svg)](https://pub.dev/packages/connectivity_plus_tizen) | No |
| [**device_info_plus_tizen**](packages/device_info_plus) | [device_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus) (1st-party) | [![pub package](https://img.shields.io/pub/v/device_info_plus_tizen.svg)](https://pub.dev/packages/device_info_plus_tizen) | No |
| [**firebase_core_tizen**](packages/firebase_core) | [firebase_core](https://github.com/firebase/flutterfire/tree/master/packages/firebase_core) | [![pub package](https://img.shields.io/pub/v/firebase_core_tizen.svg)](https://pub.dev/packages/firebase_core_tizen) | No |
+| [**firebase_auth_tizen**](packages/firebase_auth) | [firebase_auth](https://github.com/firebase/flutterfire/tree/master/packages/firebase_auth) | [![pub package](https://img.shields.io/pub/v/firebase_auth_tizen.svg)](https://pub.dev/packages/firebase_auth_tizen) | No |
+| [**firebase_functions_tizen**](packages/firebase_functions) | [firebase_functions](https://github.com/firebase/flutterfire/tree/master/packages/cloud_functions) | [![pub package](https://img.shields.io/pub/v/firebase_functions_tizen.svg)](https://pub.dev/packages/firebase_functions_tizen) | No |
| [**flutter_app_badger_tizen**](packages/flutter_app_badger) | [flutter_app_badger](https://github.com/g123k/flutter_app_badger) (3rd-party) | [![pub package](https://img.shields.io/pub/v/flutter_app_badger_tizen.svg)](https://pub.dev/packages/flutter_app_badger_tizen) | No |
| [**flutter_secure_storage_tizen**](packages/flutter_secure_storage) | [flutter_secure_storage](https://github.com/mogol/flutter_secure_storage) (3rd-party) | [![pub package](https://img.shields.io/pub/v/flutter_secure_storage_tizen.svg)](https://pub.dev/packages/flutter_secure_storage_tizen) | No |
| [**flutter_tts_tizen**](packages/flutter_tts) | [flutter_tts](https://github.com/dlutton/flutter_tts) (3rd-party) | [![pub package](https://img.shields.io/pub/v/flutter_tts_tizen.svg)](https://pub.dev/packages/flutter_tts_tizen) | No |
@@ -61,6 +63,7 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina
| [**connectivity_plus_tizen**](packages/connectivity_plus) | 4.0 | ✔️ | ⚠️ | ✔️ | ✔️ | Returns incorrect connection status |
| [**device_info_plus_tizen**](packages/device_info_plus) | 4.0 | ✔️ | ✔️ | ✔️ | ✔️ |
| [**firebase_core**](packages/firebase_core) | 4.0 | ✔️ | ✔️ | ✔️ | ✔️ |
+| [**firebase_auth**](packages/firebase_auth) | 4.0 | ✔️ | ✔️ | ✔️ | ✔️ |
| [**flutter_app_badger_tizen**](packages/flutter_app_badger) | 4.0 | ✔️ | ✔️ | ❌ | ❌ | API not supported |
| [**flutter_secure_storage_tizen**](packages/flutter_secure_storage) | 4.0 | ✔️ | ✔️ | ✔️ | ✔️ |
| [**flutter_tts_tizen**](packages/flutter_tts) | 4.0 | ✔️ | ✔️ | ✔️ | ✔️ |
diff --git a/packages/firebase_auth/.gitignore b/packages/firebase_auth/.gitignore
new file mode 100644
index 000000000..96486fd93
--- /dev/null
+++ b/packages/firebase_auth/.gitignore
@@ -0,0 +1,30 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.packages
+build/
diff --git a/packages/firebase_auth/CHANGELOG.md b/packages/firebase_auth/CHANGELOG.md
new file mode 100644
index 000000000..607323422
--- /dev/null
+++ b/packages/firebase_auth/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.1.0
+
+* Initial release.
diff --git a/packages/firebase_auth/LICENSE b/packages/firebase_auth/LICENSE
new file mode 100644
index 000000000..5841c4de2
--- /dev/null
+++ b/packages/firebase_auth/LICENSE
@@ -0,0 +1,29 @@
+BSD-3-Clause
+------------
+
+Copyright (c) 2016-present Invertase Limited & Contributors
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Creative Commons Attribution 3.0 License
+----------------------------------------
+
+Copyright (c) 2016-present Invertase Limited & Contributors
+
+Documentation and other instructional materials provided for this project
+(including on a separate documentation repository or it's documentation website) are
+licensed under the Creative Commons Attribution 3.0 License. Code samples/blocks
+contained therein are licensed under the BSD-3-Clause License (the "License"), as above.
+
+You may obtain a copy of the Creative Commons Attribution 3.0 License at
+
+ https://creativecommons.org/licenses/by/3.0/
diff --git a/packages/firebase_auth/README.md b/packages/firebase_auth/README.md
new file mode 100644
index 000000000..3d68d0bae
--- /dev/null
+++ b/packages/firebase_auth/README.md
@@ -0,0 +1,18 @@
+# firebase_auth_tizen
+
+A new Flutter plugin project.
+
+## Getting Started
+
+This project is a starting point for a Flutter
+[plug-in package](https://flutter.dev/developing-packages/),
+a specialized package that includes platform-specific implementation code for
+Android and/or iOS.
+
+For help getting started with Flutter development, view the
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
+
+The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported.
+To add platforms, run `flutter create -t plugin --platforms .` in this directory.
+You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
diff --git a/packages/firebase_auth/analysis_options.yaml b/packages/firebase_auth/analysis_options.yaml
new file mode 100644
index 000000000..a5744c1cf
--- /dev/null
+++ b/packages/firebase_auth/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/packages/firebase_auth/example/.gitignore b/packages/firebase_auth/example/.gitignore
new file mode 100644
index 000000000..0fa6b675c
--- /dev/null
+++ b/packages/firebase_auth/example/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/firebase_auth/example/README.md b/packages/firebase_auth/example/README.md
new file mode 100644
index 000000000..929123d5d
--- /dev/null
+++ b/packages/firebase_auth/example/README.md
@@ -0,0 +1,13 @@
+# example
+
+An example application for firebase_auth_tizen plugin - Firebase Auth for Tizen.
+
+## Getting Started
+
+See [flutter-tizen Getting Started](https://github.com/flutter-tizen/flutter-tizen/blob/master/doc/get-started.md) for information about preparing environment.
+
+If you have flutter-tizen installed and a Tizen device/emulator available, this application can be started with:
+```
+flutter-tizen pub get
+flutter-tizen run
+```
\ No newline at end of file
diff --git a/packages/firebase_auth/example/assets/github.png b/packages/firebase_auth/example/assets/github.png
new file mode 100644
index 0000000000000000000000000000000000000000..628da97c70890c73e59204f5b140c4e67671e92d
GIT binary patch
literal 1571
zcmaJ>c~BE~6izDPQq)#Nu*KOf(n^(VHY9;fiINM65``pc+9*v(mL$bwfCjbc%v9V{8r9iX|O%>Nr%pLD2qT{mty}c=LVleeamv
znz3SOSm@kP8jThvOOq(56Yzh*fz(booe!uZij=BJC6+_lbvQ~B8nA2>kXdv_RDtRY
z`5QXWWEySCe6vbTs^#f?J!WC*{1~RgVx!nJTJjQyO{dRANgx|FnymtGbD9%JmCh9^y)##j7{Dcqfn*1ta$rG89pJF6w-S7Z037$rr|y0;1Onp_
zGFJdT6Q!1C0AdVB0WOmpuV=AgAQ550Tn+-mivTtYPJmz*#75#_n9oV%!#rSOfmAfy
zki%C~=fTp1{O#BLpJ|0jj#m6#|LRWit-vq3PE1z9ZqyvET4sX$-Icqy7t
z<=aq5ff86AuBZBu6EjJsYWM0uejufWFTwPA7Su}0Bm$7KFb!q{Um_8~A{LUG#1l(l
zSehUda@kU8LIRg9fkk2tZ;~ss5~R+mM<==F7hLHpxqLB>>PQS%Vc7b~?q!%T5+h8Q
z4G=4Nzyi5WZ?^gkasJ{?Xhm`JC#WG6$1K2jb@=9&D3EgD#3UhGh#*21rJjulVXjCF
zvp76q62jt0zzMG5C7DlfMgPl%C^3+~wf|}Lq=}jz|MmIcQjh1Ok6NjD$Em^Iv26D>
z8tt_TnM9~^Tt8mflRGPOrrX|HtT3gG4LEuuk{g2Rn}QgJIa?gZo))!!=o_l9bvD%A
zZ`aHajl8#~u?!4f7F#*b*->A=R2L)6!>saz?h>#wTXT-I(XmQ
zx{84skS>k=i~i`(6k4C7;Zpfx%dCPVjPayMf8pugtGM=~s=Id1l#8MZJ1-73wV#Q3
zR3>v3%}jbQs1f_Z0xo;%=LILlA+nTpKI4ha%xWW}uqHrNao~&T4AY6m`P$_n-6h*g
zhoX+e4n%~gl_lhe#s+AMb7d{5WzvYTa%6Q~si@@4{;s(0zU|H&P3fE+t{7X`S#Cj@
zC#vd}^4pcBD*77Ny5=j$h8EL2_t$O38$SQiJ6fPjJMimypr~MB2(&P0aI|h}$64<0
z>_~duqNjaT=DM^6+N{&B_lED;F2wrl?!4Lk*2((x!fmrcsw+=cI^qttuZ9C}-m~5E
z-ryYVpL%^xR#&(0YI5hz<(}F7-p)?FPcyJO-zVO>%9ZDXJH8pnY;GJYFDQ>vd#j_*
zRrd}L(r=!g+1#nQwsO?kpS`Qq8`NxE+Zy{gf7*_7J*U2V_|NpLo{iasj7VCg_V9&|
ShohtYzipXxh2)4xTk _AnimatedErrorState();
+}
+
+class _AnimatedErrorState extends State
+ with SingleTickerProviderStateMixin {
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ width: double.infinity,
+ child: AnimatedSize(
+ curve: Curves.easeInOut,
+ clipBehavior: Clip.antiAliasWithSaveLayer,
+ duration: const Duration(milliseconds: 180),
+ child: Visibility(
+ key: ValueKey(widget.show),
+ visible: widget.show,
+ child: Container(
+ alignment: AlignmentDirectional.centerStart,
+ padding: const EdgeInsets.all(10),
+ margin: const EdgeInsets.symmetric(vertical: 10),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.secondary,
+ borderRadius: BorderRadius.circular(5),
+ ),
+ child: AnimatedSwitcher(
+ duration: const Duration(milliseconds: 300),
+ child: Text(
+ widget.show ? widget.text : '',
+ key: ValueKey(widget.text),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/firebase_auth/example/lib/auth.dart b/packages/firebase_auth/example/lib/auth.dart
new file mode 100644
index 000000000..be559b999
--- /dev/null
+++ b/packages/firebase_auth/example/lib/auth.dart
@@ -0,0 +1,491 @@
+// ignore_for_file: use_build_context_synchronously, public_member_api_docs
+
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_signin_button/flutter_signin_button.dart';
+
+import 'animated_error.dart';
+import 'auth_service.dart';
+import 'sms_dialog.dart';
+
+/// Helper class to show a snackbar using the passed context.
+class ScaffoldSnackbar {
+ ScaffoldSnackbar(this._context);
+
+ /// The scaffold of current context.
+ factory ScaffoldSnackbar.of(BuildContext context) {
+ return ScaffoldSnackbar(context);
+ }
+
+ final BuildContext _context;
+
+ /// Helper method to show a SnackBar.
+ void show(String message) {
+ ScaffoldMessenger.of(_context)
+ ..hideCurrentSnackBar()
+ ..showSnackBar(
+ SnackBar(
+ width: 400,
+ content: Text(message),
+ behavior: SnackBarBehavior.floating,
+ ),
+ );
+ }
+}
+
+/// The mode of the current auth session, either [AuthMode.login] or [AuthMode.register].
+enum AuthMode { login, register, phone }
+
+/// Supported Social OAuth providers.
+enum SocialOAuthProvider { google, facebook, twitter, github, apple }
+
+extension on AuthMode {
+ String get label => this == AuthMode.login
+ ? 'Sign in'
+ : this == AuthMode.phone
+ ? 'Sign in'
+ : 'Register';
+}
+
+extension on SocialOAuthProvider {
+ Buttons get button {
+ switch (this) {
+ case SocialOAuthProvider.google:
+ return Buttons.Google;
+ case SocialOAuthProvider.facebook:
+ return Buttons.Facebook;
+ case SocialOAuthProvider.twitter:
+ return Buttons.Twitter;
+ case SocialOAuthProvider.github:
+ return Buttons.GitHub;
+ case SocialOAuthProvider.apple:
+ return Buttons.Apple;
+ }
+ }
+}
+
+/// Entrypoint example for various sign-in flows with Firebase.
+class AuthGate extends StatefulWidget {
+ const AuthGate({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _AuthGateState();
+}
+
+class _AuthGateState extends State {
+ final authService = AuthService();
+
+ TextEditingController emailController = TextEditingController();
+ TextEditingController passwordController = TextEditingController();
+ TextEditingController phoneController = TextEditingController();
+
+ GlobalKey formKey = GlobalKey();
+ String error = '';
+
+ AuthMode mode = AuthMode.login;
+
+ //final _auth = FirebaseAuth.instance;
+
+ bool isLoading = false;
+
+ void setIsLoading() {
+ setState(() {
+ isLoading = !isLoading;
+ });
+ }
+
+ void resetError() {
+ if (error.isNotEmpty) {
+ setState(() {
+ error = '';
+ });
+ }
+ }
+
+ Future _resetPassword() async {
+ resetError();
+
+ String? email;
+
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return AlertDialog(
+ actions: [
+ TextButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ child: const Text('Send'),
+ ),
+ ],
+ content: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Text('Enter your email'),
+ const SizedBox(height: 20),
+ TextFormField(
+ onChanged: (value) {
+ email = value;
+ },
+ ),
+ ],
+ ),
+ );
+ },
+ );
+
+ if (email != null) {
+ try {
+ await authService.resetPassword(email!);
+ ScaffoldSnackbar.of(context).show('Password reset email is sent');
+ } catch (e) {
+ ScaffoldSnackbar.of(context).show('Error resetting');
+ }
+ }
+ }
+
+ Future _emailAuth() async {
+ resetError();
+
+ if (formKey.currentState?.validate() ?? false) {
+ setIsLoading();
+
+ try {
+ await authService.emailAuth(
+ mode,
+ email: emailController.text,
+ password: passwordController.text,
+ );
+ } on FirebaseAuthException catch (e) {
+ setIsLoading();
+
+ setState(() {
+ error = '${e.message}';
+ });
+ } catch (e) {
+ setIsLoading();
+ }
+ }
+ }
+
+ Future _anonymousAuth() async {
+ setIsLoading();
+
+ try {
+ await authService.anonymousAuth();
+ } on FirebaseAuthException catch (e) {
+ setState(() {
+ error = '${e.message}';
+ });
+ } catch (e) {
+ setState(() {
+ error = '$e';
+ });
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _phoneAuth() async {
+ resetError();
+
+ try {
+ setIsLoading();
+ await authService.phoneAuth(
+ phoneNumber: phoneController.text,
+ smsCode: () {
+ return ExampleDialog.of(context).show('SMS Code:', 'Sign in');
+ },
+ );
+ } catch (e) {
+ setState(() {
+ error = '$e';
+ });
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _googleSignIn(BuildContext context) async {
+ resetError();
+
+ try {
+ setIsLoading();
+ await authService.googleSignIn(context);
+ } on FirebaseAuthException catch (e) {
+ setState(() {
+ error = '${e.message}';
+ });
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _twitterSignIn() async {
+ resetError();
+
+ try {
+ setIsLoading();
+ await authService.twitterSignIn();
+ } on FirebaseAuthException catch (e) {
+ setState(() {
+ error = '${e.message}';
+ });
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _facebookSignIn(BuildContext context) async {
+ resetError();
+
+ try {
+ setIsLoading();
+ await authService.facebookSignIn(context);
+ } on FirebaseAuthException catch (e) {
+ setState(() {
+ error = '${e.message}';
+ });
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _githubSignIn(BuildContext context) async {
+ resetError();
+
+ try {
+ setIsLoading();
+ await authService.githubSignIn(context);
+ } on FirebaseAuthException catch (e) {
+ setState(() {
+ error = '${e.message}';
+ });
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _appleSignIn() async {
+ try {
+ setIsLoading();
+ await authService.appleSignIn();
+ } on FirebaseAuthException catch (e) {
+ setState(() {
+ error = '${e.message}';
+ });
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Stack(
+ children: [
+ SingleChildScrollView(
+ child: Padding(
+ padding: const EdgeInsets.all(20),
+ child: Center(
+ child: SizedBox(
+ width: 400,
+ child: Form(
+ key: formKey,
+ autovalidateMode: AutovalidateMode.onUserInteraction,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ AnimatedError(text: error, show: error.isNotEmpty),
+ const SizedBox(height: 20),
+ if (mode != AuthMode.phone)
+ Column(
+ children: [
+ TextFormField(
+ controller: emailController,
+ decoration:
+ const InputDecoration(hintText: 'Email'),
+ validator: (value) =>
+ value != null && value.isNotEmpty
+ ? null
+ : 'Required',
+ ),
+ const SizedBox(height: 20),
+ TextFormField(
+ controller: passwordController,
+ obscureText: true,
+ decoration:
+ const InputDecoration(hintText: 'Password'),
+ validator: (value) =>
+ value != null && value.isNotEmpty
+ ? null
+ : 'Required',
+ ),
+ ],
+ ),
+ const SizedBox(height: 10),
+ if (mode != AuthMode.phone)
+ TextButton(
+ onPressed: _resetPassword,
+ child: const Text('Forgot password?'),
+ ),
+ if (mode == AuthMode.phone)
+ TextFormField(
+ controller: phoneController,
+ decoration: const InputDecoration(
+ hintText: '+16505550101',
+ labelText: 'Phone number',
+ ),
+ validator: (value) =>
+ value != null && value.isNotEmpty
+ ? null
+ : 'Required',
+ ),
+ const SizedBox(height: 20),
+ SizedBox(
+ width: double.infinity,
+ height: 50,
+ child: ElevatedButton(
+ onPressed: isLoading
+ ? null
+ : mode == AuthMode.phone
+ ? _phoneAuth
+ : _emailAuth,
+ child: Text(mode.label),
+ ),
+ ),
+ const SizedBox(height: 20),
+ ...SocialOAuthProvider.values
+ .where(
+ (provider) =>
+ defaultTargetPlatform == TargetPlatform.macOS ||
+ provider != SocialOAuthProvider.apple,
+ )
+ .map((provider) {
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 10),
+ child: SizedBox(
+ width: double.infinity,
+ height: 50,
+ child: SignInButton(
+ provider.button,
+ onPressed: () {
+ if (!isLoading) {
+ switch (provider) {
+ case SocialOAuthProvider.google:
+ _googleSignIn(context);
+ break;
+ case SocialOAuthProvider.facebook:
+ _facebookSignIn(context);
+ break;
+ case SocialOAuthProvider.twitter:
+ _twitterSignIn();
+ break;
+ case SocialOAuthProvider.github:
+ _githubSignIn(context);
+ break;
+ case SocialOAuthProvider.apple:
+ _appleSignIn();
+ break;
+ }
+ }
+ },
+ ),
+ ),
+ );
+ }).toList(),
+ const SizedBox(height: 20),
+ SizedBox(
+ width: double.infinity,
+ height: 50,
+ child: OutlinedButton(
+ onPressed: isLoading
+ ? null
+ : () {
+ if (mode != AuthMode.phone) {
+ setState(() {
+ mode = AuthMode.phone;
+ });
+ } else {
+ setState(() {
+ mode = AuthMode.login;
+ });
+ }
+ },
+ child: Text(
+ mode != AuthMode.phone
+ ? 'Sign in with Phone Number'
+ : 'sign in with Email and Password',
+ ),
+ ),
+ ),
+ const SizedBox(height: 20),
+ if (mode != AuthMode.phone)
+ RichText(
+ text: TextSpan(
+ style: Theme.of(context).textTheme.bodyText1,
+ children: [
+ TextSpan(
+ text: mode == AuthMode.login
+ ? "Don't have an account? "
+ : 'You have an account? ',
+ ),
+ TextSpan(
+ text: mode == AuthMode.login
+ ? 'Register now'
+ : 'Click to login',
+ style: const TextStyle(color: Colors.blue),
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ setState(() {
+ mode = mode == AuthMode.login
+ ? AuthMode.register
+ : AuthMode.login;
+ });
+ },
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 10),
+ RichText(
+ text: TextSpan(
+ style: Theme.of(context).textTheme.bodyText1,
+ children: [
+ const TextSpan(text: 'Or '),
+ TextSpan(
+ text: 'continue as guest',
+ style: const TextStyle(color: Colors.blue),
+ recognizer: TapGestureRecognizer()
+ ..onTap = _anonymousAuth,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ AnimatedSwitcher(
+ duration: const Duration(milliseconds: 200),
+ child: isLoading
+ ? Container(
+ color: Colors.black.withOpacity(0.8),
+ child: const Center(
+ child: CircularProgressIndicator.adaptive(),
+ ),
+ )
+ : const SizedBox(),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/firebase_auth/example/lib/auth_service.dart b/packages/firebase_auth/example/lib/auth_service.dart
new file mode 100644
index 000000000..2a67a5200
--- /dev/null
+++ b/packages/firebase_auth/example/lib/auth_service.dart
@@ -0,0 +1,261 @@
+// ignore_for_file: public_member_api_docs
+
+import 'dart:convert';
+import 'dart:math';
+
+import 'package:crypto/crypto.dart';
+import 'package:desktop_webview_auth/desktop_webview_auth.dart';
+import 'package:desktop_webview_auth/google.dart';
+import 'package:desktop_webview_auth/twitter.dart';
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:flutter/material.dart';
+import 'package:sign_in_with_apple/sign_in_with_apple.dart';
+import 'package:webview_auth_tizen/providers/google.dart';
+import 'package:webview_auth_tizen/providers/facebook.dart';
+import 'package:webview_auth_tizen/providers/github.dart';
+
+import 'auth.dart';
+
+const _redirectUri =
+ 'https://react-native-firebase-testing.firebaseapp.com/__/auth/handler';
+const _googleClientId =
+ '448618578101-sg12d2qin42cpr00f8b0gehs5s7inm0v.apps.googleusercontent.com';
+const _twitterApiKey = '';
+const _twitterApiSecretKey = '';
+const _facebookClientId = '';
+const _githubClientId = '';
+const _githubClientSecret = '';
+
+/// Provide authentication services with [FirebaseAuth].
+class AuthService {
+ final _auth = FirebaseAuth.instance;
+
+ Future emailAuth(
+ AuthMode mode, {
+ required String email,
+ required String password,
+ }) {
+ assert(mode != AuthMode.phone);
+
+ try {
+ if (mode == AuthMode.login) {
+ return _auth.signInWithEmailAndPassword(
+ email: email,
+ password: password,
+ );
+ } else {
+ return _auth.createUserWithEmailAndPassword(
+ email: email,
+ password: password,
+ );
+ }
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future anonymousAuth() {
+ try {
+ return _auth.signInAnonymously();
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future phoneAuth({
+ required String phoneNumber,
+ required Future Function() smsCode,
+ }) async {
+ try {
+ final confirmationResult =
+ await FirebaseAuth.instance.signInWithPhoneNumber(phoneNumber);
+
+ final code = await smsCode.call();
+
+ if (code != null) {
+ await confirmationResult.confirm(code);
+ } else {
+ return;
+ }
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future googleSignIn(BuildContext context) async {
+ try {
+ // Handle login by a third-party provider.
+ final result = await GoogleLoginPage.signIn(
+ _googleClientId,
+ 'https://www.googleapis.com/auth/userinfo.email',
+ _redirectUri,
+ context,
+ );
+
+ // Create a new credential
+ final credential = GoogleAuthProvider.credential(
+ idToken: result.idToken,
+ accessToken: result.accessToken,
+ );
+
+ // Once signed in, return the UserCredential
+ await _auth.signInWithCredential(credential);
+ } on FirebaseAuthException catch (_) {
+ rethrow;
+ }
+ }
+
+ Future twitterSignIn() async {
+ try {
+ // Handle login by a third-party provider.
+ final result = await DesktopWebviewAuth.signIn(
+ TwitterSignInArgs(
+ apiKey: _twitterApiKey,
+ apiSecretKey: _twitterApiSecretKey,
+ redirectUri: _redirectUri,
+ ),
+ );
+
+ if (result != null) {
+ // Create a new credential
+ final credential = TwitterAuthProvider.credential(
+ secret: result.tokenSecret!,
+ accessToken: result.accessToken!,
+ );
+
+ // Once signed in, return the UserCredential
+ await _auth.signInWithCredential(credential);
+ } else {
+ return;
+ }
+ } on FirebaseAuthException catch (_) {
+ rethrow;
+ }
+ }
+
+ Future facebookSignIn(BuildContext context) async {
+ try {
+ // Handle login by a third-party provider.
+ final result = await FacebookLoginPage.signIn(
+ _facebookClientId,
+ _redirectUri,
+ context,
+ );
+
+ final credential = FacebookAuthProvider.credential(result.accessToken!);
+
+ // Once signed in, return the UserCredential
+ await _auth.signInWithCredential(credential);
+ } on FirebaseAuthException catch (_) {
+ rethrow;
+ }
+ }
+
+ Future githubSignIn(BuildContext context) async {
+ try {
+ // Handle login by a third-party provider.
+ final result = await GithubLoginPage.signIn(
+ _githubClientId,
+ 'user',
+ _redirectUri,
+ _githubClientSecret,
+ context,
+ );
+
+ // Create a new credential
+ final credential = GithubAuthProvider.credential(result.accessToken!);
+
+ // Once signed in, return the UserCredential
+ await _auth.signInWithCredential(credential);
+ } on FirebaseAuthException catch (_) {
+ rethrow;
+ }
+ }
+
+ /// Generates a cryptographically secure random nonce, to be included in a
+ /// credential request.
+ String _generateNonce([int length = 32]) {
+ const charset =
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
+ final random = Random.secure();
+ return List.generate(length, (_) => charset[random.nextInt(charset.length)])
+ .join();
+ }
+
+ /// Returns the sha256 hash of [input] in hex notation.
+ String _sha256ofString(String input) {
+ final bytes = utf8.encode(input);
+ final digest = sha256.convert(bytes);
+ return digest.toString();
+ }
+
+ Future appleSignIn() async {
+ try {
+ final rawNonce = _generateNonce();
+ final nonce = _sha256ofString(rawNonce);
+
+ final credential = await SignInWithApple.getAppleIDCredential(
+ scopes: [
+ AppleIDAuthorizationScopes.email,
+ AppleIDAuthorizationScopes.fullName,
+ ],
+ );
+
+ debugPrint('${credential.state}');
+
+ if (credential.identityToken != null) {
+ // Create an `OAuthCredential` from the credential returned by Apple.
+ final oauthCredential = OAuthProvider('apple.com').credential(
+ idToken: credential.identityToken,
+ rawNonce: nonce,
+ );
+
+ // Sign in the user with Firebase. If the nonce we generated earlier does
+ // not match the nonce in `appleCredential.identityToken`, sign in will fail.
+ await FirebaseAuth.instance.signInWithCredential(oauthCredential);
+ } else {
+ return;
+ }
+ } on FirebaseAuthException catch (_) {
+ rethrow;
+ }
+ }
+
+ Future resetPassword(String email) {
+ try {
+ return FirebaseAuth.instance.sendPasswordResetEmail(email: email);
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future linkWithGoogle() async {
+ try {
+ final result = await DesktopWebviewAuth.signIn(
+ GoogleSignInArgs(
+ clientId: _googleClientId,
+ redirectUri: _redirectUri,
+ scope: 'https://www.googleapis.com/auth/userinfo.email',
+ ),
+ );
+
+ if (result != null) {
+ // Create a new credential
+ final credential = GoogleAuthProvider.credential(
+ accessToken: result.accessToken,
+ idToken: result.idToken,
+ );
+
+ // Once signed in, return the UserCredential
+ await _auth.currentUser?.linkWithCredential(credential);
+ }
+ } on FirebaseAuthException catch (_) {
+ rethrow;
+ }
+ }
+
+ /// Sign the Firebase user out.
+ Future signOut() async {
+ await _auth.signOut();
+ }
+}
diff --git a/packages/firebase_auth/example/lib/main.dart b/packages/firebase_auth/example/lib/main.dart
new file mode 100644
index 000000000..883551259
--- /dev/null
+++ b/packages/firebase_auth/example/lib/main.dart
@@ -0,0 +1,103 @@
+// ignore_for_file: depend_on_referenced_packages, public_member_api_docs
+
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:yaru/yaru.dart';
+
+import 'auth.dart';
+import 'profile.dart';
+
+FirebaseOptions get firebaseOptionsDefault {
+ if (defaultTargetPlatform == TargetPlatform.macOS) {
+ // Use iOS configurations on macOS.
+ return const FirebaseOptions(
+ apiKey: 'AIzaSyAHAsf51D0A407EklG1bs-5wA7EbyfNFg0',
+ appId: '1:448618578101:ios:3e76955ab6d49ecaac3efc',
+ messagingSenderId: '448618578101',
+ projectId: 'react-native-firebase-testing',
+ authDomain: 'react-native-firebase-testing.firebaseapp.com',
+ );
+ } else {
+ // Use web configurations on Linux, Windows and Web.
+ return const FirebaseOptions(
+ apiKey: 'AIzaSyAgUhHU8wSJgO5MVNy95tMT07NEjzMOfz0',
+ authDomain: 'react-native-firebase-testing.firebaseapp.com',
+ databaseURL: 'https://react-native-firebase-testing.firebaseio.com',
+ projectId: 'react-native-firebase-testing',
+ messagingSenderId: '448618578101',
+ appId: '1:448618578101:web:0b650370bb29e29cac3efc',
+ measurementId: 'G-F79DJ0VFGS',
+ );
+ }
+}
+
+// Requires that the Firebase Auth emulator is running locally
+// e.g via `melos run firebase:emulator`.
+Future main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ await Firebase.initializeApp(options: firebaseOptionsDefault);
+
+ await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
+
+ runApp(const AuthExampleApp());
+}
+
+/// The entry point of the application.
+///
+/// Returns a [MaterialApp].
+class AuthExampleApp extends StatelessWidget {
+ const AuthExampleApp({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Firebase Example App',
+ darkTheme: yaruDark,
+ theme: yaruLight,
+ home: Scaffold(
+ body: LayoutBuilder(
+ builder: (context, constraines) {
+ return Row(
+ children: [
+ Visibility(
+ visible: constraines.maxWidth >= 1200,
+ child: Expanded(
+ child: Container(
+ height: double.infinity,
+ color: Theme.of(context).colorScheme.primary,
+ child: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ 'Firebase Auth Desktop',
+ style: Theme.of(context).textTheme.headline4,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ Expanded(
+ child: StreamBuilder(
+ stream: FirebaseAuth.instance.authStateChanges(),
+ builder: (context, snapshot) {
+ if (snapshot.hasData) {
+ return const ProfilePage();
+ }
+ return const AuthGate();
+ },
+ ),
+ ),
+ ],
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/firebase_auth/example/lib/profile.dart b/packages/firebase_auth/example/lib/profile.dart
new file mode 100644
index 000000000..3ff69956a
--- /dev/null
+++ b/packages/firebase_auth/example/lib/profile.dart
@@ -0,0 +1,267 @@
+import 'dart:developer';
+
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:flutter/material.dart';
+
+import 'auth.dart';
+import 'auth_service.dart';
+import 'sms_dialog.dart';
+
+/// Displayed as a profile image if the user doesn't have one.
+const placeholderImage =
+ 'https://upload.wikimedia.org/wikipedia/commons/c/cd/Portrait_Placeholder_Square.png';
+
+/// Profile page shows after sign in or registerationg
+class ProfilePage extends StatefulWidget {
+ // ignore: public_member_api_docs
+ const ProfilePage({Key? key}) : super(key: key);
+
+ @override
+ // ignore: library_private_types_in_public_api
+ _ProfilePageState createState() => _ProfilePageState();
+}
+
+class _ProfilePageState extends State {
+ final authService = AuthService();
+
+ late User user;
+ late TextEditingController controller;
+
+ String? photoURL;
+
+ bool showSaveButton = false;
+ bool isLoading = false;
+
+ @override
+ void initState() {
+ user = FirebaseAuth.instance.currentUser!;
+ controller = TextEditingController(text: user.displayName);
+
+ controller.addListener(_onNameChanged);
+
+ FirebaseAuth.instance.userChanges().listen((event) {
+ if (event != null && mounted) {
+ setState(() {
+ user = event;
+ });
+ }
+ });
+
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ controller.removeListener(_onNameChanged);
+ super.dispose();
+ }
+
+ void setIsLoading() {
+ setState(() {
+ isLoading = !isLoading;
+ });
+ }
+
+ void _onNameChanged() {
+ setState(() {
+ if (controller.text == user.displayName || controller.text.isEmpty) {
+ showSaveButton = false;
+ } else {
+ showSaveButton = true;
+ }
+ });
+ }
+
+ /// Map User provider data into a list of Provider Ids.
+ List get userProviders => user.providerData.map((e) => e.providerId).toList();
+
+ Future updateDisplayName() async {
+ await user.updateDisplayName(controller.text);
+
+ setState(() {
+ showSaveButton = false;
+ });
+
+ // ignore: use_build_context_synchronously
+ ScaffoldSnackbar.of(context).show('Name updated');
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: FocusScope.of(context).unfocus,
+ child: Scaffold(
+ body: Stack(
+ children: [
+ Center(
+ child: SizedBox(
+ width: 400,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Stack(
+ children: [
+ CircleAvatar(
+ maxRadius: 60,
+ backgroundImage: NetworkImage(
+ user.photoURL ?? placeholderImage,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 10),
+ TextField(
+ textAlign: TextAlign.center,
+ controller: controller,
+ decoration: const InputDecoration(
+ border: InputBorder.none,
+ floatingLabelBehavior: FloatingLabelBehavior.never,
+ alignLabelWithHint: true,
+ label: Center(
+ child: Text(
+ 'Click to add a display name',
+ ),
+ ),
+ ),
+ ),
+ Text(user.email ?? user.phoneNumber ?? 'User'),
+ const SizedBox(height: 10),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ if (userProviders.contains('phone'))
+ const Icon(Icons.phone),
+ if (userProviders.contains('password'))
+ const Icon(Icons.mail),
+ if (userProviders.contains('google.com'))
+ SizedBox(
+ width: 24,
+ child: Image.network(
+ 'https://upload.wikimedia.org/wikipedia/commons/0/09/IOS_Google_icon.png',
+ ),
+ ),
+ if (userProviders.contains('github.com'))
+ SizedBox(
+ width: 24,
+ child: Image.asset('assets/github.png'),
+ ),
+ ],
+ ),
+ const SizedBox(height: 40),
+ if (!userProviders.contains('phone'))
+ TextButton(
+ onPressed: _linkWithPhone,
+ child: const Text('Link with phone number'),
+ ),
+ if (!userProviders.contains('google.com'))
+ TextButton(
+ onPressed: _linkWithOAuth,
+ child: const Text('Link with Google'),
+ ),
+ const SizedBox(height: 40),
+ if (userProviders.contains('phone'))
+ TextButton(
+ onPressed: _updatePhoneNumber,
+ child: const Text('Update my phone'),
+ ),
+ const SizedBox(height: 10),
+ TextButton(
+ onPressed: _signOut,
+ child: const Text('Sign out'),
+ ),
+ ],
+ ),
+ ),
+ ),
+ Positioned.directional(
+ textDirection: Directionality.of(context),
+ end: 40,
+ top: 40,
+ child: AnimatedSwitcher(
+ duration: const Duration(milliseconds: 200),
+ child: !showSaveButton
+ ? SizedBox(key: UniqueKey())
+ : TextButton(
+ onPressed: isLoading ? null : updateDisplayName,
+ child: const Text('Save changes'),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
+ Future _linkWithOAuth() async {
+ try {
+ await authService.linkWithGoogle();
+ } on FirebaseAuthException catch (e) {
+ ScaffoldSnackbar.of(context).show('${e.message}');
+ log('$e');
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _linkWithPhone() async {
+ try {
+ final phoneNumber =
+ await ExampleDialog.of(context).show('Phone number:', 'Link');
+
+ if (phoneNumber != null) {
+ final confirmationResult = await user.linkWithPhoneNumber(phoneNumber);
+
+ final smsCode =
+ // ignore: use_build_context_synchronously
+ await ExampleDialog.of(context).show('SMS Code:', 'Sign in');
+
+ if (smsCode != null) {
+ await confirmationResult.confirm(smsCode);
+ }
+ }
+ } on FirebaseAuthException catch (e) {
+ ScaffoldSnackbar.of(context).show('${e.message}');
+ log('$e');
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _updatePhoneNumber() async {
+ try {
+ final phoneNumber =
+ await ExampleDialog.of(context).show('Phone number:', 'Get SMS');
+
+ if (phoneNumber != null) {
+ final res =
+ await FirebaseAuth.instance.signInWithPhoneNumber(phoneNumber);
+
+ // ignore: use_build_context_synchronously
+ final smsCode =
+ // ignore: use_build_context_synchronously
+ await ExampleDialog.of(context).show('SMS Code:', 'Sign in');
+
+ if (smsCode != null) {
+ await user.updatePhoneNumber(
+ PhoneAuthProvider.credential(
+ verificationId: res.verificationId,
+ smsCode: smsCode,
+ ),
+ );
+
+ log('${user.phoneNumber}');
+ }
+ }
+ } on FirebaseAuthException catch (e) {
+ ScaffoldSnackbar.of(context).show('${e.message}');
+ log('$e');
+ } finally {
+ setIsLoading();
+ }
+ }
+
+ Future _signOut() async {
+ await authService.signOut();
+ }
+}
diff --git a/packages/firebase_auth/example/lib/sms_dialog.dart b/packages/firebase_auth/example/lib/sms_dialog.dart
new file mode 100644
index 000000000..a1f70d8de
--- /dev/null
+++ b/packages/firebase_auth/example/lib/sms_dialog.dart
@@ -0,0 +1,65 @@
+// ignore_for_file: public_member_api_docs
+
+import 'package:flutter/material.dart';
+
+class ExampleDialog {
+ ExampleDialog._(this.context);
+
+ factory ExampleDialog.of(BuildContext context) {
+ return ExampleDialog._(context);
+ }
+
+ final BuildContext context;
+
+ late String title;
+ late String buttonLabel;
+
+ Future show(String title, String buttonLabel) async {
+ this.title = title;
+ this.buttonLabel = buttonLabel;
+
+ return getSmsCodeFromUser(context);
+ }
+
+ Future getSmsCodeFromUser(BuildContext context) async {
+ String? smsCode;
+
+ // Update the UI - wait for the user to enter the SMS code
+ await showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (context) {
+ return AlertDialog(
+ title: Text(title),
+ actions: [
+ ElevatedButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ child: Text(buttonLabel),
+ ),
+ OutlinedButton(
+ onPressed: () {
+ smsCode = null;
+ Navigator.of(context).pop();
+ },
+ child: const Text('Cancel'),
+ ),
+ ],
+ content: Container(
+ padding: const EdgeInsets.all(20),
+ child: TextField(
+ onChanged: (value) {
+ smsCode = value;
+ },
+ textAlign: TextAlign.center,
+ autofocus: true,
+ ),
+ ),
+ );
+ },
+ );
+
+ return smsCode;
+ }
+}
diff --git a/packages/firebase_auth/example/pubspec.yaml b/packages/firebase_auth/example/pubspec.yaml
new file mode 100644
index 000000000..2f6fd46fd
--- /dev/null
+++ b/packages/firebase_auth/example/pubspec.yaml
@@ -0,0 +1,36 @@
+name: firebase_auth_tizen_example
+description: Firebase Auth for Tizen example.
+
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.17.0 <3.0.0"
+
+dependencies:
+ crypto: ^3.0.1
+ desktop_webview_auth: ^0.0.9
+ firebase_auth: ^3.4.1
+ firebase_auth_tizen:
+ path: ..
+ firebase_core: ^1.19.1
+ firebase_core_tizen:
+ path: ../../firebase_core
+ flutter:
+ sdk: flutter
+ flutter_signin_button: ^2.0.0
+ sign_in_with_apple: ^3.3.0
+ yaru: ^0.2.0
+ webview_auth_tizen:
+ git:
+ url: https://github.com/JRazek/webview_auth_tizen
+ ref: d0e2450f5a6b421eaa3567449db04024e7817db8
+
+dev_dependencies:
+ flutter_lints: ^2.0.0
+
+flutter:
+ uses-material-design: true
+ assets:
+ - assets/
diff --git a/packages/firebase_auth/example/tizen/.exportMap b/packages/firebase_auth/example/tizen/.exportMap
new file mode 100644
index 000000000..3b97a4f3b
--- /dev/null
+++ b/packages/firebase_auth/example/tizen/.exportMap
@@ -0,0 +1,5 @@
+{
+ global: main;
+ _IO_*;
+ local: *;
+};
diff --git a/packages/firebase_auth/example/tizen/.gitignore b/packages/firebase_auth/example/tizen/.gitignore
new file mode 100644
index 000000000..2ce470fa9
--- /dev/null
+++ b/packages/firebase_auth/example/tizen/.gitignore
@@ -0,0 +1,9 @@
+flutter/
+lib/*.so
+res/flutter_assets/
+res/icudtl.dat
+.cproject
+.sign
+crash-info/
+Debug/
+Release/
diff --git a/packages/firebase_auth/example/tizen/Runner.csproj.user b/packages/firebase_auth/example/tizen/Runner.csproj.user
new file mode 100644
index 000000000..6353f7eff
--- /dev/null
+++ b/packages/firebase_auth/example/tizen/Runner.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ /home/user/sources/flutter-tizen/embedding/csharp/Tizen.Flutter.Embedding/Tizen.Flutter.Embedding.csproj
+
+
\ No newline at end of file
diff --git a/packages/firebase_auth/example/tizen/inc/runner.h b/packages/firebase_auth/example/tizen/inc/runner.h
new file mode 100644
index 000000000..a2d45b6ee
--- /dev/null
+++ b/packages/firebase_auth/example/tizen/inc/runner.h
@@ -0,0 +1,6 @@
+#ifndef __RUNNER_H__
+#define __RUNNER_H__
+
+#include
+
+#endif /* __RUNNER_H__ */
diff --git a/packages/firebase_auth/example/tizen/project_def.prop b/packages/firebase_auth/example/tizen/project_def.prop
new file mode 100644
index 000000000..ffe6efae0
--- /dev/null
+++ b/packages/firebase_auth/example/tizen/project_def.prop
@@ -0,0 +1,30 @@
+# See https://docs.tizen.org/application/tizen-studio/native-tools/project-conversion
+# for details.
+
+APPNAME = runner
+type = app
+profile = common-5.5
+
+# Source files
+USER_SRCS += src/*.cc
+
+# User defines
+USER_DEFS =
+USER_UNDEFS =
+USER_CPP_DEFS = TIZEN_DEPRECATION DEPRECATION_WARNING
+USER_CPP_UNDEFS =
+
+# Compiler/linker flags
+USER_CFLAGS_MISC =
+USER_CPPFLAGS_MISC = -c -fmessage-length=0
+USER_LFLAGS = -lchromium-ewk -Wl,-rpath='$$ORIGIN' -Llib/${BUILD_ARCH}
+
+# Libraries and objects
+USER_LIB_DIRS = lib
+USER_LIBS =
+USER_OBJS =
+
+# User includes
+USER_INC_DIRS = inc flutter src
+USER_INC_FILES =
+USER_CPP_INC_FILES =
diff --git a/packages/firebase_auth/example/tizen/shared/res/ic_launcher.png b/packages/firebase_auth/example/tizen/shared/res/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0
GIT binary patch
literal 1443
zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY
zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv
za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw
zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3
z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w!
zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u
zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad
z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt*
zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW
zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w
z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf<
zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$
zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v
zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq
z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek
zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+
zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q
zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl
z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f
z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H
zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP
z(1P?z~7YxD~Rf<(a@_y`
literal 0
HcmV?d00001
diff --git a/packages/firebase_auth/example/tizen/src/runner.cc b/packages/firebase_auth/example/tizen/src/runner.cc
new file mode 100644
index 000000000..74d25472a
--- /dev/null
+++ b/packages/firebase_auth/example/tizen/src/runner.cc
@@ -0,0 +1,18 @@
+#include "runner.h"
+
+#include "generated_plugin_registrant.h"
+
+class App : public FlutterApp {
+ public:
+ bool OnCreate() {
+ if (FlutterApp::OnCreate()) {
+ RegisterPlugins(this);
+ }
+ return IsRunning();
+ }
+};
+
+int main(int argc, char *argv[]) {
+ App app;
+ return app.Run(argc, argv);
+}
diff --git a/packages/firebase_auth/example/tizen/tizen-manifest.xml b/packages/firebase_auth/example/tizen/tizen-manifest.xml
new file mode 100644
index 000000000..9cb2f547d
--- /dev/null
+++ b/packages/firebase_auth/example/tizen/tizen-manifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ ic_launcher.png
+
+
+ http://tizen.org/privilege/internet
+
+
+
diff --git a/packages/firebase_auth/lib/firebase_auth_tizen.dart b/packages/firebase_auth/lib/firebase_auth_tizen.dart
new file mode 100644
index 000000000..743772a00
--- /dev/null
+++ b/packages/firebase_auth/lib/firebase_auth_tizen.dart
@@ -0,0 +1,370 @@
+// Copyright 2021 Invertase Limited. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+// ignore_for_file: require_trailing_commas
+
+library firebase_auth_tizen;
+
+import 'dart:async';
+
+import 'package:firebase_auth_dart/firebase_auth_dart.dart' as auth_dart;
+import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'package:firebase_core_dart/firebase_core_dart.dart' as core_dart;
+import 'package:meta/meta.dart';
+
+import 'src/confirmation_result.dart';
+import 'src/firebase_auth_user.dart';
+import 'src/firebase_auth_user_credential.dart';
+import 'src/recaptcha_verifier.dart';
+import 'src/utils/desktop_utils.dart';
+
+/// A Dart only implementation of `FirebaseAuth` for managing Firebase users.
+class FirebaseAuthDesktop extends FirebaseAuthPlatform {
+ /// Entry point for the [FirebaseAuthDesktop] class.
+ FirebaseAuthDesktop({required FirebaseApp app})
+ : _app = core_dart.Firebase.app(app.name),
+ super(appInstance: app) {
+ // Create a app instance broadcast stream for both delegate listener events
+ _userChangesListeners[app.name] =
+ StreamController.broadcast();
+ _authStateChangesListeners[app.name] =
+ StreamController.broadcast();
+ _idTokenChangesListeners[app.name] =
+ StreamController.broadcast();
+
+ _delegate!.authStateChanges().map((auth_dart.User? dartUser) {
+ if (dartUser == null) {
+ return null;
+ }
+ return User(this, dartUser);
+ }).listen((User? user) {
+ _authStateChangesListeners[app.name]!.add(user);
+ });
+
+ _delegate!.idTokenChanges().map((auth_dart.User? dartUser) {
+ if (dartUser == null) {
+ return null;
+ }
+ return User(this, dartUser);
+ }).listen((User? user) {
+ _idTokenChangesListeners[app.name]!.add(user);
+ _userChangesListeners[app.name]!.add(user);
+ });
+ }
+
+ FirebaseAuthDesktop._()
+ : _app = null,
+ super(appInstance: null);
+
+ /// Called by PluginRegistry to register this plugin as the implementation for Desktop
+ static void register() {
+ FirebaseAuthPlatform.instance = FirebaseAuthDesktop.instance;
+ RecaptchaVerifierFactoryPlatform.instance =
+ RecaptchaVerifierFactoryDesktop.instance;
+ }
+
+ /// Stub initializer to allow creating an instance without
+ /// registering delegates or listeners.
+ ///
+ // ignore: prefer_constructors_over_static_methods
+ static FirebaseAuthDesktop get instance {
+ return FirebaseAuthDesktop._();
+ }
+
+ /// Instance of auth from Identity Provider API service.
+ auth_dart.FirebaseAuth? get _delegate =>
+ _app == null ? null : auth_dart.FirebaseAuth.instanceFor(app: _app!);
+
+ final core_dart.FirebaseApp? _app;
+
+ @override
+ UserPlatform? get currentUser {
+ final dartCurrentUser = _delegate!.currentUser;
+
+ if (dartCurrentUser == null) {
+ return null;
+ }
+
+ return User(this, _delegate!.currentUser!);
+ }
+
+ static final Map>
+ _userChangesListeners = >{};
+
+ static final Map>
+ _authStateChangesListeners = >{};
+
+ static final Map>
+ _idTokenChangesListeners = >{};
+
+ @override
+ FirebaseAuthPlatform delegateFor(
+ {required FirebaseApp app, Persistence? persistence}) {
+ // The persistence parameter is not used because it's only available on web
+ // based platforms.
+ return FirebaseAuthDesktop(app: app);
+ }
+
+ @override
+ FirebaseAuthPlatform setInitialValues({
+ Map? currentUser,
+ String? languageCode,
+ }) {
+ return this;
+ }
+
+ @override
+ Future signInWithEmailAndPassword(
+ String email, String password) async {
+ try {
+ return UserCredential(
+ this,
+ await _delegate!.signInWithEmailAndPassword(email, password),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future> fetchSignInMethodsForEmail(String email) async {
+ try {
+ return await _delegate!.fetchSignInMethodsForEmail(email);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future applyActionCode(String code) {
+ // TODO: implement applyActionCode
+ throw UnimplementedError();
+ }
+
+ @override
+ Future checkActionCode(String code) {
+ // TODO: implement checkActionCode
+ throw UnimplementedError();
+ }
+
+ @override
+ Future sendPasswordResetEmail(String email,
+ [ActionCodeSettings? actionCodeSettings]) async {
+ try {
+ await _delegate!.sendPasswordResetEmail(
+ email: email, continueUrl: actionCodeSettings?.url);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future confirmPasswordReset(String code, String newPassword) async {
+ try {
+ await _delegate!.confirmPasswordReset(code, newPassword);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future createUserWithEmailAndPassword(
+ String email, String password) async {
+ try {
+ return UserCredential(
+ this,
+ await _delegate!.createUserWithEmailAndPassword(email, password),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future getRedirectResult() {
+ // TODO: implement getRedirectResult
+ throw UnimplementedError();
+ }
+
+ @override
+ Stream authStateChanges() async* {
+ yield currentUser;
+ yield* _authStateChangesListeners[app.name]!.stream;
+ }
+
+ @override
+ Stream idTokenChanges() async* {
+ yield currentUser;
+ yield* _idTokenChangesListeners[app.name]!.stream;
+ }
+
+ @override
+ Stream userChanges() async* {
+ yield currentUser;
+ yield* _userChangesListeners[app.name]!.stream;
+ }
+
+ @override
+ void sendAuthChangesEvent(String appName, UserPlatform? userPlatform) {
+ assert(_userChangesListeners[appName] != null);
+
+ _userChangesListeners[appName]!.add(userPlatform);
+ }
+
+ @override
+ bool isSignInWithEmailLink(String emailLink) {
+ throw UnimplementedError();
+ }
+
+ @override
+ String? get languageCode => _delegate?.languageCode;
+
+ @override
+ Future sendSignInLinkToEmail(
+ String email, ActionCodeSettings actionCodeSettings) async {
+ // TODO: implement sendSignInLinkToEmail
+ throw UnimplementedError();
+ }
+
+ @override
+ Future setLanguageCode(String? languageCode) async {
+ return _delegate?.setLanguageCode(languageCode);
+ }
+
+ @override
+ Future setPersistence(Persistence persistence) {
+ // TODO: implement setPersistence
+ throw UnimplementedError();
+ }
+
+ @override
+ Future setSettings(
+ {bool? appVerificationDisabledForTesting,
+ String? userAccessGroup,
+ String? phoneNumber,
+ String? smsCode,
+ bool? forceRecaptchaFlow}) {
+ // TODO: implement setSettings
+ throw UnimplementedError();
+ }
+
+ @override
+ Future signInAnonymously() async {
+ try {
+ return UserCredential(
+ this,
+ await _delegate!.signInAnonymously(),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future signInWithCredential(
+ AuthCredential credential) async {
+ try {
+ return UserCredential(
+ this,
+ await _delegate!
+ .signInWithCredential(mapAuthCredentialFromPlatform(credential)),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future signInWithCustomToken(String token) async {
+ try {
+ return UserCredential(
+ this,
+ await _delegate!.signInWithCustomToken(token),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future signInWithEmailLink(
+ String email, String emailLink) async {
+ // TODO: implement signInWithEmailLink
+ throw UnimplementedError();
+ }
+
+ @override
+ Future signInWithPhoneNumber(String phoneNumber,
+ RecaptchaVerifierFactoryPlatform applicationVerifier) async {
+ try {
+ final recaptchaVerifier = applicationVerifier.delegate;
+
+ return ConfirmationResultDesktop(
+ this,
+ await _delegate!.signInWithPhoneNumber(phoneNumber, recaptchaVerifier),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future signInWithPopup(AuthProvider provider) {
+ // TODO: implement signInWithPopup
+ throw UnimplementedError();
+ }
+
+ @override
+ Future signInWithRedirect(AuthProvider provider) {
+ // TODO: implement signInWithRedirect
+ throw UnimplementedError();
+ }
+
+ @override
+ Future signOut() async {
+ try {
+ await _delegate!.signOut();
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future useAuthEmulator(String host, int port) async {
+ try {
+ await _delegate!.useAuthEmulator(host: host, port: port);
+
+ return;
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future verifyPasswordResetCode(String code) async {
+ try {
+ return await _delegate!.verifyPasswordResetCode(code);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future verifyPhoneNumber({
+ String? phoneNumber,
+ PhoneMultiFactorInfo? multiFactorInfo,
+ required PhoneVerificationCompleted verificationCompleted,
+ required PhoneVerificationFailed verificationFailed,
+ required PhoneCodeSent codeSent,
+ required PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout,
+ Duration timeout = const Duration(seconds: 30),
+ int? forceResendingToken,
+ MultiFactorSession? multiFactorSession,
+ // ignore: invalid_use_of_visible_for_testing_member
+ @visibleForTesting String? autoRetrievedSmsCodeForTesting,
+ }) {
+ throw UnimplementedError('verifyPhoneNumber() is not implemented');
+ }
+}
diff --git a/packages/firebase_auth/lib/src/confirmation_result.dart b/packages/firebase_auth/lib/src/confirmation_result.dart
new file mode 100644
index 000000000..ba1b25b3a
--- /dev/null
+++ b/packages/firebase_auth/lib/src/confirmation_result.dart
@@ -0,0 +1,28 @@
+// Copyright 2021 Invertase Limited. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+import 'package:firebase_auth_dart/firebase_auth_dart.dart' as auth_dart;
+import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
+
+import '../src/utils/desktop_utils.dart';
+import 'firebase_auth_user_credential.dart';
+
+/// The desktop delegate implementation for [ConfirmationResultPlatform].
+class ConfirmationResultDesktop extends ConfirmationResultPlatform {
+ /// Creates a new [ConfirmationResultDesktop] instance.
+ ConfirmationResultDesktop(this._auth, this._result)
+ : super(_result.verificationId);
+ final auth_dart.ConfirmationResult _result;
+
+ final FirebaseAuthPlatform _auth;
+
+ @override
+ Future confirm(String verificationCode) async {
+ try {
+ return UserCredential(_auth, await _result.confirm(verificationCode));
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+}
diff --git a/packages/firebase_auth/lib/src/firebase_auth_user.dart b/packages/firebase_auth/lib/src/firebase_auth_user.dart
new file mode 100644
index 000000000..122ebbb62
--- /dev/null
+++ b/packages/firebase_auth/lib/src/firebase_auth_user.dart
@@ -0,0 +1,208 @@
+// Copyright 2021 Invertase Limited. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+// ignore_for_file: require_trailing_commas
+
+import 'package:firebase_auth_dart/firebase_auth_dart.dart' as auth_dart;
+import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
+
+import 'confirmation_result.dart';
+import 'firebase_auth_user_credential.dart';
+import 'multi_factor.dart';
+import 'utils/desktop_utils.dart';
+
+/// Dart delegate implementation of [UserPlatform].
+class User extends UserPlatform {
+ // ignore: public_member_api_docs
+ User(FirebaseAuthPlatform auth, this._user)
+ : super(auth, MultiFactorDesktop(auth), _user.toMap());
+
+ final auth_dart.User _user;
+
+ @override
+ Future delete() async {
+ await _user.delete();
+ }
+
+ @override
+ String? get displayName => _user.displayName;
+
+ @override
+ String? get email => _user.email;
+
+ @override
+ bool get emailVerified => _user.emailVerified;
+
+ @override
+ Future getIdToken(bool forceRefresh) {
+ return _user.getIdToken(forceRefresh);
+ }
+
+ @override
+ Future getIdTokenResult(bool forceRefresh) async {
+ try {
+ final idTokenResult = await _user.getIdTokenResult(forceRefresh);
+
+ return IdTokenResult(idTokenResult.toMap);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ bool get isAnonymous => _user.isAnonymous;
+
+ @override
+ Future linkWithCredential(
+ AuthCredential credential) async {
+ try {
+ return UserCredential(
+ auth,
+ await _user
+ .linkWithCredential(mapAuthCredentialFromPlatform(credential)),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future linkWithPhoneNumber(String phoneNumber,
+ RecaptchaVerifierFactoryPlatform applicationVerifier) async {
+ try {
+ final recaptchaVerifier = applicationVerifier.delegate;
+ return ConfirmationResultDesktop(
+ auth,
+ await _user.linkWithPhoneNumber(phoneNumber, recaptchaVerifier),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future linkWithPopup(AuthProvider provider) {
+ // TODO: implement linkWithPopup
+ throw UnimplementedError();
+ }
+
+ @override
+ UserMetadata get metadata => mapUserMetadataFromDart(_user.metadata);
+
+ @override
+ String? get phoneNumber => _user.phoneNumber;
+
+ @override
+ String? get photoURL => _user.photoURL;
+
+ @override
+ List get providerData {
+ return _user.providerData.map((user) => UserInfo(user.toMap())).toList();
+ }
+
+ @override
+ Future reauthenticateWithCredential(
+ AuthCredential credential) async {
+ try {
+ return UserCredential(
+ auth,
+ await _user.reauthenticateWithCredential(
+ mapAuthCredentialFromPlatform(credential)),
+ );
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ String? get refreshToken => _user.refreshToken;
+
+ @override
+ Future reload() async {
+ try {
+ await _user.reload();
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future sendEmailVerification(
+ ActionCodeSettings? actionCodeSettings) async {
+ try {
+ await _user.sendEmailVerification();
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ String? get tenantId => _user.tenantId;
+
+ @override
+ String get uid => _user.uid;
+
+ @override
+ Future unlink(String providerId) async {
+ try {
+ return User(auth, await _user.unlink(providerId));
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future updateEmail(String newEmail) async {
+ try {
+ await _user.updateEmail(newEmail);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future updatePassword(String newPassword) async {
+ try {
+ await _user.updatePassword(newPassword);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ @override
+ Future updatePhoneNumber(PhoneAuthCredential phoneCredential) async {
+ try {
+ final credentials = mapPhoneCredentialFromPlatform(phoneCredential);
+ await _user.updatePhoneNumber(credentials);
+ } catch (e) {
+ throw getFirebaseAuthException(e);
+ }
+ }
+
+ /// Update the user name.
+ Future updateDisplayName(String? displayName) {
+ return _user.updateDisplayName(displayName);
+ }
+
+ /// Update the user's profile picture.
+ Future updatePhotoURL(String? photoURL) {
+ return _user.updatePhotoURL(photoURL);
+ }
+
+ /// Update the user's profile.
+ @override
+ Future updateProfile(Map newProfile) {
+ return _user.updateProfile(
+ displayName: newProfile['displayName'],
+ photoUrl: newProfile['photoURL'],
+ );
+ }
+
+ @override
+ Future verifyBeforeUpdateEmail(String newEmail,
+ [ActionCodeSettings? actionCodeSettings]) {
+ // TODO: implement verifyBeforeUpdateEmail
+ throw UnimplementedError();
+ }
+}
diff --git a/packages/firebase_auth/lib/src/firebase_auth_user_credential.dart b/packages/firebase_auth/lib/src/firebase_auth_user_credential.dart
new file mode 100644
index 000000000..2309233cc
--- /dev/null
+++ b/packages/firebase_auth/lib/src/firebase_auth_user_credential.dart
@@ -0,0 +1,30 @@
+// Copyright 2021 Invertase Limited. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+import 'package:firebase_auth_dart/firebase_auth_dart.dart' as auth_dart;
+import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
+
+import 'firebase_auth_user.dart';
+
+/// Dart delegate implementation of [UserCredentialPlatform].
+class UserCredential extends UserCredentialPlatform {
+ // ignore: public_member_api_docs
+ UserCredential(
+ FirebaseAuthPlatform auth,
+ auth_dart.UserCredential ipUserCredential,
+ ) : super(
+ auth: auth,
+ additionalUserInfo: AdditionalUserInfo(
+ isNewUser: ipUserCredential.additionalUserInfo!.isNewUser,
+ profile: ipUserCredential.additionalUserInfo?.profile,
+ providerId: ipUserCredential.additionalUserInfo?.providerId,
+ username: ipUserCredential.additionalUserInfo?.username,
+ ),
+ credential: AuthCredential(
+ providerId: ipUserCredential.credential!.providerId,
+ signInMethod: ipUserCredential.credential!.signInMethod,
+ ),
+ user: User(auth, ipUserCredential.user!),
+ );
+}
diff --git a/packages/firebase_auth/lib/src/multi_factor.dart b/packages/firebase_auth/lib/src/multi_factor.dart
new file mode 100644
index 000000000..b5fe14e80
--- /dev/null
+++ b/packages/firebase_auth/lib/src/multi_factor.dart
@@ -0,0 +1,7 @@
+import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
+
+/// Dart delegate implementation of [MultiFactorPlatform].
+class MultiFactorDesktop extends MultiFactorPlatform {
+ // ignore: public_member_api_docs
+ MultiFactorDesktop(FirebaseAuthPlatform auth) : super(auth);
+}
diff --git a/packages/firebase_auth/lib/src/recaptcha_verifier.dart b/packages/firebase_auth/lib/src/recaptcha_verifier.dart
new file mode 100644
index 000000000..b3425e22a
--- /dev/null
+++ b/packages/firebase_auth/lib/src/recaptcha_verifier.dart
@@ -0,0 +1,96 @@
+// Copyright 2021 Invertase Limited. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+// ignore_for_file: prefer_constructors_over_static_methods
+
+import 'package:firebase_auth_dart/firebase_auth_dart.dart' as auth_dart;
+import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
+
+import 'utils/desktop_utils.dart';
+
+const String _type = 'recaptcha';
+
+/// The delegate implementation for [RecaptchaVerifierFactoryPlatform].
+///
+/// This factory class is implemented to the user facing code has no underlying knowledge
+/// of the delegate implementation.
+class RecaptchaVerifierFactoryDesktop extends RecaptchaVerifierFactoryPlatform {
+ /// Creates a new [RecaptchaVerifierFactoryDesktop] with a container and parameters.
+ RecaptchaVerifierFactoryDesktop({
+ RecaptchaVerifierSize size = RecaptchaVerifierSize.normal,
+ RecaptchaVerifierTheme theme = RecaptchaVerifierTheme.light,
+ RecaptchaVerifierOnSuccess? onSuccess,
+ RecaptchaVerifierOnError? onError,
+ RecaptchaVerifierOnExpired? onExpired,
+ }) : super() {
+ final parameters = {};
+
+ if (onSuccess != null) {
+ parameters['callback'] = (resp) {
+ onSuccess();
+ };
+ }
+
+ if (onExpired != null) {
+ parameters['expired-callback'] = () {
+ onExpired();
+ };
+ }
+
+ if (onError != null) {
+ parameters['error-callback'] = (Object error) {
+ onError(getFirebaseAuthException(error));
+ };
+ }
+
+ parameters['size'] = size.name;
+ parameters['theme'] = theme.name;
+
+ // TODO: implement recaptcha verifirer delegate
+ }
+
+ RecaptchaVerifierFactoryDesktop._() : super();
+
+ late auth_dart.RecaptchaVerifier _delegate;
+
+ @override
+ RecaptchaVerifierFactoryPlatform delegateFor({
+ required FirebaseAuthPlatform auth,
+ String? container,
+ RecaptchaVerifierSize size = RecaptchaVerifierSize.normal,
+ RecaptchaVerifierTheme theme = RecaptchaVerifierTheme.light,
+ RecaptchaVerifierOnSuccess? onSuccess,
+ RecaptchaVerifierOnError? onError,
+ RecaptchaVerifierOnExpired? onExpired,
+ }) {
+ return RecaptchaVerifierFactoryDesktop(
+ size: size,
+ theme: theme,
+ onSuccess: onSuccess,
+ onError: onError,
+ onExpired: onExpired,
+ );
+ }
+
+ /// Returns a stub instance of the class.
+ ///
+ /// This is used during initialization of the plugin so the user-facing
+ /// code has access to the class instance without directly knowing about it.
+ ///
+ // ignore: comment_references
+ /// See the [registerWith] static method on the [FirebaseAuthDesktop] class.
+ static RecaptchaVerifierFactoryDesktop get instance =>
+ RecaptchaVerifierFactoryDesktop._();
+
+ @override
+ auth_dart.RecaptchaVerifier get delegate {
+ return _delegate;
+ }
+
+ @override
+ String get type => _type;
+
+ @override
+ void clear() {}
+}
diff --git a/packages/firebase_auth/lib/src/utils/desktop_utils.dart b/packages/firebase_auth/lib/src/utils/desktop_utils.dart
new file mode 100644
index 000000000..e3d63ce56
--- /dev/null
+++ b/packages/firebase_auth/lib/src/utils/desktop_utils.dart
@@ -0,0 +1,109 @@
+// Copyright 2021 Invertase Limited. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file.
+
+// ignore_for_file: require_trailing_commas
+
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:firebase_auth_dart/firebase_auth_dart.dart' as auth_dart;
+import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
+
+/// Map from [AuthCredential] to [auth_dart.AuthCredential].
+auth_dart.AuthCredential mapAuthCredentialFromPlatform(
+ AuthCredential credential) {
+ if (credential is EmailAuthCredential) {
+ return auth_dart.EmailAuthProvider.credential(
+ email: credential.email,
+ password: credential.password!,
+ );
+ } else if (credential is GoogleAuthCredential) {
+ return auth_dart.GoogleAuthProvider.credential(
+ idToken: credential.idToken,
+ accessToken: credential.accessToken,
+ );
+ } else if (credential is TwitterAuthCredential) {
+ return auth_dart.TwitterAuthProvider.credential(
+ secret: credential.secret!,
+ accessToken: credential.accessToken!,
+ );
+ } else if (credential is FacebookAuthCredential) {
+ return auth_dart.FacebookAuthProvider.credential(credential.accessToken!);
+ } else if (credential is GithubAuthCredential) {
+ return auth_dart.GithubAuthProvider.credential(credential.accessToken!);
+ } else if (credential is OAuthCredential) {
+ return auth_dart.OAuthProvider(credential.providerId).credential(
+ accessToken: credential.accessToken,
+ idToken: credential.idToken,
+ rawNonce: credential.rawNonce,
+ secret: credential.secret,
+ );
+ } else {
+ return auth_dart.AuthCredential(
+ providerId: credential.providerId,
+ signInMethod: credential.signInMethod,
+ );
+ }
+}
+
+/// Map from [PhoneAuthCredential] to [auth_dart.PhoneAuthCredential].
+auth_dart.PhoneAuthCredential mapPhoneCredentialFromPlatform(
+ PhoneAuthCredential credential) {
+ return auth_dart.PhoneAuthProvider.credential(
+ verificationId: credential.verificationId!,
+ smsCode: credential.smsCode!,
+ );
+}
+
+/// Map from [auth_dart.AuthCredential] to [AuthCredential].
+AuthCredential mapAuthCredentialFromDart(auth_dart.AuthCredential credential) {
+ if (credential is auth_dart.EmailAuthCredential) {
+ return EmailAuthProvider.credential(
+ email: credential.email,
+ password: credential.password!,
+ );
+ } else if (credential is auth_dart.GoogleAuthCredential) {
+ return GoogleAuthProvider.credential(
+ idToken: credential.idToken,
+ accessToken: credential.accessToken,
+ );
+ } else if (credential is auth_dart.TwitterAuthCredential) {
+ return TwitterAuthProvider.credential(
+ secret: credential.idToken!,
+ accessToken: credential.accessToken!,
+ );
+ } else if (credential is auth_dart.FacebookAuthCredential) {
+ return FacebookAuthProvider.credential(credential.accessToken!);
+ } else if (credential is auth_dart.GithubAuthCredential) {
+ return GithubAuthProvider.credential(credential.accessToken!);
+ } else if (credential is auth_dart.OAuthCredential) {
+ return OAuthProvider(credential.providerId).credential(
+ accessToken: credential.accessToken,
+ idToken: credential.idToken,
+ rawNonce: credential.rawNonce,
+ secret: credential.secret,
+ );
+ } else {
+ return AuthCredential(
+ providerId: credential.providerId,
+ signInMethod: credential.signInMethod,
+ );
+ }
+}
+
+/// Map [auth_dart.UserMetadata] to [UserMetadata].
+UserMetadata mapUserMetadataFromDart(auth_dart.UserMetadata? metadata) {
+ return UserMetadata(
+ metadata?.creationTime?.millisecondsSinceEpoch,
+ metadata?.lastSignInTime?.millisecondsSinceEpoch,
+ );
+}
+
+/// Map [auth_dart.FirebaseAuthException] to [FirebaseAuthException].
+FirebaseAuthException getFirebaseAuthException(Object e) {
+ if (e is auth_dart.FirebaseAuthException) {
+ return FirebaseAuthException(code: e.code, message: e.message);
+ } else {
+ // ignore: only_throw_errors
+ throw e;
+ }
+}
diff --git a/packages/firebase_auth/pubspec.yaml b/packages/firebase_auth/pubspec.yaml
new file mode 100644
index 000000000..731138d03
--- /dev/null
+++ b/packages/firebase_auth/pubspec.yaml
@@ -0,0 +1,32 @@
+name: firebase_auth_tizen
+description: Firebase Auth for Tizen
+version: 0.1.0
+homepage: https://github.com/flutter-tizen/plugins
+repository: https://github.com/flutter-tizen/plugins/tree/master/packages/firebase_auth
+
+environment:
+ sdk: '>=2.18.4 <3.0.0'
+ flutter: ">=2.5.0"
+
+dependencies:
+ firebase_auth: ^3.6.0
+ firebase_auth_dart: ^1.0.1
+ firebase_auth_platform_interface: ^6.5.1
+ firebase_core: ^1.20.0
+ firebase_core_dart: ^1.0.1
+ flutter:
+ sdk: flutter
+ meta: ^1.8.0
+
+dev_dependencies:
+ flutter_lints: ^2.0.0
+
+flutter:
+ plugin:
+ platforms:
+ tizen:
+ dartPluginClass: FirebaseAuthDesktop
+
+false_secrets:
+ - /example/lib/main.dart
+ - /example/lib/auth_service.dart
diff --git a/packages/firebase_core/pubspec.yaml b/packages/firebase_core/pubspec.yaml
index 1c7353daa..df0dfa540 100644
--- a/packages/firebase_core/pubspec.yaml
+++ b/packages/firebase_core/pubspec.yaml
@@ -10,7 +10,7 @@ environment:
dependencies:
firebase_core_dart: ^1.0.1
- firebase_core_platform_interface: ^4.5.2
+ firebase_core_platform_interface: 4.5.1
flutter:
sdk: flutter