├── .gitignore ├── .isort.cfg ├── LICENSE ├── MANIFEST.in ├── README.md ├── github_auth ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── urls.py └── views.py ├── requirements.txt └── setup.py /.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 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | multi_line_output=5 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 coogger 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 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | recursive-include github_auth * 3 | recursive-include github_auth/migrations * 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Github Auth

2 |

3 | A django application to login with github. 4 |

5 | 6 | [![MIT License](https://img.shields.io/github/license/coogger/github_auth.svg)](https://github.com/coogger/github_auth/blob/master/LICENSE) [![releases](https://img.shields.io/github/release/coogger/github_auth.svg)](https://github.com/coogger/github_auth/releases) [![last-commit](https://img.shields.io/github/last-commit/coogger/github_auth.svg)](https://github.com/coogger/github_auth/commits/master) [![Codacy Badge](https://img.shields.io/codacy/grade/8e73ecaa394440dfae5418bae3f71363)](https://app.codacy.com/manual/hakancelik96/github_auth) [![style](https://img.shields.io/badge/style-black-black)](https://github.com/psf/black) [![style](https://img.shields.io/badge/style-isort-lightgrey)](https://github.com/timothycrosley/isort) [![style](https://img.shields.io/badge/style-unimport-green)](https://github.com/coogger/github_auth) [![](https://img.shields.io/github/contributors/coogger/github_auth)](https://github.com/coogger/github_auth/graphs/contributors) [![](https://pepy.tech/badge/github-auth)](https://pepy.tech/badge/github-auth) 7 | 8 | ### 🚀 Installation and Usage 🚀 9 | 10 | #### Installation 11 | - Github Aauth can be installed by running `pip install github_auth`. 12 | 13 | - Add "github_auth" to your INSTALLED_APPS setting: 14 | 15 | ```python 16 | INSTALLED_APPS = [] 17 | # … 18 | "github_auth", 19 | ] 20 | ``` 21 | 22 | - Set login and logout redirect url 23 | 24 | ```python 25 | LOGIN_REDIRECT_URL = "/" # after users login, they will redirect this url 26 | LOGOUT_REDIRECT_URL = "/" # after users logout 27 | ``` 28 | 29 | - Set your Github app configuration 30 | 31 | ```python 32 | GITHUB_AUTH = dict( 33 | redirect_uri="your_redirect_uri", 34 | scope="your scope", 35 | client_secret="your github client_secret", 36 | client_id="your github client_id", 37 | ) 38 | ``` 39 | 40 | #### Usage 41 | 42 | - In your **myapp/urls.py**: 43 | 44 | ```python 45 | 46 | urlpatterns = [ 47 | ... 48 | path("accounts/github/", include('github_auth.urls')), 49 | ] 50 | ``` 51 | 52 | - In your **myapp/templates**: 53 | 54 | ```html 55 | login wia github 56 | ``` 57 | 58 | - Github Auth extend default user model using `OneToOneField` so you can use as below. 59 | 60 | > after login to show profile image from github profile image 61 | 62 | ```html 63 | 64 |

{{request.user.github_auth.get_extra_data_as_dict.bio}}

65 | ``` 66 | 67 | ```python 68 | request.user.github_auth.get_extra_data_as_dict.bio # and other extra_data fields 69 | request.user.github_auth.avatar_url 70 | ``` 71 | 72 | or 73 | 74 | ```python 75 | from django.contrip.auth.models import User 76 | 77 | admin_avatar_url = User.objects.get(username="admin").github_auth.avatar_url 78 | 79 | ``` 80 | 81 | and you can use `next` field to redirect any addresses for example; 82 | The user will be redirected to the same address after login. 83 | 84 | ```html 85 | login wia github 86 | ``` 87 | 88 | ## Author / Social 89 | 90 | 👤 **Hakan Çelik** 👤 91 | 92 | - [![](https://img.shields.io/twitter/follow/hakancelik96?style=social)](https://twitter.com/hakancelik96) 93 | - [![](https://img.shields.io/github/followers/hakancelik96?label=hakancelik96&style=social)](https://github.com/hakancelik96) 94 | 95 | 96 | ## Version Notes 97 | 98 | ### V0.0.7 99 | - Next attr added when login or logout 100 | - Get email bug fixed 101 | - Migrations files added 102 | 103 | 104 | ### V0.0.5 105 | - View ( update githubuser ) bug fixed 106 | 107 | ### V0.0.2 108 | - login 109 | - logout 110 | - get extra data 111 | -------------------------------------------------------------------------------- /github_auth/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin import ModelAdmin, site 2 | 3 | from .models import GithubAuthUser 4 | 5 | 6 | class GithubAuthUserAdmin(ModelAdmin): 7 | list_display = ["user"] 8 | list_display_links = list_display 9 | search_fields = list_display 10 | fields = ["user", "code", "access_token", "extra_data"] 11 | 12 | 13 | site.register(GithubAuthUser, GithubAuthUserAdmin) 14 | -------------------------------------------------------------------------------- /github_auth/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class GithubAuthConfig(AppConfig): 5 | name = "github_auth" 6 | -------------------------------------------------------------------------------- /github_auth/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.12 on 2019-09-02 20:21 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name="GithubAuthUser", 17 | fields=[ 18 | ( 19 | "id", 20 | models.AutoField( 21 | auto_created=True, 22 | primary_key=True, 23 | serialize=False, 24 | verbose_name="ID", 25 | ), 26 | ), 27 | ( 28 | "code", 29 | models.CharField( 30 | help_text="github user code / to get access_token", 31 | max_length=500, 32 | ), 33 | ), 34 | ( 35 | "access_token", 36 | models.CharField( 37 | help_text="github user access_token to any api operations", 38 | max_length=500, 39 | ), 40 | ), 41 | ("extra_data", models.TextField()), 42 | ( 43 | "user", 44 | models.OneToOneField( 45 | on_delete=django.db.models.deletion.CASCADE, 46 | to=settings.AUTH_USER_MODEL, 47 | ), 48 | ), 49 | ], 50 | ) 51 | ] 52 | -------------------------------------------------------------------------------- /github_auth/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coogger/github_auth/ff09a4c4890312f2fee46cf0b2c550510f4336e3/github_auth/migrations/__init__.py -------------------------------------------------------------------------------- /github_auth/models.py: -------------------------------------------------------------------------------- 1 | import ast 2 | 3 | from django.conf import settings 4 | from django.contrib.auth.models import User 5 | from django.db import models 6 | from django.utils.translation import gettext as _ 7 | 8 | AUTH_USER_MODEL = getattr(settings, "AUTH_USER_MODEL", User) 9 | 10 | 11 | class GithubAuthUser(models.Model): 12 | user = models.OneToOneField(AUTH_USER_MODEL, on_delete=models.CASCADE) 13 | code = models.CharField( 14 | max_length=500, help_text=_("github user code / to get access_token") 15 | ) 16 | access_token = models.CharField( 17 | max_length=500, help_text=_("github user access_token to any api operations") 18 | ) 19 | extra_data = models.TextField() 20 | 21 | def __str__(self): 22 | return str(self.user) 23 | 24 | @property 25 | def get_extra_data_as_dict(self): 26 | return ast.literal_eval(self.extra_data) 27 | 28 | @property 29 | def avatar_url(self): 30 | return self.get_extra_data_as_dict.get("avatar_url") 31 | 32 | @property 33 | def username(self): 34 | return self.user.username 35 | -------------------------------------------------------------------------------- /github_auth/urls.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from django.conf import settings 3 | from django.shortcuts import redirect 4 | from django.urls import path 5 | 6 | from .views import Github, Logout 7 | 8 | 9 | def redirect_github(requests): 10 | github_params = settings.GITHUB_AUTH 11 | redirect_uri = ( 12 | github_params.get("redirect_uri") + f"?next={requests.GET.get('next')}" 13 | ) 14 | scope = github_params.get("scope") 15 | client_secret = github_params.get("client_secret") 16 | client_id = github_params.get("client_id") 17 | return redirect( 18 | f"https://github.com/login/oauth/authorize?redirect_uri={redirect_uri}&scope={scope}&client_secret={client_secret}&client_id={client_id}" 19 | ) 20 | 21 | 22 | urlpatterns = [ 23 | path("login/", Github.as_view(), name="login-via-github"), 24 | path("logout/", Logout.as_view(), name="logout"), 25 | path("", redirect_github, name="redirect-github"), 26 | ] 27 | -------------------------------------------------------------------------------- /github_auth/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | from django.conf import settings 5 | from django.contrib import messages 6 | from django.contrib.auth import login, logout 7 | from django.contrib.auth.models import User 8 | from django.db.utils import IntegrityError 9 | from django.http import Http404, HttpResponseRedirect 10 | from django.views import View 11 | 12 | from .models import GithubAuthUser 13 | 14 | github_params = settings.GITHUB_AUTH 15 | 16 | 17 | class Github(View): 18 | access_token_api_url = "https://github.com/login/oauth/access_token" 19 | user_api_url = "https://api.github.com/user" 20 | 21 | def get(self, request, *args, **kwargs): 22 | code = request.GET.get("code", None) 23 | if code is None: 24 | raise Http404 25 | github_params.__setitem__("code", code) 26 | r = requests.post(self.access_token_api_url, params=github_params) 27 | access_token = self.convert_json(r.text).get("access_token", None) 28 | if access_token is None: 29 | raise Http404 30 | extra_data = requests.get( 31 | self.user_api_url, headers={"Authorization": f"token {access_token}"} 32 | ) 33 | extra_data = json.loads(extra_data.text) 34 | username = extra_data.get("login") 35 | email = extra_data.get("email") 36 | user, created = User.objects.get_or_create(username=username) 37 | if created: 38 | GithubAuthUser( 39 | user=user, code=code, access_token=access_token, extra_data=extra_data 40 | ).save() 41 | else: 42 | github_user = GithubAuthUser.objects.get(user=user) 43 | github_user.code = code 44 | github_user.access_token = access_token 45 | github_user.extra_data = extra_data 46 | github_user.save() 47 | login(request, user, backend="django.contrib.auth.backends.ModelBackend") 48 | user.email = email 49 | try: 50 | user.save() 51 | except IntegrityError: 52 | pass 53 | return HttpResponseRedirect( 54 | request.GET.get("next", settings.LOGIN_REDIRECT_URL) 55 | ) 56 | 57 | @staticmethod 58 | def convert_json(text): 59 | payload_to_json = dict() 60 | for i in text.split("&"): 61 | key, value = i.split("=") 62 | payload_to_json[key] = value 63 | return payload_to_json 64 | 65 | 66 | class Logout(View): 67 | success = "See you again {}" 68 | 69 | def get(self, request, *args, **kwargs): 70 | if request.user.is_authenticated: 71 | messages.success(request, self.success.format(request.user)) 72 | logout(request) 73 | return HttpResponseRedirect( 74 | request.GET.get("next", settings.LOGOUT_REDIRECT_URL) 75 | ) 76 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==2.2.10 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="github_auth", 5 | version="0.0.7", 6 | description="A django application to login via github.", 7 | author="hakancelik96", 8 | author_email="hakancelik96@outlook.com", 9 | packages=["github_auth"], 10 | include_package_data=True, 11 | install_requires=[], 12 | url="https://github.com/djangoapps/github_auth", 13 | license="MIT", 14 | zip_safe=False, 15 | keywords="github, django github, login github", 16 | classifiers=[ 17 | "Environment :: Web Environment", 18 | "Framework :: Django", 19 | "License :: OSI Approved :: MIT License", 20 | "Operating System :: OS Independent", 21 | "Programming Language :: Python :: 3.5", 22 | "Topic :: Internet :: WWW/HTTP", 23 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 24 | ], 25 | ) 26 | --------------------------------------------------------------------------------