├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── python-publish.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── demo ├── bin │ └── glogin__armeabi-v7a-0.1-armeabi-v7a-debug.apk ├── buildozer.spec ├── demo.gif ├── intents.xml ├── kivyauth_desktop_alpha.gif ├── kivyauth_logo.png ├── main.py └── splash.jpg ├── docs ├── integrate-firebase-auth.md ├── integrate-google-facebook-login.md └── prerequisites.md ├── kivyauth ├── __init__.py ├── android │ ├── __init__.py │ ├── facebook_auth.py │ ├── firebase_auth.py │ ├── github_auth.py │ ├── google_auth.py │ └── twitter_auth.py ├── desktop │ ├── __init__.py │ ├── facebook_auth.py │ ├── github_auth.py │ ├── google_auth.py │ ├── twitter_auth.py │ └── utils.py ├── facebook_auth.py ├── github_auth.py ├── google_auth.py ├── twitter_auth.py └── utils.py └── setup.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | 29 | **Smartphone (please complete the following information):** 30 | - Device: [e.g. Samsung M30] 31 | - OS: [e.g. Android 10] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install build 33 | - name: Build package 34 | run: python -m build 35 | - name: Publish package 36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | .buildozer 113 | .vscode 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | cache: pip 2 | matrix: 3 | fast_finish: true 4 | include: 5 | - name: Black 6 | env: RUN=black 7 | language: python 8 | python: 3.7 9 | os: linux 10 | dist: bionic 11 | install: 12 | - pip install pycodestyle 13 | script: 14 | - pycodestyle kivyauth/android/__init__.py 15 | after_failure: 16 | - sleep 10; 17 | - echo == End == 18 | deploy: 19 | skip_existing: true 20 | provider: pypi 21 | user: __token__ 22 | password: 23 | secure: HDX4vTR4+shTPAc2+SPOE5Z+/5eZkvlX0JtDY+UniEbdSUougWxS1Z+TxWSlQQyL1LE0wB3Wxt1EimBYPDgKu+AiIhSXkjonGvnD4txXTV1qolDOudNzWndu+yPc2AnZi8eWaRGE+s7cJjWVeW7ddCZGfCS2WycSLz3MtGXJfPOz4bB+UhAWc+uAj8VBu72ZeyU8QFRvYgCbeHVAhFcx4U1t6gju601/n3eoylJsUb4Dx/ssCARMplHjW81MvGCX7jbuBadB/mOoo7FdQ42PCzPtDIxeyORR8T6gKcWWga+IWswf8bWau//ry/walbWWqegIam7XeXQK8a1nDMqGGJ0Sn4J5jpJS8wVnmK6Dtw6VALtmPTUd+S7p1pnaDCvEF79zv33qowrVbe4hgHPsX+RWHVs1Vkv2MJ+WtUoDuQizeK7pi+DIpDkHXSZCZRSd2m1pDEz51DjDhKJVJfKmuypIaf2yRtiNpnJgtqfSC9Hk+ErcZW9C311oyFzHIvTlYjlQfepfl3cUImwBoj6FTIUPovLbnn6eXh6k45GltBLG5mOt8nWpFvhHDNf+kcCotyqxTpS46a5p0p4rpsURELSXLsCUely1g1xqEkXLWVeu793qBZweyahiAp9iH+gx+za8xjip5Rty5uUxITuPbv1ITU6nEshQKvbrNfwwALA= 24 | distributions: sdist bdist_wheel 25 | on: 26 | tags: true 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Shashi Ranjan 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 |

2 | 3 | 4 | # KivyAuth 5 | #### *Integrate Google, Facebook, Github & Twitter login in kivy applications* 6 | [![build](https://travis-ci.org/shashi278/social-auth-kivy.svg?branch=master)](https://travis-ci.org/github/shashi278/social-auth-kivy/) [![Python 3.6](https://img.shields.io/pypi/pyversions/kivymd)](https://www.python.org/downloads/release/python-360/) [![pypi](https://img.shields.io/pypi/v/kivyauth)](https://pypi.org/project/KivyAuth/) [![license](https://img.shields.io/pypi/l/kivyauth)](https://github.com/shashi278/social-auth-kivy/blob/master/LICENSE) [![format](https://img.shields.io/pypi/format/kivyauth)](https://pypi.org/project/KivyAuth/#modal-close) [![downloads](https://img.shields.io/pypi/dm/kivyauth)](https://pypi.org/project/KivyAuth/) [![code size](https://img.shields.io/github/languages/code-size/shashi278/social-auth-kivy)]() [![repo size](https://img.shields.io/github/repo-size/shashi278/social-auth-kivy)]() 7 | 8 | ### KivyAuth on Android 9 | ![Demo Gif](https://raw.githubusercontent.com/shashi278/social-auth-kivy/master/demo/demo.gif) 10 | 11 | Get it on Playstore 12 | 13 | ## 14 | ### KivyAuth on Desktop 15 | ![Desktop_demo_test gif](https://raw.githubusercontent.com/shashi278/social-auth-kivy/cross-platform/demo/kivyauth_desktop_alpha.gif) 16 | 17 | 18 | ### Run [demo](demo/) app on desktop: 19 | * **Make Sure you've created OAuth apps and have their CLIENT_ID and CLIENT_SECRET handy before running demo application** 20 | * Create an .env file in the app directory with below format: 21 | ```properties 22 | GOOGLE_CLIENT_ID= 23 | GOOGLE_CLIENT_SECRET= 24 | 25 | FACEBOOK_CLIENT_ID= 26 | FACEBOOK_CLIENT_SECRET= 27 | 28 | GITHUB_CLIENT_ID= 29 | GITHUB_CLIENT_SECRET= 30 | 31 | ``` 32 | 33 | ## 34 | ## How to use 35 | 36 | ### Instruction for using KivyAuth on Desktop: 37 | * pip install kivyauth==2.3.3 38 | 39 | ### Note for android: 40 | Make sure you go through the [prerequisites](https://github.com/shashi278/social-auth-kivy/blob/master/docs/prerequisites.md) 41 | for the login methods you're going to integrate in your application before moving further 42 | 43 | # 44 | The example below shows integrating google login. Similarly other login methods can also be used. 45 | 46 | * Include necessary imports for google login 47 | ```python 48 | from kivyauth.google_auth import initialize_google, login_google, logout_google 49 | ``` 50 | 51 | * Initialize google login inside your app's build method 52 | ```python 53 | def build(self): 54 | initialize_google(self.after_login, self.error_listener) 55 | ``` 56 | `after_login` is a function to be called upon successful login with `name`, `email`, and `photo url` of the user. So, create a success listener function which accepts three parameters and perform after-login stuffs(like updating UI, etc.). `error_listener` is called in case of any error and it doesn't accept any argument. 57 | 58 | * You can also add auto-login( if the user is already logged in then directly move to after-login stuff) inside app's `on_start` method as below(mention only login providers you are using in your app): 59 | ```python 60 | def on_start(self): 61 | 62 | if auto_login(login_providers.google): 63 | self.current_provider = login_providers.google 64 | elif auto_login(login_providers.facebook): 65 | self.current_provider = login_providers.facebook 66 | elif auto_login(login_providers.github): 67 | self.current_provider = login_providers.github 68 | elif auto_login(login_providers.twitter): 69 | self.current_provider = login_providers.twitter 70 | ``` 71 | 72 | * Next, call `login_google()` upon a button click to initiate login process. 73 | 74 | * Similarly, to logout, call `logout_google` as 75 | ```python 76 | logout_google(self.after_logout) 77 | ``` 78 | `after_logout` is a function to be called after user gets logged out. For example, to update UI. 79 | 80 | * Make sure to include `kivyauth` as a requirement in the buildozer.spec file 81 | ```spec 82 | requirements = python3,kivy,kivyauth==2.3.3 83 | ``` 84 | 85 | 86 | ## 87 | ### TODO: 88 | * Support iOS 89 | 90 | ## 91 | ### Changelog 92 | #### v2.3.3 93 | * Fixed werkzeug server not shutting down 94 | 95 | 96 | #### v2.3.2 97 | * Fixed crashing when user doesn't have a photo 98 | 99 | 100 | #### v2.3.1 - KivyAuth cross-platform 101 | * Kivyauth APIs are now platform-independent 102 | * Desktop support for linux, win and possibly mac 103 | 104 | 105 | #### v2.3 - KivyAuth cross-platform 106 | * Desktop support added(in alpha) 107 | * All android auths are inside `kivyauth.android` while those for desktops are inside `kivyauth.desktop` 108 | 109 | 110 | #### v2.2 111 | * Added Auto-login feature 112 | * `login_providers` are now inside `kivyauth` rather than `kivyauth.providers` 113 | 114 | 115 | #### v2.0 116 | * Individual login providers are moved into respective folders 117 | * Fix problem of not being able to use individual login methods 118 | * Now it's relatively easier to use the library 119 | 120 | ### Other 121 | ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square) 122 | 123 | **Contributing**: Contributions are more than welcome. Looking for contributions in making it cross-platform(specifically for iOS) and better documentation. 124 | 125 | 126 | Feel free to ping me or raise an issue if you want to talk about this project or Kivy in general. 127 | -------------------------------------------------------------------------------- /demo/bin/glogin__armeabi-v7a-0.1-armeabi-v7a-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashi278/social-auth-kivy/0dee71933953ba1e1f45d5e6f255936e78aa889f/demo/bin/glogin__armeabi-v7a-0.1-armeabi-v7a-debug.apk -------------------------------------------------------------------------------- /demo/buildozer.spec: -------------------------------------------------------------------------------- 1 | [app] 2 | 3 | # (str) Title of your application 4 | title = KivyAuth Demo 5 | 6 | # (str) Package name 7 | package.name = glogin 8 | 9 | # (str) Package domain (needed for android/ios packaging) 10 | package.domain = com.watney 11 | 12 | # (str) Source code where the main.py live 13 | source.dir = . 14 | 15 | # (list) Source files to include (let empty to include all the files) 16 | source.include_exts = py,png,jpg,kv,atlas,json 17 | 18 | # (list) List of inclusions using pattern matching 19 | #source.include_patterns = assets/*,images/*.png 20 | 21 | # (list) Source files to exclude (let empty to not exclude anything) 22 | #source.exclude_exts = spec 23 | 24 | # (list) List of directory to exclude (let empty to not exclude anything) 25 | #source.exclude_dirs = tests, bin 26 | 27 | # (list) List of exclusions using pattern matching 28 | #source.exclude_patterns = license,images/*/*.jpg 29 | 30 | # (str) Application versioning (method 1) 31 | version = 0.1 32 | 33 | # (str) Application versioning (method 2) 34 | # version.regex = __version__ = ['"](.*)['"] 35 | # version.filename = %(source.dir)s/main.py 36 | 37 | # (list) Application requirements 38 | # comma separated e.g. requirements = sqlite3,kivy 39 | requirements = python3,kivy,pyjnius,kivymd,certifi,kivyauth 40 | 41 | # (str) Custom source folders for requirements 42 | # Sets custom source for any requirements with recipes 43 | # requirements.source.kivyauthzz = "../kivyauth" 44 | 45 | # (list) Garden requirements 46 | #garden_requirements = 47 | 48 | # (str) Presplash of the application 49 | presplash.filename = splash.jpg 50 | 51 | # (str) Icon of the application 52 | icon.filename = kivyauth_logo.png 53 | 54 | # (str) Supported orientation (one of landscape, sensorLandscape, portrait or all) 55 | orientation = portrait 56 | 57 | # (list) List of service to declare 58 | #services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY 59 | 60 | # 61 | # OSX Specific 62 | # 63 | 64 | # 65 | # author = © Copyright Info 66 | 67 | # change the major version of python used by the app 68 | osx.python_version = 3 69 | 70 | # Kivy version to use 71 | osx.kivy_version = 1.9.1 72 | 73 | # 74 | # Android specific 75 | # 76 | 77 | # (bool) Indicate if the application should be fullscreen or not 78 | fullscreen = 0 79 | 80 | # (string) Presplash background color (for new android toolchain) 81 | # Supported formats are: #RRGGBB #AARRGGBB or one of the following names: 82 | # red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray, 83 | # darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy, 84 | # olive, purple, silver, teal. 85 | #android.presplash_color = #FFFFFF 86 | 87 | # (list) Permissions 88 | android.permissions = INTERNET, ACCESS_NETWORK_STATE 89 | 90 | # (int) Target Android API, should be as high as possible. 91 | android.api = 29 92 | 93 | # (int) Minimum API your APK will support. 94 | android.minapi = 27 95 | 96 | # (int) Android SDK version to use 97 | #android.sdk = 20 98 | 99 | # (str) Android NDK version to use 100 | #android.ndk = 19c 101 | 102 | # (int) Android NDK API to use. This is the minimum API your app will support, it should usually match android.minapi. 103 | #android.ndk_api = 21 104 | 105 | # (bool) Use --private data storage (True) or --dir public storage (False) 106 | #android.private_storage = True 107 | 108 | # (str) Android NDK directory (if empty, it will be automatically downloaded.) 109 | #android.ndk_path = 110 | 111 | # (str) Android SDK directory (if empty, it will be automatically downloaded.) 112 | #android.sdk_path = 113 | 114 | # (str) ANT directory (if empty, it will be automatically downloaded.) 115 | #android.ant_path = 116 | 117 | # (bool) If True, then skip trying to update the Android sdk 118 | # This can be useful to avoid excess Internet downloads or save time 119 | # when an update is due and you just want to test/build your package 120 | # android.skip_update = False 121 | 122 | # (bool) If True, then automatically accept SDK license 123 | # agreements. This is intended for automation only. If set to False, 124 | # the default, you will be shown the license when first running 125 | # buildozer. 126 | # android.accept_sdk_license = False 127 | 128 | # (str) Android entry point, default is ok for Kivy-based app 129 | #android.entrypoint = org.renpy.android.PythonActivity 130 | 131 | # (str) Android app theme, default is ok for Kivy-based app 132 | # android.apptheme = "@android:style/Theme.NoTitleBar" 133 | 134 | # (list) Pattern to whitelist for the whole project 135 | #android.whitelist = 136 | 137 | # (str) Path to a custom whitelist file 138 | #android.whitelist_src = 139 | 140 | # (str) Path to a custom blacklist file 141 | #android.blacklist_src = 142 | 143 | # (list) List of Java .jar files to add to the libs so that pyjnius can access 144 | # their classes. Don't add jars that you do not need, since extra jars can slow 145 | # down the build process. Allows wildcards matching, for example: 146 | # OUYA-ODK/libs/*.jar 147 | #android.add_jars = foo.jar,bar.jar,path/to/more/*.jar 148 | 149 | # (list) List of Java files to add to the android project (can be java or a 150 | # directory containing the files) 151 | #android.add_src = 152 | 153 | # (list) Android AAR archives to add (currently works only with sdl2_gradle 154 | # bootstrap) 155 | #android.add_aars = 156 | 157 | # (list) Gradle dependencies to add (currently works only with sdl2_gradle 158 | # bootstrap) 159 | android.gradle_dependencies = com.facebook.android:facebook-login:8.1.0, 160 | com.google.android.gms:play-services-auth:18.0.0, 161 | com.google.firebase:firebase-auth:19.3.1 162 | 163 | # (list) add java compile options 164 | # this can for example be necessary when importing certain java libraries using the 'android.gradle_dependencies' option 165 | # see https://developer.android.com/studio/write/java8-support for further information 166 | # android.add_compile_options = "sourceCompatibility = 1.8", "targetCompatibility = 1.8" 167 | 168 | # (list) Gradle repositories to add {can be necessary for some android.gradle_dependencies} 169 | # please enclose in double quotes 170 | # e.g. android.gradle_repositories = "maven { url 'https://kotlin.bintray.com/ktor' }" 171 | android.add_gradle_repositories = "mavenCentral()" 172 | 173 | # (list) packaging options to add 174 | # see https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.PackagingOptions.html 175 | # can be necessary to solve conflicts in gradle_dependencies 176 | # please enclose in double quotes 177 | # e.g. android.add_packaging_options = "exclude 'META-INF/common.kotlin_module'", "exclude 'META-INF/*.kotlin_module'" 178 | #android.add_packaging_options = 179 | 180 | # (list) Java classes to add as activities to the manifest. 181 | android.add_activites = com.facebook.FacebookActivity, com.facebook.CustomTabActivity 182 | 183 | # (str) OUYA Console category. Should be one of GAME or APP 184 | # If you leave this blank, OUYA support will not be enabled 185 | #android.ouya.category = GAME 186 | 187 | # (str) Filename of OUYA Console icon. It mu++st be a 732x412 png image. 188 | #android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png 189 | 190 | # (str) XML file to include as an intent filters in tag 191 | android.manifest.intent_filters = intents.xml 192 | 193 | # (str) launchMode to set for the main activity 194 | #android.manifest.launch_mode = standard 195 | 196 | # (list) Android additional libraries to copy into libs/armeabi 197 | #android.add_libs_armeabi = libs/android/*.so 198 | #android.add_libs_armeabi_v7a = libs/android-v7/*.so 199 | #android.add_libs_arm64_v8a = libs/android-v8/*.so 200 | #android.add_libs_x86 = libs/android-x86/*.so 201 | #android.add_libs_mips = libs/android-mips/*.so 202 | 203 | # (bool) Indicate whether the screen should stay on 204 | # Don't forget to add the WAKE_LOCK permission if you set this to True 205 | #android.wakelock = False 206 | 207 | # (list) Android application meta-data to set (key=value format) 208 | android.meta_data = com.facebook.sdk.ApplicationId=fb439926446854840 209 | 210 | # (list) Android library project to add (will be added in the 211 | # project.properties automatically.) 212 | #android.library_references = 213 | 214 | # (list) Android shared libraries which will be added to AndroidManifest.xml using tag 215 | #android.uses_library = 216 | 217 | # (str) Android logcat filters to use 218 | #android.logcat_filters = *:S python:D 219 | 220 | # (bool) Copy library instead of making a libpymodules.so 221 | #android.copy_libs = 1 222 | 223 | # (str) The Android arch to build for, choices: armeabi-v7a, arm64-v8a, x86, x86_64 224 | android.arch = armeabi-v7a 225 | 226 | # 227 | # Python for android (p4a) specific 228 | # 229 | 230 | # (str) python-for-android fork to use, defaults to upstream (kivy) 231 | #p4a.fork = kivy 232 | 233 | # (str) python-for-android branch to use, defaults to master 234 | #p4a.branch = master 235 | 236 | # (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) 237 | #p4a.source_dir = 238 | 239 | # (str) The directory in which python-for-android should look for your own build recipes (if any) 240 | #p4a.local_recipes = 241 | 242 | # (str) Filename to the hook for p4a 243 | #p4a.hook = 244 | 245 | # (str) Bootstrap to use for android builds 246 | # p4a.bootstrap = sdl2 247 | 248 | # (int) port number to specify an explicit --port= p4a argument (eg for bootstrap flask) 249 | #p4a.port = 250 | 251 | 252 | # 253 | # iOS specific 254 | # 255 | 256 | # (str) Path to a custom kivy-ios folder 257 | #ios.kivy_ios_dir = ../kivy-ios 258 | # Alternately, specify the URL and branch of a git checkout: 259 | ios.kivy_ios_url = https://github.com/kivy/kivy-ios 260 | ios.kivy_ios_branch = master 261 | 262 | # Another platform dependency: ios-deploy 263 | # Uncomment to use a custom checkout 264 | #ios.ios_deploy_dir = ../ios_deploy 265 | # Or specify URL and branch 266 | ios.ios_deploy_url = https://github.com/phonegap/ios-deploy 267 | ios.ios_deploy_branch = 1.7.0 268 | 269 | # (str) Name of the certificate to use for signing the debug version 270 | # Get a list of available identities: buildozer ios list_identities 271 | #ios.codesign.debug = "iPhone Developer: ()" 272 | 273 | # (str) Name of the certificate to use for signing the release version 274 | #ios.codesign.release = %(ios.codesign.debug)s 275 | 276 | 277 | [buildozer] 278 | 279 | # (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) 280 | log_level = 2 281 | 282 | # (int) Display warning if buildozer is run as root (0 = False, 1 = True) 283 | warn_on_root = 1 284 | 285 | # (str) Path to build artifact storage, absolute or relative to spec file 286 | # build_dir = ./.buildozer 287 | 288 | # (str) Path to build output (i.e. .apk, .ipa) storage 289 | # bin_dir = ./bin 290 | 291 | # ----------------------------------------------------------------------------- 292 | # List as sections 293 | # 294 | # You can define all the "list" as [section:key]. 295 | # Each line will be considered as a option to the list. 296 | # Let's take [app] / source.exclude_patterns. 297 | # Instead of doing: 298 | # 299 | #[app] 300 | #source.exclude_patterns = license,data/audio/*.wav,data/images/original/* 301 | # 302 | # This can be translated into: 303 | # 304 | #[app:source.exclude_patterns] 305 | #license 306 | #data/audio/*.wav 307 | #data/images/original/* 308 | # 309 | 310 | 311 | # ----------------------------------------------------------------------------- 312 | # Profiles 313 | # 314 | # You can extend section / key with a profile 315 | # For example, you want to deploy a demo version of your application without 316 | # HD content. You could first change the title to add "(demo)" in the name 317 | # and extend the excluded directories to remove the HD content. 318 | # 319 | #[app@demo] 320 | #title = My Application (demo) 321 | # 322 | #[app:source.exclude_patterns@demo] 323 | #images/hd/* 324 | # 325 | # Then, invoke the command line with the "demo" profile: 326 | # 327 | #buildozer --profile demo android debug 328 | -------------------------------------------------------------------------------- /demo/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashi278/social-auth-kivy/0dee71933953ba1e1f45d5e6f255936e78aa889f/demo/demo.gif -------------------------------------------------------------------------------- /demo/intents.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/kivyauth_desktop_alpha.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashi278/social-auth-kivy/0dee71933953ba1e1f45d5e6f255936e78aa889f/demo/kivyauth_desktop_alpha.gif -------------------------------------------------------------------------------- /demo/kivyauth_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashi278/social-auth-kivy/0dee71933953ba1e1f45d5e6f255936e78aa889f/demo/kivyauth_logo.png -------------------------------------------------------------------------------- /demo/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | from kivy.lang.builder import Builder 5 | from kivy.metrics import dp 6 | from kivy.uix.image import AsyncImage 7 | from kivy.uix.screenmanager import Screen 8 | from kivy.uix.boxlayout import BoxLayout 9 | from kivy import platform 10 | from kivy.clock import Clock 11 | from kivymd.app import MDApp 12 | from kivymd.uix.button import MDRectangleFlatIconButton 13 | from kivymd.uix.snackbar import Snackbar 14 | from kivymd.uix.behaviors.elevation import CommonElevationBehavior 15 | 16 | from kivyauth.google_auth import initialize_google, login_google, logout_google 17 | from kivyauth.facebook_auth import initialize_fb, login_facebook, logout_facebook 18 | from kivyauth.github_auth import initialize_github, login_github, logout_github 19 | from kivyauth.twitter_auth import initialize_twitter, login_twitter, logout_twitter 20 | from kivyauth.utils import stop_login 21 | from kivyauth.utils import login_providers, auto_login 22 | 23 | load_dotenv() 24 | 25 | if platform == "android": 26 | from android.runnable import run_on_ui_thread 27 | from jnius import autoclass, cast 28 | 29 | Toast = autoclass("android.widget.Toast") 30 | String = autoclass("java.lang.String") 31 | CharSequence = autoclass("java.lang.CharSequence") 32 | Intent = autoclass("android.content.Intent") 33 | Uri = autoclass("android.net.Uri") 34 | NewRelic = autoclass("com.newrelic.agent.android.NewRelic") 35 | LayoutParams = autoclass("android.view.WindowManager$LayoutParams") 36 | AndroidColor = autoclass("android.graphics.Color") 37 | 38 | PythonActivity = autoclass("org.kivy.android.PythonActivity") 39 | 40 | context = PythonActivity.mActivity 41 | 42 | @run_on_ui_thread 43 | def show_toast(text): 44 | t = Toast.makeText( 45 | context, cast(CharSequence, String(text)), Toast.LENGTH_SHORT 46 | ) 47 | t.show() 48 | 49 | @run_on_ui_thread 50 | def set_statusbar_color(): 51 | window = context.getWindow() 52 | window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) 53 | window.setStatusBarColor(AndroidColor.TRANSPARENT) 54 | 55 | 56 | kv = """ 57 | #:import Clock kivy.clock.Clock 58 | #:import platform kivy.platform 59 | ScreenManager: 60 | 61 | LoginScreen: 62 | id: login_screen 63 | 64 | HomeScreen: 65 | id: home_screen 66 | 67 | : 68 | name:"loginscreen" 69 | BoxLayout: 70 | orientation:"vertical" 71 | 72 | MDTopAppBar: 73 | title: "KivyAuth Demo" 74 | elevation:9 75 | opposite_colos: True 76 | left_action_items: [['menu', lambda x: None]] 77 | right_action_items: [['source-fork', lambda x: app.send_to_github()]] 78 | 79 | BoxLayout: 80 | orientation:"vertical" 81 | 82 | Widget: 83 | size_hint_y: None 84 | height: dp(100) 85 | 86 | LoginButton 87 | text: "Sign In with Google" 88 | icon: "google" 89 | text_color: 1,1,1,1 90 | can_color: 66/255, 133/255, 244/255, 1 91 | release_action: app.gl_login 92 | 93 | LoginButton 94 | text: "Sign In with Facebook" 95 | icon: "facebook" 96 | text_color: 1,1,1,1 97 | can_color: 59/255, 89/255, 152/255, 1 98 | release_action: app.fb_login 99 | 100 | LoginButton 101 | text: "Sign In with Github" 102 | icon: "github-circle" if platform == "android" else "github" 103 | text_color: 1,1,1,1 104 | can_color: 33/255, 31/255, 31/255, 1 105 | release_action: app.git_login 106 | 107 | LoginButton 108 | text: "Sign In with Twitter" 109 | icon: "twitter" 110 | text_color: 1,1,1,1 111 | can_color: 8/255, 160/255, 233/255, 1 112 | release_action: app.twitter_login 113 | 114 | Widget: 115 | size_hint_y: None 116 | height: dp(100) 117 | 118 | : 119 | text:"" 120 | icon: "" 121 | text_color: [0,0,0,1] 122 | can_color: 1,1,1,1 123 | release_action: print 124 | RectangleRaisedIconButton: 125 | width: dp(270) 126 | height: dp(50) 127 | canvas.before: 128 | Color: 129 | rgba: root.can_color 130 | Rectangle: 131 | pos: self.pos 132 | size: self.size 133 | 134 | 135 | elevation: 8 136 | icon: root.icon 137 | text: root.text 138 | font_size: dp(8) if platform == "android" else dp(18) 139 | text_color: root.text_color 140 | on_release: 141 | if root.release_action: Clock.schedule_once(root.release_action, 0) 142 | 143 | 144 | : 145 | name:"homescreen" 146 | 147 | BoxLayout: 148 | id: main_box 149 | orientation:"vertical" 150 | 151 | MDTopAppBar: 152 | id: user_name 153 | title: "" 154 | elevation: 9 155 | opposite_colos: True 156 | left_action_items: [['menu', lambda x: None]] 157 | right_action_items: [['information-outline', lambda x: None]] 158 | 159 | AnchorLayout: 160 | id: user_photo 161 | 162 | BoxLayout: 163 | size_hint_y:None 164 | height: dp(20) 165 | padding: dp(5) 166 | 167 | MDLabel: 168 | id: user_email 169 | halign: "center" 170 | font_style: "Body1" 171 | text: "" 172 | 173 | AnchorLayout: 174 | MDRaisedButton: 175 | text: "LOGOUT" 176 | md_bg_color: .9,.9,.9,1 177 | theme_text_color: "Custom" 178 | text_color: 0,0,0,1 179 | on_release: 180 | app.logout_() 181 | 182 | 183 | : 184 | orientation: "vertical" 185 | size_hint_y: None 186 | height: "90dp" 187 | 188 | AnchorLayout: 189 | MDSpinner: 190 | size_hint: None, None 191 | size: dp(30), dp(30) 192 | pos_hint: {'center_x': .5, 'center_y': .5} 193 | 194 | AnchorLayout: 195 | MDLabel: 196 | text: "Logging in..." 197 | halign: "center" 198 | """ 199 | 200 | class Content(BoxLayout): 201 | pass 202 | 203 | class Content(BoxLayout): 204 | pass 205 | 206 | 207 | class LoginScreen(Screen): 208 | pass 209 | 210 | 211 | class RectangleRaisedIconButton( 212 | MDRectangleFlatIconButton, CommonElevationBehavior 213 | ): 214 | pass 215 | 216 | 217 | class LoginDemo(MDApp): 218 | current_provider = "" 219 | 220 | def build(self): 221 | initialize_google( 222 | self.after_login, 223 | self.error_listener, 224 | os.getenv("GOOGLE_CLIENT_ID"), 225 | os.getenv("GOOGLE_CLIENT_SECRET"), 226 | ) 227 | initialize_fb( 228 | self.after_login, 229 | self.error_listener, 230 | os.getenv("FACEBOOK_CLIENT_ID"), 231 | os.getenv("FACEBOOK_CLIENT_SECRET"), 232 | ) 233 | initialize_github( 234 | self.after_login, 235 | self.error_listener, 236 | os.getenv("GITHUB_CLIENT_ID"), 237 | os.getenv("GITHUB_CLIENT_SECRET"), 238 | ) 239 | 240 | if platform == "android": 241 | set_statusbar_color() 242 | tmp = Builder.load_string(kv) 243 | if platform != "android": 244 | from kivymd.uix.dialog import MDDialog 245 | from kivymd.uix.button import MDFlatButton 246 | 247 | btn = MDFlatButton(text="CANCEL", text_color=self.theme_cls.primary_color) 248 | btn.bind(on_release=lambda *args: (stop_login(), self.dialog.dismiss())) 249 | self.dialog = MDDialog( 250 | title="", 251 | size_hint_x=None, 252 | size_hint_y=None, 253 | width="250dp", 254 | type="custom", 255 | auto_dismiss=False, 256 | content_cls=Content(), 257 | buttons=[btn], 258 | ) 259 | return tmp 260 | 261 | def on_start(self): 262 | if platform == "android": 263 | if auto_login(login_providers.google): 264 | self.current_provider = login_providers.google 265 | elif auto_login(login_providers.facebook): 266 | self.current_provider = login_providers.facebook 267 | elif auto_login(login_providers.github): 268 | self.current_provider = login_providers.github 269 | elif auto_login(login_providers.twitter): 270 | self.current_provider = login_providers.twitter 271 | primary_clr= [ 108/255, 52/255, 131/255 ] 272 | hex_color= '#%02x%02x%02x' % (int(primary_clr[0]*200), int(primary_clr[1]*200), int(primary_clr[2]*200)) 273 | set_statusbar_color() 274 | pass 275 | 276 | def show_login_progress(self): 277 | if platform != "android": 278 | self.dialog.open() 279 | 280 | def hide_login_progress(self): 281 | if platform != "android": 282 | self.dialog.dismiss() 283 | 284 | def fb_login(self, *args): 285 | login_facebook() 286 | self.current_provider = login_providers.facebook 287 | self.show_login_progress() 288 | 289 | def gl_login(self, *args): 290 | login_google() 291 | self.current_provider = login_providers.google 292 | self.show_login_progress() 293 | 294 | def git_login(self, *args): 295 | login_github() 296 | self.current_provider = login_providers.github 297 | self.show_login_progress() 298 | 299 | def twitter_login(self, *args): 300 | login_twitter() 301 | self.current_provider = login_providers.twitter 302 | self.show_login_progress() 303 | 304 | def logout_(self): 305 | if self.current_provider == login_providers.google: 306 | logout_google(self.after_logout) 307 | if self.current_provider == login_providers.facebook: 308 | logout_facebook(self.after_logout) 309 | if self.current_provider == login_providers.github: 310 | logout_github(self.after_logout) 311 | if self.current_provider == login_providers.twitter: 312 | logout_twitter(self.after_logout) 313 | 314 | def after_login(self, name, email, photo_uri): 315 | self.hide_login_progress() 316 | 317 | if platform == "android": 318 | show_toast("Logged in using {}".format(self.current_provider)) 319 | else: 320 | Snackbar(text="Logged in using {}".format(self.current_provider)).open() 321 | 322 | self.root.current = "homescreen" 323 | self.update_ui(name, email, photo_uri) 324 | 325 | def after_logout(self): 326 | self.update_ui("", "", "") 327 | self.root.current = "loginscreen" 328 | if platform == "android": 329 | show_toast(text="Logged out from {} login".format(self.current_provider)) 330 | else: 331 | Snackbar( 332 | text="Logged out from {} login".format(self.current_provider) 333 | ).open() 334 | 335 | def update_ui(self, name, email, photo_uri): 336 | self.root.ids.home_screen.ids.user_photo.add_widget( 337 | AsyncImage( 338 | source=photo_uri, size_hint=(None, None), height=dp(60), width=dp(60) 339 | ) 340 | ) 341 | self.root.ids.home_screen.ids.user_name.title = "Welcome, {}".format(name) 342 | self.root.ids.home_screen.ids.user_email.text = ( 343 | "Your Email: {}".format(email) 344 | if email 345 | else "Your Email: Could not fetch email" 346 | ) 347 | 348 | def error_listener(self): 349 | if platform == "android": 350 | show_toast("Error logging in.") 351 | else: 352 | Snackbar(text="Error logging in. Check connection or try again.").show() 353 | Clock.schedule_once(lambda *args: self.hide_login_progress()) 354 | 355 | def send_to_github(self): 356 | if platform == "android": 357 | intent = Intent() 358 | intent.setAction(Intent.ACTION_VIEW) 359 | intent.setData(Uri.parse("https://github.com/shashi278/social-auth-kivy")) 360 | 361 | context.startActivity(intent) 362 | else: 363 | import webbrowser 364 | webbrowser.open("https://github.com/shashi278/social-auth-kivy") 365 | 366 | 367 | if __name__ == "__main__": 368 | LoginDemo().run() 369 | -------------------------------------------------------------------------------- /demo/splash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashi278/social-auth-kivy/0dee71933953ba1e1f45d5e6f255936e78aa889f/demo/splash.jpg -------------------------------------------------------------------------------- /docs/integrate-firebase-auth.md: -------------------------------------------------------------------------------- 1 | ### Integrate Github and Twitter Login in Kivy Applications 2 | 3 | #### Firebase has been used to integrate Github and Twitter logins 4 | 5 | ##### Prerequisites 6 | * Declare Firebase as a gradle dependency in your `buildozer.spec` file: 7 | ```spec 8 | android.gradle_dependencies = com.google.firebase:firebase-auth:19.3.1 9 | ``` 10 | * For using firebase in kivy applications, we need to tweak few settings internally: 11 | * Build your application once, doesn't matter if it crashes 12 | * Then find *build.tmpl.gradle* inside *.buildozer/android/platform/build-armeabi-v7a/dists/app-name__armeabi-v7a/templates/* and change gradle plugin version from 3.1.4 to 3.5.2(I've already created a PR for the same in p4a. Hope they merge it) and add google-services plugin as it's required by firebase and apply the plugin: 13 | ```java 14 | buildscript { 15 | repositories { 16 | //... 17 | } 18 | dependencies { 19 | //make sure its 3.5.2 here instead of 3.1.4 20 | classpath 'com.android.tools.build:gradle:3.5.2' 21 | 22 | //google-services plugin, required by firebase 23 | classpath 'com.google.gms:google-services:4.3.3' 24 | } 25 | } 26 | 27 | //... 28 | 29 | // At the bottom 30 | apply plugin: 'com.google.gms.google-services' 31 | ``` 32 | * Make sure gradle version is set to latest(6.4.1) inside *gradle-wrapper.properties*(It's been updated in develop branch. If you're using master branch of p4a, you may need to update it manually). This file is located at: *.buildozer/android/platform/build-armeabi-v7a/dists/app-name__armeabi-v7a/gradle/wrapper/* 33 | 34 | * Copy your *google-services.json* inside *.buildozer/android/platform/build-armeabi-v7a/dists/app-name__armeabi-v7a/*. Its required for firebase authentication. 35 | 36 | ##### Start Integrating 37 | * Feel free to look at their [docs](https://firebase.google.com/docs/auth/android/start) for more. 38 | * Add few required java classes for firebase authentication 39 | ```python 40 | #----Firebase classes for Github and Twitter Login----# 41 | FirebaseAuth= autoclass('com.google.firebase.auth.FirebaseAuth') 42 | FirebaseApp = autoclass('com.google.firebase.FirebaseApp') 43 | OAuthProvider = autoclass('com.google.firebase.auth.OAuthProvider') 44 | FirebaseUser = autoclass('com.google.firebase.auth.FirebaseUser') 45 | ``` 46 | * We need to implement two java classes to handle success and failure while logging in. 47 | ```python 48 | class OnSuccessListener(PythonJavaClass): 49 | __javainterfaces__=['com/google/android/gms/tasks/OnSuccessListener'] 50 | __javacontext__= 'app' 51 | 52 | @java_method('(Ljava/lang/Object;)V') 53 | def onSuccess(self, result): 54 | # User is signed in 55 | # You may get user information like name,email, etc. now and perform after-login stuffs. 56 | user = FirebaseAuth.getInstance().getCurrentUser() 57 | 58 | # user.getDisplayName() 59 | # user.getEmail() 60 | # user.getPhotoUrl().toString() 61 | 62 | class OnFailureListener(PythonJavaClass): 63 | __javainterfaces__=['com/google/android/gms/tasks/OnFailureListener'] 64 | __javacontext__= 'app' 65 | 66 | @java_method('(Ljava/lang/Exception;)V') 67 | def onFailure(self, e): 68 | #handle exception 69 | ``` 70 | 71 | * Next, get an instance of `FirebaseAuth` inside `build` method of your app: 72 | ```python 73 | self.mAuth= FirebaseAuth.getInstance() 74 | ``` 75 | * Finally, add below codes to start login process inside a method to be called when user clicks the login button: 76 | ```python 77 | provider = OAuthProvider.newBuilder("github.com") # "twitter.com" for twitter login 78 | pendingResultTask = FirebaseAuth.getPendingAuthResult() 79 | if pendingResultTask: 80 | #There's something already here! Finish the sign-in for your user. 81 | task= pendingResultTask.addOnSuccessListener(OnSuccessListener()) 82 | task= task.addOnFailureListener(OnFailureListener()) 83 | else: 84 | #There's no pending result so you need to start the sign-in flow. 85 | 86 | task= FirebaseAuth.startActivityForSignInWithProvider(context, provider.build()) 87 | task= task.addOnSuccessListener(OnSuccessListener()) 88 | task= task.addOnFailureListener(OnFailureListener()) 89 | ``` 90 | 91 | -------------------------------------------------------------------------------- /docs/integrate-google-facebook-login.md: -------------------------------------------------------------------------------- 1 | ## Integrate Google and Facebook Login in Kivy Applications 2 | 3 | ## 4 | ### Google Login: 5 | 6 | #### Prerequisite 7 | * Declare Google Play services as a gradle dependency in your `buildozer.spec` file: 8 | ```spec 9 | android.gradle_dependencies = com.google.android.gms:play-services-auth:18.0.0 10 | ``` 11 | 12 | #### Start Integrating 13 | * Feel free to look at their [doc](https://developers.google.com/identity/sign-in/android/sign-in) for more info 14 | * Add java classes required for google authentication in your `.py` file: 15 | ```python 16 | #----Java Classes For Google Login----# 17 | Gso= autoclass('com.google.android.gms.auth.api.signin.GoogleSignInOptions') 18 | GsoBuilder= autoclass('com.google.android.gms.auth.api.signin.GoogleSignInOptions$Builder') 19 | GSignIn= autoclass('com.google.android.gms.auth.api.signin.GoogleSignIn') 20 | ApiException= autoclass('com.google.android.gms.common.api.ApiException') 21 | ``` 22 | * Create instance of `GoogleSignInClient` inside `build` method of your kivy App: 23 | ```python 24 | gso= GsoBuilder(Gso.DEFAULT_SIGN_IN).requestEmail().build() 25 | self.mGSignInClient= GSignIn.getClient(context, gso) 26 | ``` 27 | Make sure you've, 28 | ```python 29 | PythonActivity= autoclass('org.kivy.android.PythonActivity') 30 | context= PythonActivity.mActivity 31 | ``` 32 | * After the user signs in, you can get a `GoogleSignInAccount` object for the user in the activity's `onActivityResult` method. 33 | For this, we need to bind our function we want to be called when `onActivityResult` gets called during authentication. We can 34 | bind our function using bind function from android.activity as: 35 | ```python 36 | from android.activity import bind as result_bind 37 | 38 | # inside build method 39 | result_bind(on_activity_result=activity_listener_google) 40 | ``` 41 | * Create your activity listener function: 42 | ```python 43 | def activity_listener_google(request_code, result_code, data): 44 | if request_code == RC_SIGN_IN: 45 | task= GSignIn.getSignedInAccountFromIntent(data) 46 | try: 47 | account= task.getResult(ApiException) 48 | if account: 49 | #user is logged in 50 | #Do stuffs you want after a user gets authenticated 51 | #eg. update UI, change screen, etc. 52 | 53 | else: 54 | #unable to get account 55 | 56 | except Exception as e: 57 | #Error in signing in 58 | 59 | ``` 60 | `RC_SIGN_IN` is just a unique request code used to differentiate among different requests passed to `onActivityResult`. Define it an 61 | integer constant. 62 | 63 | * Finally, start sign in process when user clicks a button. Create a function in your `App` class to be called upon clicking login 64 | button and include below codes: 65 | ```python 66 | signInIntent= self.mGSignInClient.getSignInIntent() 67 | context.startActivityForResult(signInIntent, RC_SIGN_IN) 68 | 69 | ``` 70 | 71 | ## 72 | 73 | ### Facebook Login: 74 | 75 | #### Prerequisite 76 | * We'll be creating our own version of facebook login i.e. using python and kivy by following each step from their [doc](https://developers.facebook.com/docs/facebook-login/android/). So, open it in a new tab and follow it along-side. 77 | 78 | #### Start Integrating 79 | * Follow their instructions in step 1 and create an OAuth App. You may skip step 2. 80 | 81 | * For step 3, add Facebook-login SDK as a gradle dependency in your `buildozer.spec` file: 82 | ```spec 83 | android.gradle_dependencies = com.facebook.android:facebook-login:7.0.0 84 | ``` 85 | 86 | * For 4th step, 87 | * Add `INTERNET` permission in `buildozer.spec` file 88 | ```spec 89 | android.permissions = INTERNET 90 | ``` 91 | * Find `android.meta_data` in buildozer.spec file and make it to look like 92 | ```spec 93 | android.meta_data = com.facebook.sdk.ApplicationId=fb 94 | ``` 95 | * Next, find `android.add_activities` in buildozer.spec file and add some activity classes 96 | ```spec 97 | android.add_activites = com.facebook.FacebookActivity, com.facebook.CustomTabActivity 98 | ``` 99 | * Lastly, create an xml file in the same directory as `main.py`. Name it whatever you like and add this into that xml file 100 | ```xml 101 | 102 | 103 | 104 | 105 | 106 | ``` 107 | Now add this xml as an intent filter in the spec file 108 | ```spec 109 | android.manifest.intent_filters = .xml 110 | ``` 111 | 112 | * Do as instructed in step 5 and 6 113 | 114 | * Skip steps 7 and 8 115 | 116 | * For next steps, we'll be implementing few java interfaces in python. 117 | 118 | * Include few required java classes: 119 | ```python 120 | #----Java Classes For Facebook Login----# 121 | AccessToken= autoclass('com.facebook.AccessToken') 122 | CallbackManagerFactory= autoclass('com.facebook.CallbackManager$Factory') 123 | FacebookCallback= autoclass('com.facebook.FacebookCallback') 124 | FacebookException= autoclass('com.facebook.FacebookException') 125 | FacebookSdk= autoclass('com.facebook.FacebookSdk') 126 | LoginManager= autoclass('com.facebook.login.LoginManager') 127 | GraphRequest= autoclass('com.facebook.GraphRequest') 128 | ImageRequest= autoclass('com.facebook.internal.ImageRequest') 129 | 130 | ``` 131 | * Now, to respond to a login result, you need to register a callback with `LoginManager`. Before that, 132 | we need to implement `GraphJSONObjectCallback` class which will be used when login request succeeds and within which 133 | we can get user information(like, name, email, etc.) 134 | ```python 135 | class PythonGraphJSONObjectCallback(PythonJavaClass): 136 | __javainterfaces__= ['com/facebook/GraphRequest$GraphJSONObjectCallback'] 137 | __javacontext__= 'app' 138 | 139 | @java_method('(Lorg/json/JSONObject;Lcom/facebook/GraphResponse;)V') 140 | def onCompleted(self, me, response): 141 | if response.getError(): 142 | #handle error 143 | 144 | else: 145 | 146 | if AccessToken.isCurrentAccessTokenActive(): 147 | access_token= AccessToken.getCurrentAccessToken().getToken() 148 | else: 149 | access_token= "" 150 | 151 | uri= ImageRequest.getProfilePictureUri( 152 | me.optString("id"), #user id 153 | 100, #image height 154 | 100, #image width 155 | access_token #access token 156 | ) 157 | 158 | # user has been logged in. Get other info 159 | # and do after login stuffs(like, updating UI, etc.) 160 | ``` 161 | 162 | * Next, we need to implement `FacebookCallback` class which will be registered with `LoginManager`. 163 | ```python 164 | class PythonFacebookCallback(PythonJavaClass): 165 | __javainterfaces__= ['com/facebook/FacebookCallback'] 166 | __javacontext__= 'app' 167 | 168 | @java_method('(Ljava/lang/Object;)V') 169 | def onSuccess(self, result): 170 | 171 | request= GraphRequest.newMeRequest( 172 | result.getAccessToken(), 173 | PythonGraphJSONObjectCallback() 174 | ) 175 | 176 | params= Bundle() 177 | params.putString("fields", "last_name,first_name,email") 178 | request.setParameters(params) 179 | request.executeAsync() 180 | 181 | 182 | @java_method('()V') 183 | def onCancel(self): 184 | #Login has been cancelled 185 | 186 | 187 | @java_method('(Lcom/facebook/FacebookException;)V') 188 | def onError(self, error): 189 | #Error in logging in 190 | 191 | ``` 192 | 193 | * We then need o initialize Facebook SDK inside App's `build` method: 194 | ```python 195 | FacebookSdk.sdkInitialize(context.getApplicationContext()) 196 | ``` 197 | * And then register the callback with `LoginManager` inside `build` method: 198 | ```python 199 | mCallbackManager = CallbackManagerFactory.create() 200 | mFacebookCallback = PythonFacebookCallback() 201 | self.mLoginMgr = LoginManager.getInstance() 202 | self.mLoginMgr.registerCallback(mCallbackManager, mFacebookCallback) 203 | ``` 204 | * Finally, in your `onActivityResult` method, call `callbackManager.onActivityResult` to pass the login results to the `LoginManager` via `callbackManager`. For this we just need to add one more line to our `build` method: 205 | ```python 206 | result_bind(on_activity_result=mCallbackManager.onActivityResult) 207 | ``` 208 | Make sure you've `result_bind` imported: 209 | ```python 210 | from android.activity import bind as result_bind 211 | ``` 212 | 213 | * Finally, perform the actual login with required scopes upon clicking a button. Add below codes inside a function to be called upon a button's pressing/releasing event. 214 | ```python 215 | self.mLoginMgr.logInWithReadPermissions( 216 | cast(autoclass('android.app.Activity'), 217 | context), Arrays.asList("email", "public_profile") 218 | ) 219 | ``` 220 | 221 | -------------------------------------------------------------------------------- /docs/prerequisites.md: -------------------------------------------------------------------------------- 1 | ### For Google Login 2 | # 3 | * Goto https://console.cloud.google.com/ 4 | * While on cloud console, head to `APIs & Services` > `Credentials` > Click on `+ Create Credentials` and select `OAuth client ID` 5 | * On the following screen select `Android` under `Application Type` field and follow on-screen instructions to fill all the fields and create a client ID for Android. 6 | * Declare Google Play services as a gradle dependency in your `buildozer.spec` file: 7 | 8 | ```spec 9 | android.gradle_dependencies = com.google.android.gms:play-services-auth:18.0.0 10 | ``` 11 | 12 | * Add `INTERNET` permission in `buildozer.spec` file 13 | ```spec 14 | android.permissions = INTERNET 15 | ``` 16 | 17 | # 18 | ### For Facebook Login 19 | ## 20 | * You need to follow their [doc](https://developers.facebook.com/docs/facebook-login/android/) along-side. So open it up in a new tab. 21 | 22 | * Follow their instruction in step 1 and create an OAuth App. You may skip step 2. 23 | 24 | * For step 3, add Facebook-login SDK as a gradle dependency in your `buildozer.spec` file: 25 | ```spec 26 | android.gradle_dependencies = com.facebook.android:facebook-login:7.0.0 27 | ``` 28 | 29 | * For 4th step, 30 | * Add `INTERNET` permission in `buildozer.spec` file 31 | ```spec 32 | android.permissions = INTERNET 33 | ``` 34 | * Copy your OAuth App ID and find `android.meta_data` in buildozer.spec file and make it to look like 35 | ```spec 36 | android.meta_data = com.facebook.sdk.ApplicationId=fb 37 | ``` 38 | * Next, find `android.add_activities` in buildozer.spec file and add some activity classes 39 | ```spec 40 | android.add_activites = com.facebook.FacebookActivity, com.facebook.CustomTabActivity 41 | ``` 42 | * Lastly, create an xml file in the same directory as `main.py`. Name it whatever you like and paste below text into that xml file 43 | ```xml 44 | 45 | 46 | 47 | 48 | 49 | 50 | ``` 51 | Now add this xml as an intent filter in the spec file 52 | ```spec 53 | android.manifest.intent_filters = .xml 54 | ``` 55 | # 56 | ### For Firebase Login 57 | ## 58 | * Make sure you've created OAuth apps for [Github](https://github.com/settings/applications/new) and/or [Twitter](https://developer.twitter.com/en/apps/create) logins before proceeding further. 59 | 60 | * Go to [firebase console](https://console.firebase.google.com) and create a project for your application. 61 | 62 | * Once you've created a project, add your android app into the project from the Project Overview screen and follow the on-screen instructions and finally download 63 | the `google-services.json` file. 64 | 65 | * Declare Firebase as a gradle dependency in your `buildozer.spec` file: 66 | ```spec 67 | android.gradle_dependencies = com.google.firebase:firebase-auth:19.3.1 68 | ``` 69 | 70 | * Add `INTERNET` permission in `buildozer.spec` file 71 | ```spec 72 | android.permissions = INTERNET 73 | ``` 74 | 75 | * Next, from your project's console head to the Authentication section and then switch to the Sign-in method tab and enable the sign-in methods you want for your 76 | app.(For kivyauth you only need to enable Github and Twitter and follow on-screen instructions) 77 | 78 | * Now you need to tweak few settings internally: 79 | * Build your application once, doesn't matter if it crashes 80 | * Then find ***build.tmpl.gradle*** inside *.buildozer/android/platform/python-for-android/pythonforandroid/bootstraps/common/build/templates* and change gradle plugin version 81 | from 3.1.4 to 3.5.2 and add google-services plugin as it's required by firebase and apply the plugin: 82 | ```java 83 | buildscript { 84 | repositories { 85 | //... 86 | } 87 | dependencies { 88 | //make sure its 3.5.2 here instead of 3.1.4 89 | classpath 'com.android.tools.build:gradle:3.5.2' 90 | 91 | //google-services plugin, required by firebase 92 | classpath 'com.google.gms:google-services:4.3.3' 93 | } 94 | } 95 | 96 | //... 97 | 98 | // At the bottom 99 | apply plugin: 'com.google.gms.google-services' 100 | ``` 101 | 102 | * Paste your ***google-services.json*** inside *.buildozer/android/platform/python-for-android/pythonforandroid/bootstraps/common/build/*. 103 | * Re-build your application 104 | 105 | -------------------------------------------------------------------------------- /kivyauth/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy.logger import Logger 2 | from kivy.utils import platform 3 | 4 | __version__ = "2.3.3" 5 | _log_message = "KivyAuth:" + f" {__version__}" + f' (installed at "{__file__}")' 6 | 7 | __all__ = ("login_providers", "auto_login") 8 | 9 | Logger.info(_log_message) 10 | -------------------------------------------------------------------------------- /kivyauth/android/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashi278/social-auth-kivy/0dee71933953ba1e1f45d5e6f255936e78aa889f/kivyauth/android/__init__.py -------------------------------------------------------------------------------- /kivyauth/android/facebook_auth.py: -------------------------------------------------------------------------------- 1 | from android.activity import bind as result_bind 2 | from jnius import PythonJavaClass, autoclass, cast, java_method 3 | from kivy.logger import Logger 4 | 5 | CallbackManagerFactory = autoclass("com.facebook.CallbackManager$Factory") 6 | FacebookSdk = autoclass("com.facebook.FacebookSdk") 7 | LoginManager = autoclass("com.facebook.login.LoginManager") 8 | AccessToken = autoclass("com.facebook.AccessToken") 9 | GraphRequest = autoclass("com.facebook.GraphRequest") 10 | ImageRequest = autoclass("com.facebook.internal.ImageRequest") 11 | 12 | Bundle = autoclass("android.os.Bundle") 13 | Arrays = autoclass("java.util.Arrays") 14 | 15 | PythonActivity = autoclass("org.kivy.android.PythonActivity") 16 | 17 | context = PythonActivity.mActivity 18 | mLoginMgr = None 19 | mList = None 20 | 21 | 22 | __all__ = ("initialize_fb", "login_facebook", "logout_facebook") 23 | 24 | 25 | class PythonGraphJSONObjectCallback(PythonJavaClass): 26 | __javainterfaces__ = ["com/facebook/GraphRequest$GraphJSONObjectCallback"] 27 | __javacontext__ = "app" 28 | 29 | def __init__(self, complete_listener): 30 | self.complete_listener = complete_listener 31 | super().__init__() 32 | 33 | @java_method("(Lorg/json/JSONObject;Lcom/facebook/GraphResponse;)V") 34 | def onCompleted(self, me, response): 35 | if response.getError(): 36 | # handle error 37 | Logger.error("KivyAuth: Unable to retrieve profile info") 38 | 39 | else: 40 | 41 | if AccessToken.isCurrentAccessTokenActive(): 42 | access_token = AccessToken.getCurrentAccessToken().getToken() 43 | else: 44 | access_token = "" 45 | 46 | uri = ImageRequest.getProfilePictureUri( 47 | me.optString("id"), 200, 200, access_token 48 | ) 49 | 50 | Logger.info( 51 | "KivyAuth: Profile info retrieved successfully." 52 | " Calling success listener." 53 | ) 54 | 55 | self.complete_listener( 56 | me.optString("first_name") + " " + me.optString("last_name"), 57 | me.optString("email"), 58 | uri.toString() if uri else '', 59 | ) 60 | 61 | 62 | class PythonFacebookCallback(PythonJavaClass): 63 | __javainterfaces__ = ["com/facebook/FacebookCallback"] 64 | __javacontext__ = "app" 65 | 66 | def __init__(self, success_listener, cancel_listener, error_listener): 67 | self.success_listener = success_listener 68 | self.cancel_listener = cancel_listener 69 | self.error_listener = error_listener 70 | 71 | @java_method("(Ljava/lang/Object;)V") 72 | def onSuccess(self, result): 73 | Logger.info("KivyAuth: Login success. Requesting profile info.") 74 | 75 | request = GraphRequest.newMeRequest( 76 | result.getAccessToken(), 77 | PythonGraphJSONObjectCallback(self.success_listener), 78 | ) 79 | 80 | params = Bundle() 81 | params.putString("fields", "last_name,first_name,email") 82 | request.setParameters(params) 83 | request.executeAsync() 84 | 85 | @java_method("()V") 86 | def onCancel(self): 87 | Logger.info("KivyAuth: Login Cancelled.") 88 | self.cancel_listener() 89 | 90 | @java_method("(Lcom/facebook/FacebookException;)V") 91 | def onError(self, error): 92 | Logger.error("KivyAuth: Error logging in.") 93 | self.error_listener() 94 | 95 | 96 | def initialize_fb(success_listener, error_listener, *args, **kwargs): 97 | """ 98 | Function to initialize facebook login. 99 | Must be called inside `build` method of kivy App before actual login. 100 | 101 | :param: `success_listener` - Function to be called on login success 102 | :param: `error_listener` - Function to be called on login error 103 | """ 104 | FacebookSdk.sdkInitialize(context.getApplicationContext()) 105 | mCallbackManager = CallbackManagerFactory.create() 106 | mFacebookCallback = PythonFacebookCallback( 107 | success_listener, error_listener, error_listener 108 | ) 109 | result_bind(on_activity_result=mCallbackManager.onActivityResult) 110 | 111 | Logger.info("KivyAuth: Initialized facebook signin") 112 | global mList 113 | mList = [mCallbackManager, mFacebookCallback] 114 | 115 | 116 | def login_facebook(): 117 | """ 118 | Function to login using facebook 119 | """ 120 | Logger.info("KivyAuth: Initiated facebook login") 121 | global mLoginMgr 122 | mLoginMgr = LoginManager.getInstance() 123 | mLoginMgr.registerCallback(*mList) 124 | mLoginMgr.logInWithReadPermissions( 125 | cast(autoclass("android.app.Activity"), context), 126 | Arrays.asList("email", "public_profile"), 127 | ) 128 | 129 | 130 | def auto_facebook(): 131 | """ 132 | Auto login using Facebook. You may call it `on_start`. 133 | """ 134 | accessToken = AccessToken.getCurrentAccessToken() 135 | if accessToken and not accessToken.isExpired(): 136 | login_facebook() 137 | return True 138 | 139 | 140 | def logout_facebook(after_logout): 141 | """ 142 | Logout from facebook login 143 | 144 | :param: `after_logout` - Function to be called after logging out 145 | """ 146 | mLoginMgr.logOut() 147 | after_logout() 148 | Logger.info("KivyAuth: Logged out from facebook login") 149 | -------------------------------------------------------------------------------- /kivyauth/android/firebase_auth.py: -------------------------------------------------------------------------------- 1 | from android.activity import bind as result_bind 2 | from jnius import PythonJavaClass, autoclass, java_method 3 | from kivy.logger import Logger 4 | 5 | FirebaseAuth = autoclass("com.google.firebase.auth.FirebaseAuth") 6 | OAuthProvider = autoclass("com.google.firebase.auth.OAuthProvider") 7 | 8 | PythonActivity = autoclass("org.kivy.android.PythonActivity") 9 | 10 | context = PythonActivity.mActivity 11 | 12 | event_success_listener = None 13 | event_error_listener = None 14 | 15 | 16 | class OnSuccessListener(PythonJavaClass): 17 | __javainterfaces__ = ["com/google/android/gms/tasks/OnSuccessListener"] 18 | __javacontext__ = "app" 19 | 20 | def __init__(self, success_listener): 21 | self.success_listener = success_listener 22 | 23 | @java_method("(Ljava/lang/Object;)V") 24 | def onSuccess(self, result): 25 | # user is signed in 26 | Logger.info("KivyAuth: Sign in successful using firebase.") 27 | user = FirebaseAuth.getInstance().getCurrentUser() 28 | 29 | _call_success(user) 30 | 31 | 32 | class OnFailureListener(PythonJavaClass): 33 | __javainterfaces__ = ["com/google/android/gms/tasks/OnFailureListener"] 34 | __javacontext__ = "app" 35 | 36 | def __init__(self, error_listener): 37 | self.error_listener = error_listener 38 | 39 | @java_method("(Ljava/lang/Exception;)V") 40 | def onFailure(self, e): 41 | # handle exception 42 | Logger.info("KivyAuth: Sign in using firebase failed") 43 | self.error_listener() 44 | 45 | 46 | def _call_success(user): 47 | event_success_listener( 48 | user.getDisplayName(), 49 | user.getEmail(), 50 | user.getPhotoUrl().toString() if user.getPhotoUrl() else '' 51 | ) 52 | 53 | 54 | def initialize_firebase(success_listener, error_listener): 55 | """ 56 | Function to initialize firebase login. 57 | Must be called inside `build` method of kivy App before actual login. 58 | 59 | :param: `success_listener` - Function to be called on login success 60 | :param: `error_listener` - Function to be called on login error 61 | """ 62 | FirebaseAuth.getInstance() 63 | Logger.info("KivyAuth: Initialized firebase auth") 64 | global event_success_listener 65 | event_success_listener = success_listener 66 | global event_error_listener 67 | event_error_listener = error_listener 68 | 69 | 70 | def firebase_login(provider): 71 | pendingResultTask = FirebaseAuth.getPendingAuthResult() 72 | if pendingResultTask: 73 | # There's something already here! Finish the sign-in for your user. 74 | 75 | task = pendingResultTask.addOnSuccessListener( 76 | OnSuccessListener(event_success_listener) 77 | ) 78 | task = task.addOnFailureListener(OnFailureListener(event_error_listener)) 79 | else: 80 | # There's no pending result so you need to start the sign-in flow. 81 | 82 | task = FirebaseAuth.startActivityForSignInWithProvider( 83 | context, provider.build() 84 | ) 85 | task = task.addOnSuccessListener(OnSuccessListener(event_success_listener)) 86 | task = task.addOnFailureListener(OnFailureListener(event_error_listener)) 87 | 88 | 89 | def firebase_logout(after_logout): 90 | Logger.info("KivyAuth: Initiated logout using firebase") 91 | FirebaseAuth.getInstance().signOut() 92 | after_logout() 93 | Logger.info("KivyAuth: Logged out from firebase auth") 94 | 95 | 96 | def auto_firebase(): 97 | user = FirebaseAuth.getInstance().getCurrentUser() 98 | if user: 99 | _call_success(user) 100 | return True 101 | 102 | 103 | def login_github(): 104 | """ 105 | Function to login using github 106 | """ 107 | Logger.info("KivyAuth: Initiated github login") 108 | provider = OAuthProvider.newBuilder("github.com") 109 | firebase_login(provider) 110 | 111 | 112 | def login_twitter(): 113 | """ 114 | Function to login using twitter 115 | """ 116 | Logger.info("KivyAuth: Initiated twitter login") 117 | provider = OAuthProvider.newBuilder("twitter.com") 118 | firebase_login(provider) 119 | 120 | 121 | def logout_twitter(after_logout): 122 | """ 123 | Logout from twitter login 124 | 125 | :param: `after_logout` - Function to be called after logging out 126 | """ 127 | firebase_logout(after_logout) 128 | 129 | 130 | def logout_github(after_logout): 131 | """ 132 | Logout from github login 133 | 134 | :param: `after_logout` - Function to be called after logging out 135 | """ 136 | firebase_logout(after_logout) 137 | -------------------------------------------------------------------------------- /kivyauth/android/github_auth.py: -------------------------------------------------------------------------------- 1 | from kivy.logger import Logger 2 | from kivyauth.android.firebase_auth import ( 3 | firebase_login, 4 | firebase_logout, 5 | initialize_firebase, 6 | OAuthProvider, 7 | auto_firebase as auto_github, 8 | ) 9 | 10 | __all__ = ("initialize_github", "login_github", "logout_github") 11 | 12 | 13 | def initialize_github(succes_listener, error_listener, *args, **kwargs): 14 | initialize_firebase(succes_listener, error_listener) 15 | 16 | 17 | def login_github(): 18 | """ 19 | Function to login using github 20 | """ 21 | Logger.info("KivyAuth: Initiated github login") 22 | provider = OAuthProvider.newBuilder("github.com") 23 | firebase_login(provider) 24 | 25 | 26 | def logout_github(after_logout): 27 | """ 28 | Logout from github login 29 | 30 | :param: `after_logout` - Function to be called after logging out 31 | """ 32 | firebase_logout(after_logout) 33 | -------------------------------------------------------------------------------- /kivyauth/android/google_auth.py: -------------------------------------------------------------------------------- 1 | from android.activity import bind as result_bind 2 | from jnius import autoclass 3 | from kivy.clock import mainthread 4 | from kivy.logger import Logger 5 | 6 | Gso = autoclass("com.google.android.gms.auth.api.signin.GoogleSignInOptions") 7 | GsoBuilder = autoclass( 8 | "com.google.android.gms.auth.api.signin.GoogleSignInOptions$Builder" 9 | ) 10 | GSignIn = autoclass("com.google.android.gms.auth.api.signin.GoogleSignIn") 11 | ApiException = autoclass("com.google.android.gms.common.api.ApiException") 12 | PythonActivity = autoclass("org.kivy.android.PythonActivity") 13 | context = PythonActivity.mActivity 14 | 15 | RC_SIGN_IN = 10122 16 | mGSignInClient = None 17 | event_success_listener = None 18 | 19 | __all__ = ("initialize_google", "login_google", "logout_google") 20 | 21 | 22 | class GoogleActivityListener: 23 | def __init__(self, success_listener, error_listener): 24 | self.success_listener = success_listener 25 | self.error_listener = error_listener 26 | 27 | def google_activity_listener(self, request_code, result_code, data): 28 | if request_code == RC_SIGN_IN: 29 | Logger.info("KivyAuth: google_activity_listener called.") 30 | task = GSignIn.getSignedInAccountFromIntent(data) 31 | try: 32 | account = task.getResult(ApiException) 33 | _call_success(account) 34 | 35 | except Exception as e: 36 | Logger.info("KivyAuth: Error signing in using Google. {}".format(e)) 37 | self.error_listener() 38 | 39 | 40 | def _call_success(account): 41 | if account: 42 | Logger.info("KivyAuth: Google Login success. Calling success listener.") 43 | event_success_listener( 44 | account.getDisplayName(), 45 | account.getEmail(), 46 | account.getPhotoUrl().toString() if account.getPhotoUrl() else '', 47 | ) 48 | return True 49 | 50 | 51 | def initialize_google(success_listener, error_listener, *args, **kwargs): 52 | """ 53 | Function to initialize google login. 54 | Must be called inside `build` method of kivy App before actual login. 55 | 56 | :param: `success_listener` - Function to be called on login success 57 | :param: `error_listener` - Function to be called on login error 58 | """ 59 | global event_success_listener 60 | event_success_listener = success_listener 61 | gso = GsoBuilder(Gso.DEFAULT_SIGN_IN).requestEmail().build() 62 | global mGSignInClient 63 | mGSignInClient = GSignIn.getClient(context, gso) 64 | gal = GoogleActivityListener(success_listener, error_listener) 65 | result_bind(on_activity_result=gal.google_activity_listener) 66 | 67 | Logger.info("KivyAuth: Initialized google signin") 68 | 69 | 70 | # @mainthread 71 | def login_google(): 72 | """ 73 | Function to login using google 74 | """ 75 | Logger.info("KivyAuth: Initiated google login") 76 | signInIntent = mGSignInClient.getSignInIntent() 77 | context.startActivityForResult(signInIntent, RC_SIGN_IN) 78 | 79 | 80 | def auto_google(): 81 | """ 82 | Auto login using Google. You may call it `on_start`. 83 | """ 84 | account = GSignIn.getLastSignedInAccount(context) 85 | return _call_success(account) 86 | 87 | 88 | def logout_google(after_logout): 89 | """ 90 | Logout from google login 91 | 92 | :param: `after_logout` - Function to be called after logging out 93 | """ 94 | mGSignInClient.signOut() 95 | after_logout() 96 | Logger.info("KivyAuth: Logged out from google login") 97 | -------------------------------------------------------------------------------- /kivyauth/android/twitter_auth.py: -------------------------------------------------------------------------------- 1 | from kivy.logger import Logger 2 | from kivyauth.android.firebase_auth import ( 3 | firebase_login, 4 | firebase_logout, 5 | initialize_firebase, 6 | OAuthProvider, 7 | auto_firebase as auto_twitter, 8 | ) 9 | 10 | __all__ = ("initialize_twitter", "login_twitter", "logout_twitter") 11 | 12 | 13 | def initialize_twitter(succes_listener, error_listener, *args, **kwargs): 14 | initialize_firebase(succes_listener, error_listener) 15 | 16 | 17 | def login_twitter(): 18 | """ 19 | Function to login using twitter 20 | """ 21 | Logger.info("KivyAuth: Initiated twitter login") 22 | provider = OAuthProvider.newBuilder("twitter.com") 23 | firebase_login(provider) 24 | 25 | 26 | def logout_twitter(after_logout): 27 | """ 28 | Logout from twitter login 29 | 30 | :param: `after_logout` - Function to be called after logging out 31 | """ 32 | firebase_logout(after_logout) 33 | -------------------------------------------------------------------------------- /kivyauth/desktop/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shashi278/social-auth-kivy/0dee71933953ba1e1f45d5e6f255936e78aa889f/kivyauth/desktop/__init__.py -------------------------------------------------------------------------------- /kivyauth/desktop/facebook_auth.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from oauthlib.oauth2 import WebApplicationClient 3 | import json 4 | import webbrowser 5 | import random 6 | 7 | from kivyauth.desktop.utils import ( 8 | request, 9 | redirect, 10 | is_connected, 11 | start_server, 12 | app, 13 | _close_server_pls, 14 | port, 15 | stop_login, 16 | ) 17 | from kivy.app import App 18 | from kivy.clock import Clock 19 | 20 | # facebook configuration 21 | FACEBOOK_CLIENT_ID = "" 22 | FACEBOOK_CLIENT_SECRET = "" 23 | fb_authorization_endpoint = "https://www.facebook.com/v15.0/dialog/oauth?" 24 | fb_token_endpoint = "https://graph.facebook.com/v15.0/oauth/access_token?" 25 | fb_userinfo_endpoint = "https://graph.facebook.com/v15.0/me?" 26 | 27 | client_facebook = None 28 | 29 | event_success_listener = None 30 | event_error_listener = None 31 | 32 | __all__ = ("initialize_fb", "login_facebook", "logout_facebook") 33 | 34 | 35 | def initialize_fb(success_listener, error_listener, client_id=None, client_secret=None): 36 | a = App.get_running_app() 37 | a.bind(on_stop=lambda *args: _close_server_pls(port)) 38 | 39 | global event_success_listener 40 | event_success_listener = success_listener 41 | 42 | global event_error_listener 43 | event_error_listener = error_listener 44 | 45 | global FACEBOOK_CLIENT_ID 46 | FACEBOOK_CLIENT_ID = client_id 47 | 48 | global FACEBOOK_CLIENT_SECRET 49 | FACEBOOK_CLIENT_SECRET = client_secret 50 | 51 | global client_facebook 52 | client_facebook = WebApplicationClient(FACEBOOK_CLIENT_ID) 53 | 54 | 55 | @app.route("/loginFacebook") 56 | def loginFacebook(): 57 | 58 | st = "".join([random.choice("abcdefgh1234567") for _ in range(10)]) 59 | ds = "".join([random.choice("1234567890") for _ in range(10)]) 60 | 61 | request_uri = client_facebook.prepare_request_uri( 62 | fb_authorization_endpoint, 63 | redirect_uri=request.base_url + "/callbackFacebook", 64 | scope=["email"], 65 | state="{st=" + st + ",ds=" + ds + "}", 66 | ) 67 | 68 | return redirect(request_uri) 69 | 70 | 71 | @app.route("/loginFacebook/callbackFacebook") 72 | def callbackFacebook(): 73 | code = request.args.get("code") 74 | 75 | # prepare a request to get tokens 76 | token_url, headers, body = client_facebook.prepare_token_request( 77 | fb_token_endpoint, 78 | client_id=FACEBOOK_CLIENT_ID, 79 | client_secret=FACEBOOK_CLIENT_SECRET, 80 | code=code, 81 | redirect_url=request.base_url, 82 | ) 83 | 84 | # send the request and get the response 85 | token_response = requests.post(token_url, headers=headers, data=body) 86 | 87 | # send the request and get the response 88 | # app_token_response = requests.get(token_url, headers=headers, data=body) 89 | 90 | headers = { 91 | "Authorization": token_response.json()["access_token"] 92 | + " " 93 | + token_response.json()["token_type"] 94 | } 95 | 96 | request_uri = client_facebook.prepare_request_uri( 97 | fb_userinfo_endpoint, 98 | fields=["id", "name", "email", "picture"], 99 | access_token=token_response.json()["access_token"], 100 | ) 101 | 102 | # make the request and get the response 103 | userinfo_response = requests.get(request_uri, headers=headers, data=None).json() 104 | stop_login() 105 | 106 | # parse the information 107 | if userinfo_response.get("id"): 108 | Clock.schedule_once(lambda *args: event_success_listener( 109 | userinfo_response["name"], 110 | userinfo_response["email"], 111 | userinfo_response["picture"]["data"]["url"], 112 | ), 0) 113 | 114 | return "

Logged in using Facebook. Return back to the Kivy application

" 115 | 116 | event_error_listener() 117 | return "User Email not available or not verified" 118 | 119 | 120 | def login_facebook(): 121 | if is_connected(): 122 | start_server(port) 123 | webbrowser.open("https://127.0.0.1:{}/loginFacebook".format(port), 1, False) 124 | else: 125 | event_error_listener() 126 | 127 | 128 | def logout_facebook(after_logout): 129 | """ 130 | Logout from facebook login 131 | 132 | :param: `after_logout` - Function to be called after logging out 133 | """ 134 | after_logout() 135 | -------------------------------------------------------------------------------- /kivyauth/desktop/github_auth.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from oauthlib.oauth2 import WebApplicationClient 3 | import json 4 | import webbrowser 5 | import random 6 | import re 7 | 8 | from kivyauth.desktop.utils import ( 9 | request, 10 | redirect, 11 | is_connected, 12 | start_server, 13 | app, 14 | _close_server_pls, 15 | port, 16 | stop_login, 17 | ) 18 | from kivy.app import App 19 | from kivy.clock import Clock 20 | 21 | # github configuration 22 | GITHUB_CLIENT_ID = "" 23 | GITHUB_CLIENT_SECRET = "" 24 | git_authorization_endpoint = "https://github.com/login/oauth/authorize" 25 | git_token_endpoint = "https://github.com/login/oauth/access_token" 26 | git_userinfo_endpoint = "https://api.github.com/user" 27 | 28 | client_github = None 29 | 30 | event_success_listener = None 31 | event_error_listener = None 32 | 33 | __all__ = ("initialize_github", "login_github", "logout_github") 34 | 35 | 36 | def initialize_github( 37 | success_listener, error_listener, client_id=None, client_secret=None 38 | ): 39 | a = App.get_running_app() 40 | a.bind(on_stop=lambda *args: _close_server_pls(port)) 41 | 42 | global event_success_listener 43 | event_success_listener = success_listener 44 | 45 | global event_error_listener 46 | event_error_listener = error_listener 47 | 48 | global GITHUB_CLIENT_ID 49 | GITHUB_CLIENT_ID = client_id 50 | 51 | global GITHUB_CLIENT_SECRET 52 | GITHUB_CLIENT_SECRET = client_secret 53 | 54 | global client_github 55 | client_github = WebApplicationClient(GITHUB_CLIENT_ID) 56 | 57 | @app.route("/loginGithub") 58 | def loginGithub(): 59 | request_uri = client_github.prepare_request_uri( 60 | git_authorization_endpoint, 61 | redirect_uri=request.base_url + "/callbackGithub", 62 | scope=["read:user:email"], 63 | state="".join([random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(20)]), 64 | ) 65 | return redirect(request_uri) 66 | 67 | @app.route("/loginGithub/callbackGithub") 68 | def callbackGithub(): 69 | # Get authorization code Github sent back 70 | code = request.args.get("code") 71 | 72 | # prepare a request to get tokens 73 | token_url, headers, body = client_github.prepare_token_request( 74 | git_token_endpoint, 75 | client_id=GITHUB_CLIENT_ID, 76 | client_secret=GITHUB_CLIENT_SECRET, 77 | code=code, 78 | redirect_url=request.base_url, 79 | state="".join([random.choice("abcdefghijklmnopqrstuvwxyz") for _ in range(20)]), 80 | ) 81 | 82 | # send the request and get the response 83 | token_response = requests.post(token_url, headers=headers, data=body) 84 | 85 | # for some reason, token_response.json() is raising an error. So gotta do it manually 86 | token_info = re.search( 87 | "^access_token=(.*)&scope=.*&token_type=(.*)$", token_response.text 88 | ) 89 | 90 | headers = {"Authorization": token_info.group(2) + " " + token_info.group(1)} 91 | body = None 92 | 93 | userinfo_response = requests.get( 94 | git_userinfo_endpoint, headers=headers, data=body 95 | ).json() 96 | stop_login() 97 | 98 | # parse the information 99 | if userinfo_response.get("id"): 100 | Clock.schedule_once(lambda *args: event_success_listener( 101 | userinfo_response["name"], 102 | userinfo_response["email"], 103 | userinfo_response["avatar_url"], 104 | ), 0) 105 | return "

Logged in using Github. Return back to the Kivy application

" 106 | 107 | event_error_listener() 108 | return "User Email not available or not verified" 109 | 110 | def login_github(): 111 | if is_connected(): 112 | start_server(port) 113 | webbrowser.open("https://127.0.0.1:{}/loginGithub".format(port), 1, False) 114 | else: 115 | event_error_listener() 116 | 117 | def logout_github(after_logout): 118 | """ 119 | Logout from github login 120 | 121 | :param: `after_logout` - Function to be called after logging out 122 | """ 123 | after_logout() 124 | -------------------------------------------------------------------------------- /kivyauth/desktop/google_auth.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from oauthlib.oauth2 import WebApplicationClient 3 | import json 4 | import webbrowser 5 | 6 | from kivyauth.desktop.utils import ( 7 | request, 8 | redirect, 9 | is_connected, 10 | start_server, 11 | app, 12 | _close_server_pls, 13 | port, 14 | stop_login, 15 | ) 16 | from kivy.app import App 17 | from kivy.clock import Clock 18 | 19 | # google configurations 20 | GOOGLE_CLIENT_ID = "" 21 | GOOGLE_CLIENT_SECRET = "" 22 | GOOGLE_DISCOVERY_URL = "https://accounts.google.com/.well-known/openid-configuration" 23 | 24 | client_google = None 25 | 26 | event_success_listener = None 27 | event_error_listener = None 28 | 29 | __all__ = ("initialize_google", "login_google", "logout_google") 30 | 31 | 32 | def get_google_provider_cfg(): 33 | return requests.get(GOOGLE_DISCOVERY_URL).json() 34 | 35 | 36 | def initialize_google( 37 | success_listener, error_listener, client_id=None, client_secret=None 38 | ): 39 | a = App.get_running_app() 40 | a.bind(on_stop=lambda *args: _close_server_pls(port)) 41 | 42 | global event_success_listener 43 | event_success_listener = success_listener 44 | 45 | global event_error_listener 46 | event_error_listener = error_listener 47 | 48 | global GOOGLE_CLIENT_ID 49 | GOOGLE_CLIENT_ID = client_id 50 | 51 | global GOOGLE_CLIENT_SECRET 52 | GOOGLE_CLIENT_SECRET = client_secret 53 | 54 | global client_google 55 | client_google = WebApplicationClient(GOOGLE_CLIENT_ID) 56 | 57 | @app.route("/loginGoogle") 58 | def loginGoogle(): 59 | # takeout auth endpoint url from google login 60 | google_provider_cfg = get_google_provider_cfg() 61 | 62 | authorization_endpoint = google_provider_cfg["authorization_endpoint"] 63 | # construct the request uri 64 | request_uri = client_google.prepare_request_uri( 65 | authorization_endpoint, 66 | redirect_uri=request.base_url + "/callbackGoogle", 67 | scope=["openid", "email", "profile"], 68 | ) 69 | 70 | return redirect(request_uri) 71 | 72 | @app.route("/loginGoogle/callbackGoogle") 73 | def callbackGoogle(): 74 | # Get authorization code Google sent back 75 | code = request.args.get("code") 76 | 77 | # Extract the URL to hit to get tokens 78 | # that allows to ask things on behalf of a user 79 | google_provider_cfg = get_google_provider_cfg() 80 | token_endpoint = google_provider_cfg["token_endpoint"] 81 | 82 | # prepare a request to get tokens 83 | token_url, headers, body = client_google.prepare_token_request( 84 | token_endpoint, 85 | authorization_response=request.url, 86 | redirect_url=request.base_url, 87 | code=code, 88 | ) 89 | 90 | # send the request and get the response 91 | token_response = requests.post( 92 | token_url, 93 | headers=headers, 94 | data=body, 95 | auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET), 96 | ) 97 | 98 | # parse the token response 99 | client_google.parse_request_body_response(json.dumps(token_response.json())) 100 | 101 | # Now we already have necessary tokens 102 | # lets ask Google for required informations 103 | userinfo_endpoint = google_provider_cfg["userinfo_endpoint"] 104 | uri, headers, body = client_google.add_token(userinfo_endpoint) 105 | 106 | userinfo_response = requests.get(uri, headers=headers, data=body).json() 107 | stop_login() 108 | 109 | # parse the information 110 | if userinfo_response.get("email_verified"): 111 | Clock.schedule_once(lambda *args: event_success_listener( 112 | userinfo_response["name"], 113 | userinfo_response["email"], 114 | userinfo_response["picture"], 115 | ), 0) 116 | 117 | return "

Logged in using Google. Return back to the Kivy application

" 118 | 119 | event_error_listener() 120 | return "User Email not available or not verified" 121 | 122 | def login_google(): 123 | if is_connected(): 124 | start_server(port) 125 | webbrowser.open("https://127.0.0.1:{}/loginGoogle".format(port), 1, False) 126 | 127 | else: 128 | event_error_listener() 129 | 130 | def logout_google(after_logout): 131 | """ 132 | Logout from google login 133 | 134 | :param: `after_logout` - Function to be called after logging out 135 | """ 136 | after_logout() 137 | -------------------------------------------------------------------------------- /kivyauth/desktop/twitter_auth.py: -------------------------------------------------------------------------------- 1 | __all__ = ("initialize_twitter", "login_twitter", "logout_twitter") 2 | 3 | 4 | def initialize_twitter( 5 | success_listener, error_listener, client_id=None, client_secret=None 6 | ): 7 | raise NotImplementedError 8 | 9 | 10 | def login_twitter(): 11 | raise NotImplementedError 12 | 13 | 14 | def logout_twitter(): 15 | raise NotImplementedError 16 | 17 | # # twitter configuration 18 | # TWITTER_CLIENT_ID = "" 19 | # TWITTER_CLIENT_SECRET = "" 20 | # tw_authorization_endpoint = "https://api.twitter.com/oauth/authorize" 21 | # tw_token_endpoint = "https://api.twitter.com/oauth/access_token" 22 | # tw_userinfo_endpoint = "https://api.twitter.com/user" 23 | # tw_request_token_endpoint = "https://api.twitter.com/oauth/request_token" 24 | 25 | # @app.route("/loginTwitter") 26 | # def loginTwitter(): 27 | 28 | # # construct the request uri 29 | # # request_uri = client_twitter.prepare_request_uri( 30 | # # tw_authorization_endpoint, 31 | # # redirect_uri=request.base_url + "/callbackTwitter", 32 | # # ) 33 | # # return redirect(request_uri) 34 | 35 | # resp = requests.post( 36 | # tw_request_token_endpoint, 37 | # { 38 | # "oauth_callback": "https%3A%2F%2F127.0.0.1%3A9004%2FloginTwitter%2FcallbackTwitter" 39 | # }, 40 | # ) 41 | # return resp.json() 42 | 43 | 44 | # @app.route("/loginTwitter/callbackTwitter") 45 | # def callbackTwitter(): 46 | # code = request.args.get("code") 47 | 48 | # token_url, headers, body = client_twitter.prepare_token_request( 49 | # tw_token_endpoint, 50 | # client_id=TWITTER_CLIENT_ID, 51 | # client_secret=TWITTER_CLIENT_SECRET, 52 | # code=code, 53 | # redirect_url=request.base_url, 54 | # ) 55 | 56 | # # send the request and get the response 57 | # token_response = requests.post(token_url, headers=headers, data=body) 58 | 59 | # # for some reason, token_response.json() is raising an error. So gotta do it manually 60 | # token_info = re.search( 61 | # "^access_token=(.*)&scope=.*&token_type=(.*)$", token_response.text 62 | # ) 63 | # uri = tw_userinfo_endpoint 64 | # headers = {"Authorization": token_info.group(2) + " " + token_info.group(1)} 65 | # body = None 66 | 67 | # global userinfo_response 68 | # userinfo_response = requests.get(uri, headers=headers, data=body).json() 69 | 70 | # # parse the information 71 | # if userinfo_response.get("id"): 72 | # close_server() 73 | # print(get_userinfo()) 74 | # print(get_userinfo()) 75 | # # return update_database( 76 | # # tmp["id"], tmp["email"], tmp["picture"]["data"]["url"], tmp["name"] 77 | # # ) 78 | # return "Success using twitter. Return to the app" 79 | # return "User Email not available or not verified", 400 80 | 81 | 82 | # if __name__ == "__main__": 83 | 84 | # if is_connected(): 85 | # port = 9004 86 | # start_server(port) 87 | 88 | # webbrowser.open("https://127.0.0.1:{}/loginGoogle".format(port), 1, False) 89 | 90 | # else: 91 | # print("Could not connect. Check connection and try again") 92 | -------------------------------------------------------------------------------- /kivyauth/desktop/utils.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, redirect 2 | import requests 3 | import os 4 | import threading 5 | import socket 6 | import random 7 | 8 | app = Flask("Local Server: KivyAuth Login") 9 | app.secret_key = os.urandom(26) 10 | 11 | PATH = os.path.dirname(__file__) 12 | 13 | port = 9004 14 | ran_num = random.randint(1111, 9999) 15 | 16 | 17 | def _start_server(*args): 18 | try: 19 | app.run(host="127.0.0.1", port=port, ssl_context='adhoc') 20 | except OSError as e: 21 | print(e) 22 | 23 | def start_server(port): 24 | thread = threading.Thread(target=_start_server) 25 | 26 | thread.start() 27 | 28 | 29 | @app.route("/kill{}".format(ran_num)) 30 | def close_server(*args, **kwargs): 31 | func = request.environ.get("werkzeug.server.shutdown") 32 | if func is None: 33 | raise RuntimeError("Not running with the Werkzeug Server") 34 | func() 35 | 36 | return "" 37 | 38 | def stop_login(*args): 39 | _close_server_pls(port) 40 | 41 | def _close_server_pls(port, *args): 42 | try: 43 | requests.get("https://127.0.0.1:{}/kill{}".format(port, ran_num), verify=False) 44 | except requests.exceptions.ConnectionError: 45 | pass 46 | 47 | def is_connected(): 48 | try: 49 | socket.create_connection(("www.google.com", 80)) 50 | return True 51 | except OSError: 52 | pass 53 | return False 54 | -------------------------------------------------------------------------------- /kivyauth/facebook_auth.py: -------------------------------------------------------------------------------- 1 | from kivy.utils import platform 2 | 3 | if platform == "android": 4 | from kivyauth.android.facebook_auth import ( 5 | initialize_fb, 6 | login_facebook, 7 | logout_facebook, 8 | ) 9 | 10 | elif platform != "ios": 11 | from kivyauth.desktop.facebook_auth import ( 12 | initialize_fb, 13 | login_facebook, 14 | logout_facebook, 15 | ) 16 | -------------------------------------------------------------------------------- /kivyauth/github_auth.py: -------------------------------------------------------------------------------- 1 | from kivy.utils import platform 2 | 3 | if platform == "android": 4 | from kivyauth.android.github_auth import ( 5 | initialize_github, 6 | login_github, 7 | logout_github, 8 | ) 9 | 10 | elif platform != "ios": 11 | from kivyauth.desktop.github_auth import ( 12 | initialize_github, 13 | login_github, 14 | logout_github, 15 | ) 16 | -------------------------------------------------------------------------------- /kivyauth/google_auth.py: -------------------------------------------------------------------------------- 1 | from kivy.utils import platform 2 | 3 | if platform == "android": 4 | from kivyauth.android.google_auth import ( 5 | initialize_google, 6 | login_google, 7 | logout_google, 8 | ) 9 | 10 | elif platform != "ios": 11 | from kivyauth.desktop.google_auth import ( 12 | initialize_google, 13 | login_google, 14 | logout_google, 15 | ) 16 | -------------------------------------------------------------------------------- /kivyauth/twitter_auth.py: -------------------------------------------------------------------------------- 1 | from kivy.utils import platform 2 | 3 | if platform == "android": 4 | from kivyauth.android.twitter_auth import ( 5 | initialize_twitter, 6 | login_twitter, 7 | logout_twitter, 8 | ) 9 | 10 | elif platform != "ios": 11 | from kivyauth.desktop.twitter_auth import ( 12 | initialize_twitter, 13 | login_twitter, 14 | logout_twitter, 15 | ) 16 | -------------------------------------------------------------------------------- /kivyauth/utils.py: -------------------------------------------------------------------------------- 1 | from kivy.utils import platform 2 | 3 | if platform == "android": 4 | 5 | def stop_login(*args): 6 | pass 7 | 8 | 9 | elif platform != "ios": 10 | from kivyauth.desktop.utils import stop_login 11 | 12 | 13 | class LoginProviders: 14 | google = "google" 15 | facebook = "facebook" 16 | github = "github" 17 | twitter = "twitter" 18 | 19 | 20 | login_providers = LoginProviders() 21 | 22 | 23 | def auto_login(provider): 24 | """ 25 | Auto login using a given provider. You may call it `on_start`. 26 | 27 | :param: `provider` is one of `kivyauth.login_providers` 28 | """ 29 | if platform == "android": 30 | if provider == login_providers.google: 31 | from kivyauth.android.google_auth import auto_google 32 | 33 | return auto_google() 34 | 35 | if provider == login_providers.facebook: 36 | from kivyauth.android.facebook_auth import auto_facebook 37 | 38 | return auto_facebook() 39 | 40 | if provider == login_providers.github: 41 | from kivyauth.android.github_auth import auto_github 42 | 43 | return auto_github() 44 | 45 | if provider == login_providers.twitter: 46 | from kivyauth.android.twitter_auth import auto_twitter 47 | 48 | return auto_twitter() 49 | 50 | else: 51 | raise NotImplementedError("Not yet availabe for desktop") 52 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import os, re 3 | 4 | with open("README.md", "r") as fh: 5 | long_description = fh.read() 6 | 7 | 8 | def is_android(): 9 | if "ANDROID_BOOTLOGO" in os.environ: 10 | return True 11 | return False 12 | 13 | 14 | def get_version() -> str: 15 | """Get __version__ from __init__.py file.""" 16 | version_file = os.path.join(os.path.dirname(__file__), "kivyauth", "__init__.py") 17 | version_file_data = open(version_file, "rt", encoding="utf-8").read() 18 | version_regex = r"(?<=^__version__ = ['\"])[^'\"]+(?=['\"]$)" 19 | try: 20 | version = re.findall(version_regex, version_file_data, re.M)[0] 21 | return version 22 | except IndexError: 23 | raise ValueError(f"Unable to find version string in {version_file}.") 24 | 25 | 26 | setup( 27 | name="KivyAuth", 28 | version=get_version(), 29 | packages=["kivyauth"], 30 | package_data={"kivyauth": ["*.py", "desktop/*", "android/*"],}, 31 | # metadata to display on PyPI 32 | author="Shashi Ranjan", 33 | author_email="shashiranjankv@gmail.com", 34 | description="Integrate Google, Facebook, Github & Twitter login in kivy applications ", 35 | long_description=long_description, 36 | long_description_content_type="text/markdown", 37 | keywords="social-login google-login facebook-login firebase-auth kivy-application kivy python", 38 | url="https://github.com/shashi278/social-auth-kivy", 39 | classifiers=[ 40 | "Programming Language :: Python :: 3", 41 | "License :: OSI Approved :: MIT License", 42 | "Operating System :: Android", 43 | "Operating System :: Microsoft :: Windows", 44 | "Operating System :: OS Independent" 45 | ], 46 | install_requires=["kivy>=2.0.0", "oauthlib", "werkzeug==2.0.3", "flask==2.0.3", "requests"] 47 | if not is_android() 48 | else [], 49 | python_requires=">=3.6", 50 | ) 51 | --------------------------------------------------------------------------------