├── .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 | [](https://travis-ci.org/github/shashi278/social-auth-kivy/) [](https://www.python.org/downloads/release/python-360/) [](https://pypi.org/project/KivyAuth/) [](https://github.com/shashi278/social-auth-kivy/blob/master/LICENSE) [](https://pypi.org/project/KivyAuth/#modal-close) [](https://pypi.org/project/KivyAuth/) []() []()
7 |
8 | ### KivyAuth on Android
9 | 
10 |
11 |
12 |
13 | ##
14 | ### KivyAuth on Desktop
15 | 
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 | 
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 |
--------------------------------------------------------------------------------