├── .gitignore ├── Droid ├── Assets │ └── AboutAssets.txt ├── DroidModule.cs ├── MainActivity.cs ├── Properties │ ├── AndroidManifest.xml │ └── AssemblyInfo.cs ├── Resources │ ├── AboutResources.txt │ ├── Resource.designer.cs │ ├── drawable-hdpi │ │ └── icon.png │ ├── drawable-xhdpi │ │ └── icon.png │ ├── drawable-xxhdpi │ │ └── icon.png │ ├── drawable │ │ └── icon.png │ ├── layout │ │ ├── Tabbar.axml │ │ └── Toolbar.axml │ └── values │ │ └── styles.xml ├── Service │ └── FirebaseAuthenticator.cs ├── XFFirebaseAuthExample.Droid.csproj └── packages.config ├── LICENSE ├── README.md ├── XFFirebaseAuthExample.sln ├── XFFirebaseAuthExample ├── App.xaml ├── App.xaml.cs ├── Converters │ └── InvertBooleanConverter.cs ├── FodyWeavers.xml ├── Services │ ├── IFirebaseAuthenticator.cs │ └── NavigationService.cs ├── Validations │ ├── EmailValidator.cs │ ├── IValidationRule.cs │ ├── PasswordValidator.cs │ └── ValidatableObject.cs ├── ViewModels │ ├── BaseViewModel.cs │ ├── HomeViewModel.cs │ └── LoginViewModel.cs ├── Views │ ├── HomePage.xaml │ ├── HomePage.xaml.cs │ ├── LoginPage.xaml │ └── LoginPage.xaml.cs └── XFFirebaseAuthExample.csproj ├── XFFirebaseAuthExampleWebApi ├── Controllers │ └── DataController.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── XFFirebaseAuthExampleWebApi.csproj ├── appsettings.Development.json └── appsettings.json └── iOS ├── AppDelegate.cs ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Entitlements.plist ├── FodyWeavers.xml ├── IOSModule.cs ├── Info.plist ├── LaunchScreen.storyboard ├── Main.cs ├── Services └── FirebaseAuthenticator.cs ├── XFFirebaseAuthExample.iOS.csproj └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | # Autosave files 2 | *~ 3 | 4 | # build 5 | [Oo]bj/ 6 | [Bb]in/ 7 | packages/ 8 | TestResults/ 9 | 10 | # globs 11 | Makefile.in 12 | *.DS_Store 13 | *.sln.cache 14 | *.suo 15 | *.cache 16 | *.pidb 17 | *.userprefs 18 | *.usertasks 19 | config.log 20 | config.make 21 | config.status 22 | aclocal.m4 23 | install-sh 24 | autom4te.cache/ 25 | *.user 26 | *.tar.gz 27 | tarballs/ 28 | test-results/ 29 | Thumbs.db 30 | .vs/ 31 | 32 | # Mac bundle stuff 33 | *.dmg 34 | *.app 35 | 36 | # resharper 37 | *_Resharper.* 38 | *.Resharper 39 | 40 | # dotCover 41 | *.dotCover 42 | 43 | # Firebase configuration files 44 | Droid/google-services.json 45 | iOS/GoogleService-Info.plist -------------------------------------------------------------------------------- /Droid/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with your package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /Droid/DroidModule.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using XFFirebaseAuthExample.Droid.Service; 3 | using XFFirebaseAuthExample.Services; 4 | 5 | namespace XFFirebaseAuthExample.Droid 6 | { 7 | public class DroidModule : Module 8 | { 9 | protected override void Load(ContainerBuilder builder) 10 | { 11 | builder.RegisterType().As().SingleInstance(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Droid/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content.PM; 3 | using Android.OS; 4 | using Firebase; 5 | 6 | namespace XFFirebaseAuthExample.Droid 7 | { 8 | [Activity(Label = "XFFirebaseAuthExample.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] 9 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity 10 | { 11 | protected override void OnCreate(Bundle bundle) 12 | { 13 | TabLayoutResource = Resource.Layout.Tabbar; 14 | ToolbarResource = Resource.Layout.Toolbar; 15 | 16 | base.OnCreate(bundle); 17 | 18 | global::Xamarin.Forms.Forms.Init(this, bundle); 19 | 20 | FirebaseApp.InitializeApp(Application.Context); 21 | 22 | LoadApplication(new App(new DroidModule())); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Droid/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Droid/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using Android.App; 4 | 5 | // Information about this assembly is defined by the following attributes. 6 | // Change them to the values specific to your project. 7 | 8 | [assembly: AssemblyTitle("XFFirebaseAuthExample.Droid")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("")] 13 | [assembly: AssemblyCopyright("(c) Evgeny Zborovsky")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 18 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 19 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 20 | 21 | [assembly: AssemblyVersion("1.0.0")] 22 | 23 | // The following attributes are used to specify the signing key for the assembly, 24 | // if desired. See the Mono documentation for more information about signing. 25 | 26 | //[assembly: AssemblyDelaySign(false)] 27 | //[assembly: AssemblyKeyFile("")] 28 | -------------------------------------------------------------------------------- /Droid/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.axml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable/ 12 | icon.png 13 | 14 | layout/ 15 | main.axml 16 | 17 | values/ 18 | strings.xml 19 | 20 | In order to get the build system to recognize Android resources, set the build action to 21 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 22 | instead operate on resource IDs. When you compile an Android application that uses resources, 23 | the build system will package the resources for distribution and generate a class called "R" 24 | (this is an Android convention) that contains the tokens for each one of the resources 25 | included. For example, for the above Resources layout, this is what the R class would expose: 26 | 27 | public class R { 28 | public class drawable { 29 | public const int icon = 0x123; 30 | } 31 | 32 | public class layout { 33 | public const int main = 0x456; 34 | } 35 | 36 | public class strings { 37 | public const int first_string = 0xabc; 38 | public const int second_string = 0xbcd; 39 | } 40 | } 41 | 42 | You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main 43 | to reference the layout/main.axml file, or R.strings.first_string to reference the first 44 | string in the dictionary file values/strings.xml. 45 | -------------------------------------------------------------------------------- /Droid/Resources/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuv4ik/XFFirebaseAuthExample/c20105676e98a8d52f7c858852122cb5883b1f0d/Droid/Resources/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /Droid/Resources/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuv4ik/XFFirebaseAuthExample/c20105676e98a8d52f7c858852122cb5883b1f0d/Droid/Resources/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /Droid/Resources/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuv4ik/XFFirebaseAuthExample/c20105676e98a8d52f7c858852122cb5883b1f0d/Droid/Resources/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /Droid/Resources/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuv4ik/XFFirebaseAuthExample/c20105676e98a8d52f7c858852122cb5883b1f0d/Droid/Resources/drawable/icon.png -------------------------------------------------------------------------------- /Droid/Resources/layout/Tabbar.axml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /Droid/Resources/layout/Toolbar.axml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /Droid/Resources/values/styles.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 5 | 6 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /Droid/Service/FirebaseAuthenticator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Firebase.Auth; 4 | using XFFirebaseAuthExample.Services; 5 | 6 | namespace XFFirebaseAuthExample.Droid.Service 7 | { 8 | public class FirebaseAuthenticator : IFirebaseAuthenticator 9 | { 10 | public async Task LoginWithEmailPassword(string email, string password) 11 | { 12 | var user = await FirebaseAuth.Instance.SignInWithEmailAndPasswordAsync(email, password); 13 | var token = await user.User.GetIdTokenAsync(false); 14 | return token.Token; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Droid/XFFirebaseAuthExample.Droid.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {4374D46A-29E8-4F7E-BE66-0F0894797629} 9 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | XFFirebaseAuthExample.Droid 12 | XFFirebaseAuthExample.Droid 13 | v8.1 14 | True 15 | Resources\Resource.designer.cs 16 | Resource 17 | Properties\AndroidManifest.xml 18 | Resources 19 | Assets 20 | false 21 | 22 | 23 | true 24 | full 25 | false 26 | bin\Debug 27 | DEBUG; 28 | prompt 29 | 4 30 | None 31 | 32 | 33 | true 34 | pdbonly 35 | true 36 | bin\Release 37 | prompt 38 | 4 39 | true 40 | false 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ..\packages\Xamarin.Android.Support.Annotations.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Annotations.dll 49 | 50 | 51 | ..\packages\Xamarin.Android.Arch.Core.Common.1.0.0\lib\MonoAndroid80\Xamarin.Android.Arch.Core.Common.dll 52 | 53 | 54 | ..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.0.3\lib\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Common.dll 55 | 56 | 57 | ..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.0.3\lib\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Runtime.dll 58 | 59 | 60 | ..\packages\Xamarin.Android.Support.Compat.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Compat.dll 61 | 62 | 63 | ..\packages\Xamarin.Android.Support.Core.UI.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Core.UI.dll 64 | 65 | 66 | ..\packages\Xamarin.Android.Support.Core.Utils.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Core.Utils.dll 67 | 68 | 69 | ..\packages\Xamarin.Android.Support.Fragment.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Fragment.dll 70 | 71 | 72 | ..\packages\Xamarin.Android.Support.Media.Compat.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Media.Compat.dll 73 | 74 | 75 | ..\packages\Xamarin.Android.Support.Transition.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Transition.dll 76 | 77 | 78 | ..\packages\Xamarin.Android.Support.v4.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.v4.dll 79 | 80 | 81 | ..\packages\Xamarin.Android.Support.v7.CardView.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.v7.CardView.dll 82 | 83 | 84 | ..\packages\Xamarin.Android.Support.v7.Palette.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.v7.Palette.dll 85 | 86 | 87 | ..\packages\Xamarin.Android.Support.v7.RecyclerView.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.v7.RecyclerView.dll 88 | 89 | 90 | ..\packages\Xamarin.Android.Support.Vector.Drawable.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Vector.Drawable.dll 91 | 92 | 93 | ..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Animated.Vector.Drawable.dll 94 | 95 | 96 | ..\packages\Xamarin.Android.Support.v7.AppCompat.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.v7.AppCompat.dll 97 | 98 | 99 | ..\packages\Xamarin.Android.Support.Design.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.Design.dll 100 | 101 | 102 | ..\packages\Xamarin.Android.Support.v7.MediaRouter.27.0.2\lib\MonoAndroid81\Xamarin.Android.Support.v7.MediaRouter.dll 103 | 104 | 105 | ..\packages\Xamarin.Forms.2.5.0.280555\lib\MonoAndroid10\FormsViewGroup.dll 106 | 107 | 108 | ..\packages\Xamarin.Forms.2.5.0.280555\lib\MonoAndroid10\Xamarin.Forms.Core.dll 109 | 110 | 111 | ..\packages\Xamarin.Forms.2.5.0.280555\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll 112 | 113 | 114 | ..\packages\Xamarin.Forms.2.5.0.280555\lib\MonoAndroid10\Xamarin.Forms.Platform.dll 115 | 116 | 117 | ..\packages\Xamarin.Forms.2.5.0.280555\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll 118 | 119 | 120 | 121 | 122 | ..\packages\Autofac.4.6.2\lib\netstandard1.1\Autofac.dll 123 | 124 | 125 | ..\packages\Xamarin.GooglePlayServices.Basement.60.1142.1\lib\MonoAndroid80\Xamarin.GooglePlayServices.Basement.dll 126 | 127 | 128 | ..\packages\Xamarin.GooglePlayServices.Tasks.60.1142.1\lib\MonoAndroid80\Xamarin.GooglePlayServices.Tasks.dll 129 | 130 | 131 | ..\packages\Xamarin.Firebase.Common.60.1142.1\lib\MonoAndroid80\Xamarin.Firebase.Common.dll 132 | 133 | 134 | ..\packages\Xamarin.GooglePlayServices.Base.60.1142.1\lib\MonoAndroid80\Xamarin.GooglePlayServices.Base.dll 135 | 136 | 137 | ..\packages\Xamarin.Firebase.Auth.60.1142.1\lib\MonoAndroid80\Xamarin.Firebase.Auth.dll 138 | 139 | 140 | 141 | 142 | {77C55988-B213-4B30-9794-DEED5DF69E4C} 143 | XFFirebaseAuthExample 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /Droid/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Evgeny Zborovsky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## XFFirebaseAuthExample 2 | Is an example of Xamarin.Forms `Droid` & `iOS` application that authenticates users via `Firebase` and consuming a protected .NET Core WEB API backend with `Firebase` OAuthToken. 3 | 4 | Beside that you will also find examples of: 5 | * IOC using Autofac 6 | * PropertyChanged.Fody 7 | * Input validation 8 | * IValueConverter 9 | * Dynamically enable/disable `Command` with `ChangeCanExecute` 10 | * Styles 11 | 12 | ## Firebase 13 | 14 | ### Service Configuration 15 | 16 | * [Sign-in](https://console.firebase.google.com/u/1/) and navigate to console 17 | * Create a new project 18 | * Add application for Android 19 | * Download google-services.json 20 | * Add application for iOs 21 | * Download GoogleService-Info.plist 22 | * Open Authentication section 23 | * Switch to "Sign-in method" tab 24 | * Enable "Email/Password" 25 | * Switch to "Users" tab 26 | * Add new user 27 | 28 | ### .NET Core WEB API Configuration 29 | 30 | * In `Startup.ConfigureServices(..)` set the var `firebaseProjectId` to your firebase id project. 31 | 32 | ### Mobile App Configuration 33 | 34 | #### Droid 35 | 36 | * Add "Xamarin.Firebase.Auth" NuGet package 37 | * Import "google-services.json" and set the building action to "GoogleServicesJson" 38 | * In other words the `.csproj` should contain the next line: 39 | * Make sure that your package name is identical to the package name inside "google-services.json" 40 | * Add the next line of code in the MainActivity.cs OnCreate before LoadApplication: 41 | ```C# 42 | FirebaseApp.InitializeApp(Application.Context); 43 | ``` 44 | * Implement IFirebaseAuthenticator 45 | 46 | #### iOS 47 | * Add "Xamarin.Firebase.iOS.Auth" NuGet package to iOS project 48 | * Import "GoogleService-Info.plist" and set the building action to "BundleResource" 49 | * Make sure that your bundle identifier is identical to the bundle identifier inside "GoogleService-Info.plist" 50 | * Add the next line of code in the AppDelegate.cs FinishedLaunching before LoadApplication: 51 | ```C# 52 | Firebase.Core.App.Configure(); 53 | ``` 54 | * Implement IFirebaseAuthenticator 55 | * Please note that if you want to test on the iOS simulator you will have to do one extra step as described [here](https://stackoverflow.com/a/39488013/1970317) due to a bug. 56 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XFFirebaseAuthExample", "XFFirebaseAuthExample\XFFirebaseAuthExample.csproj", "{77C55988-B213-4B30-9794-DEED5DF69E4C}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XFFirebaseAuthExample.iOS", "iOS\XFFirebaseAuthExample.iOS.csproj", "{5FD37254-03BC-4410-8A0B-71FA74E9239D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XFFirebaseAuthExample.Droid", "Droid\XFFirebaseAuthExample.Droid.csproj", "{4374D46A-29E8-4F7E-BE66-0F0894797629}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XFFirebaseAuthExampleWebApi", "XFFirebaseAuthExampleWebApi\XFFirebaseAuthExampleWebApi.csproj", "{EBD193E3-D4CB-4906-8A6B-03D16EF671A1}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | Debug|iPhoneSimulator = Debug|iPhoneSimulator 17 | Release|iPhone = Release|iPhone 18 | Release|iPhoneSimulator = Release|iPhoneSimulator 19 | Debug|iPhone = Debug|iPhone 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 27 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 28 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Release|iPhone.ActiveCfg = Release|Any CPU 29 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Release|iPhone.Build.0 = Release|Any CPU 30 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 31 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 32 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Debug|iPhone.ActiveCfg = Debug|Any CPU 33 | {77C55988-B213-4B30-9794-DEED5DF69E4C}.Debug|iPhone.Build.0 = Debug|Any CPU 34 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator 35 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator 36 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Release|Any CPU.ActiveCfg = Release|iPhone 37 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Release|Any CPU.Build.0 = Release|iPhone 38 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator 39 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator 40 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Release|iPhone.ActiveCfg = Release|iPhone 41 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Release|iPhone.Build.0 = Release|iPhone 42 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator 43 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator 44 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Debug|iPhone.ActiveCfg = Debug|iPhone 45 | {5FD37254-03BC-4410-8A0B-71FA74E9239D}.Debug|iPhone.Build.0 = Debug|iPhone 46 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 51 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 52 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Release|iPhone.ActiveCfg = Release|Any CPU 53 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Release|iPhone.Build.0 = Release|Any CPU 54 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 55 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 56 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Debug|iPhone.ActiveCfg = Debug|Any CPU 57 | {4374D46A-29E8-4F7E-BE66-0F0894797629}.Debug|iPhone.Build.0 = Debug|Any CPU 58 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 63 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 64 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Release|iPhone.ActiveCfg = Release|Any CPU 65 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Release|iPhone.Build.0 = Release|Any CPU 66 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 67 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 68 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Debug|iPhone.ActiveCfg = Debug|Any CPU 69 | {EBD193E3-D4CB-4906-8A6B-03D16EF671A1}.Debug|iPhone.Build.0 = Debug|Any CPU 70 | EndGlobalSection 71 | EndGlobal 72 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/App.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Xamarin.Forms; 3 | using XFFirebaseAuthExample.Services; 4 | using XFFirebaseAuthExample.ViewModels; 5 | using XFFirebaseAuthExample.Views; 6 | 7 | namespace XFFirebaseAuthExample 8 | { 9 | public partial class App : Application 10 | { 11 | public IContainer Container { get; } 12 | public string AuthToken { get; set; } 13 | 14 | public App(Module module) 15 | { 16 | InitializeComponent(); 17 | Container = BuildContainer(module); 18 | MainPage = new NavigationPage(new LoginPage()); 19 | } 20 | 21 | protected override void OnStart() 22 | { 23 | // Handle when your app starts 24 | } 25 | 26 | protected override void OnSleep() 27 | { 28 | // Handle when your app sleeps 29 | } 30 | 31 | protected override void OnResume() 32 | { 33 | // Handle when your app resumes 34 | } 35 | 36 | IContainer BuildContainer(Module module) 37 | { 38 | var builder = new ContainerBuilder(); 39 | builder.RegisterType().AsSelf(); 40 | builder.RegisterType().AsSelf(); 41 | builder.RegisterType().AsSelf().SingleInstance(); 42 | builder.RegisterModule(module); 43 | return builder.Build(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Converters/InvertBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Xamarin.Forms; 4 | 5 | namespace XFFirebaseAuthExample.Converters 6 | { 7 | public class InvertBooleanConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => 10 | Invert((bool)value); 11 | 12 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => 13 | Invert((bool)value); 14 | 15 | bool Invert(bool val) => !val; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Services/IFirebaseAuthenticator.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace XFFirebaseAuthExample.Services 4 | { 5 | public interface IFirebaseAuthenticator 6 | { 7 | /// 8 | /// Login with email and password. 9 | /// 10 | /// OAuth token 11 | /// Email 12 | /// Password 13 | Task LoginWithEmailPassword(string email, string password); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Services/NavigationService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xamarin.Forms; 3 | 4 | namespace XFFirebaseAuthExample.Services 5 | { 6 | public class NavigationService 7 | { 8 | public Task PushAsync(Page page) => GetCurrentNavigation().PushAsync(page); 9 | INavigation GetCurrentNavigation() => (Application.Current as App).MainPage.Navigation; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Validations/EmailValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace XFFirebaseAuthExample 4 | { 5 | public class EmailValidator : IValidationRule 6 | { 7 | const string pattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|" + @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(? "Please enter a valid email."; 9 | 10 | public bool Validate(string value) 11 | { 12 | if (string.IsNullOrEmpty(value)) return false; 13 | var regex = new Regex(pattern, RegexOptions.IgnoreCase); 14 | return regex.IsMatch(value); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Validations/IValidationRule.cs: -------------------------------------------------------------------------------- 1 | namespace XFFirebaseAuthExample 2 | { 3 | public interface IValidationRule 4 | { 5 | string Description { get; } 6 | bool Validate(T value); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Validations/PasswordValidator.cs: -------------------------------------------------------------------------------- 1 | using XFFirebaseAuthExample.ViewModels; 2 | 3 | namespace XFFirebaseAuthExample 4 | { 5 | public class PasswordValidator : IValidationRule 6 | { 7 | const int minLength = 6; 8 | public string Description => $"Password should at least {minLength} characters long."; 9 | public bool Validate(string value) => !string.IsNullOrEmpty(value) && value.Length >= minLength; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Validations/ValidatableObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using PropertyChanged; 5 | using XFFirebaseAuthExample.ViewModels; 6 | 7 | namespace XFFirebaseAuthExample 8 | { 9 | public class ValidatableObject : BaseViewModel 10 | { 11 | public List> Validations { get; } = new List>(); 12 | public T Value { get; set; } 13 | 14 | void OnValueChanged() => propertyChangedCallback?.Invoke(); 15 | 16 | readonly Action propertyChangedCallback; 17 | 18 | public ValidatableObject(Action propertyChangedCallback = null, params IValidationRule[] validations) 19 | { 20 | this.propertyChangedCallback = propertyChangedCallback; 21 | foreach (var val in validations) 22 | Validations.Add(val); 23 | } 24 | 25 | [DependsOn(nameof(Value))] 26 | public bool IsValid => Validations.TrueForAll(v => v.Validate(Value)); 27 | 28 | public string ValidationDescriptions => string.Join(Environment.NewLine, Validations.Select(v => v.Description)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/ViewModels/BaseViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace XFFirebaseAuthExample.ViewModels 4 | { 5 | // PropertyChanged.Fody will take care of boiler plate code ralted to INotifyPropertyChanged 6 | public class BaseViewModel : INotifyPropertyChanged 7 | { 8 | public event PropertyChangedEventHandler PropertyChanged; 9 | public bool IsBusy { get; protected set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/ViewModels/HomeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Net.Http.Headers; 4 | using System.Threading.Tasks; 5 | using System.Windows.Input; 6 | using Xamarin.Forms; 7 | 8 | namespace XFFirebaseAuthExample.ViewModels 9 | { 10 | public class HomeViewModel : BaseViewModel 11 | { 12 | public ICommand MakeApiRequestCmd { get; } 13 | 14 | public string RemoteData { get; private set; } 15 | 16 | Action propChangedCallBack => (MakeApiRequestCmd as Command).ChangeCanExecute; 17 | 18 | public HomeViewModel() 19 | { 20 | MakeApiRequestCmd = new Command(async (authorized) => await MakeApiRequest(authorized), _ => !IsBusy); 21 | } 22 | 23 | // Without provided token a 401 will be returned 24 | async Task MakeApiRequest(bool authorized) 25 | { 26 | try 27 | { 28 | IsBusy = true; 29 | propChangedCallBack(); 30 | 31 | RemoteData = string.Empty; 32 | 33 | using (var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(3) }) 34 | { 35 | if (authorized) 36 | { 37 | var authValue = new AuthenticationHeaderValue("Bearer", (Application.Current as App).AuthToken); 38 | httpClient.DefaultRequestHeaders.Authorization = authValue; 39 | } 40 | 41 | var apiUrl = GetApiUrl(); 42 | var res = await httpClient.GetAsync($"{apiUrl}:5001/api/data"); 43 | var content = await res.Content.ReadAsStringAsync(); 44 | 45 | RemoteData = $"StatusCode: {res.StatusCode}, Content: {content}"; 46 | } 47 | } 48 | catch(Exception ex) 49 | { 50 | RemoteData = $"Exception: {ex.Message}"; 51 | } 52 | finally 53 | { 54 | IsBusy = false; 55 | propChangedCallBack(); 56 | } 57 | 58 | } 59 | 60 | // https://smellyc0de.wordpress.com/2018/03/03/how-to-access-localhost-from-android-emulator-and-ios-simulator/ 61 | string GetApiUrl() 62 | { 63 | string apiUrl = null; 64 | if (Device.RuntimePlatform == Device.Android) 65 | return "http://10.0.2.2"; 66 | else if (Device.RuntimePlatform == Device.iOS) 67 | return "http://localhost"; 68 | else throw new NotSupportedException("Platform is not supported."); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/ViewModels/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Windows.Input; 4 | using Xamarin.Forms; 5 | using XFFirebaseAuthExample.Services; 6 | using XFFirebaseAuthExample.Views; 7 | 8 | namespace XFFirebaseAuthExample.ViewModels 9 | { 10 | public class LoginViewModel : BaseViewModel 11 | { 12 | public ValidatableObject Email { get; } 13 | public ValidatableObject Password { get; } 14 | public ICommand LoginCmd { get; } 15 | 16 | readonly IFirebaseAuthenticator firebaseAuthenticator; 17 | readonly NavigationService navigationService; 18 | 19 | Action propChangedCallBack => (LoginCmd as Command).ChangeCanExecute; 20 | 21 | public LoginViewModel( 22 | IFirebaseAuthenticator firebaseAuthenticator, 23 | NavigationService navigationService) 24 | { 25 | this.firebaseAuthenticator = firebaseAuthenticator; 26 | this.navigationService = navigationService; 27 | 28 | LoginCmd = new Command(async () => await Login(), () => Email.IsValid && Password.IsValid && !IsBusy); 29 | 30 | Email = new ValidatableObject(propChangedCallBack, new EmailValidator()) { Value = "myuser@service.com" }; 31 | Password = new ValidatableObject(propChangedCallBack, new PasswordValidator()) { Value = "Qwerty123" }; 32 | } 33 | 34 | async Task Login() 35 | { 36 | IsBusy = true; 37 | propChangedCallBack(); 38 | (Application.Current as App).AuthToken = await firebaseAuthenticator.LoginWithEmailPassword(Email.Value, Password.Value); 39 | await navigationService.PushAsync(new HomePage()); 40 | IsBusy = false; 41 | propChangedCallBack(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /XFFirebaseAuthExample/Views/HomePage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | True 10 | False 11 | 12 | 13 | 17 | 20 |