Skip to content

Commit 33add8f

Browse files
author
mominur-helios
committed
add github social auth
1 parent 6122c6f commit 33add8f

File tree

10 files changed

+121
-116
lines changed

10 files changed

+121
-116
lines changed
Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 3.2.5 on 2021-07-15 11:29
1+
# Generated by Django 3.2.13 on 2022-12-06 11:33
22

33
from django.conf import settings
44
from django.db import migrations, models
@@ -15,26 +15,12 @@ class Migration(migrations.Migration):
1515

1616
operations = [
1717
migrations.CreateModel(
18-
name="ActiveSession",
18+
name='ActiveSession',
1919
fields=[
20-
(
21-
"id",
22-
models.BigAutoField(
23-
auto_created=True,
24-
primary_key=True,
25-
serialize=False,
26-
verbose_name="ID",
27-
),
28-
),
29-
("token", models.CharField(max_length=255)),
30-
("date", models.DateTimeField(auto_now_add=True)),
31-
(
32-
"user",
33-
models.ForeignKey(
34-
on_delete=django.db.models.deletion.CASCADE,
35-
to=settings.AUTH_USER_MODEL,
36-
),
37-
),
20+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21+
('token', models.CharField(max_length=255)),
22+
('date', models.DateTimeField(auto_now_add=True)),
23+
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
3824
],
3925
),
4026
]

api/authentication/serializers/login.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from api.authentication.models import ActiveSession
99

1010

11+
1112
def _generate_jwt_token(user):
1213
token = jwt.encode(
1314
{"id": user.pk, "exp": datetime.utcnow() + timedelta(days=7)},
@@ -61,3 +62,8 @@ def validate(self, data):
6162
"token": session.token,
6263
"user": {"_id": user.pk, "username": user.username, "email": user.email},
6364
}
65+
66+
67+
class GithubSerializer(serializers.Serializer):
68+
code = serializers.CharField(max_length=255)
69+
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import requests
2+
import jwt
3+
4+
from rest_framework import viewsets
5+
from rest_framework.response import Response
6+
from rest_framework.permissions import AllowAny
7+
from django.conf import settings
8+
from django.core.exceptions import ObjectDoesNotExist
9+
10+
from api.authentication.serializers.login import GithubSerializer, _generate_jwt_token
11+
from api.user.models import User
12+
from api.authentication.models import ActiveSession
13+
14+
15+
class GithubSocialLogin(viewsets.ModelViewSet):
16+
http_method_names = ["post"]
17+
permission_classes = (AllowAny,)
18+
serializer_class = GithubSerializer
19+
20+
def create(self, request, *args, **kwargs):
21+
serializer = self.get_serializer(data=request.data)
22+
serializer.is_valid(raise_exception=True)
23+
24+
code = serializer.data['code']
25+
client_id = getattr(settings, 'GITHUB_CLIENT_ID')
26+
client_secret = getattr(settings, 'GITHUB_SECRET_KEY')
27+
root_url = 'https://github.com/login/oauth/access_token'
28+
29+
params = { 'client_id': client_id, 'client_secret': client_secret, 'code': code }
30+
31+
data = requests.post(root_url, params=params, headers={
32+
'Content-Type': 'application/x-www-form-urlencoded',
33+
})
34+
35+
response = data._content.decode('utf-8')
36+
access_token = response.split('&')[0].split('=')[1]
37+
38+
user_data = requests.get('https://api.github.com/user', headers={
39+
"Authorization": "Bearer " + access_token
40+
}).json()
41+
42+
if User.objects.filter(username=user_data['login'], email=user_data['email']).exists():
43+
user = User.objects.get(username=user_data['login'], email=user_data['email'])
44+
else:
45+
user = User.objects.create_user(username=user_data['login'], email=user_data['email'])
46+
47+
try:
48+
session = ActiveSession.objects.get(user=user)
49+
if not session.token:
50+
raise ValueError
51+
52+
jwt.decode(session.token, settings.SECRET_KEY, algorithms=["HS256"])
53+
54+
except (ObjectDoesNotExist, ValueError, jwt.ExpiredSignatureError):
55+
session = ActiveSession.objects.create(
56+
user=user, token=_generate_jwt_token(user)
57+
)
58+
59+
return Response({
60+
"success": True,
61+
"user": {"_id": user.pk, "username": user.username, "email": user.email, "token": session.token},
62+
})

api/routers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
ActiveSessionViewSet,
55
LogoutViewSet,
66
)
7+
from api.authentication.viewsets.social_login import GithubSocialLogin
78
from rest_framework import routers
89
from api.user.viewsets import UserViewSet
910

@@ -19,6 +20,8 @@
1920

2021
router.register(r"logout", LogoutViewSet, basename="logout")
2122

23+
router.register(r"github-login", GithubSocialLogin, basename="github-login")
24+
2225
urlpatterns = [
2326
*router.urls,
2427
]
Lines changed: 15 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 3.2.5 on 2021-07-15 11:20
1+
# Generated by Django 3.2.13 on 2022-12-06 11:33
22

33
from django.db import migrations, models
44

@@ -8,69 +8,27 @@ class Migration(migrations.Migration):
88
initial = True
99

1010
dependencies = [
11-
("auth", "0012_alter_user_first_name_max_length"),
11+
('auth', '0012_alter_user_first_name_max_length'),
1212
]
1313

1414
operations = [
1515
migrations.CreateModel(
16-
name="User",
16+
name='User',
1717
fields=[
18-
(
19-
"id",
20-
models.BigAutoField(
21-
auto_created=True,
22-
primary_key=True,
23-
serialize=False,
24-
verbose_name="ID",
25-
),
26-
),
27-
("password", models.CharField(max_length=128, verbose_name="password")),
28-
(
29-
"last_login",
30-
models.DateTimeField(
31-
blank=True, null=True, verbose_name="last login"
32-
),
33-
),
34-
(
35-
"is_superuser",
36-
models.BooleanField(
37-
default=False,
38-
help_text="Designates that this user has all permissions without explicitly assigning them.",
39-
verbose_name="superuser status",
40-
),
41-
),
42-
("username", models.CharField(db_index=True, max_length=255)),
43-
(
44-
"email",
45-
models.EmailField(db_index=True, max_length=254, unique=True),
46-
),
47-
("is_active", models.BooleanField(default=True)),
48-
("date", models.DateTimeField(auto_now_add=True)),
49-
(
50-
"groups",
51-
models.ManyToManyField(
52-
blank=True,
53-
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
54-
related_name="user_set",
55-
related_query_name="user",
56-
to="auth.Group",
57-
verbose_name="groups",
58-
),
59-
),
60-
(
61-
"user_permissions",
62-
models.ManyToManyField(
63-
blank=True,
64-
help_text="Specific permissions for this user.",
65-
related_name="user_set",
66-
related_query_name="user",
67-
to="auth.Permission",
68-
verbose_name="user permissions",
69-
),
70-
),
18+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19+
('password', models.CharField(max_length=128, verbose_name='password')),
20+
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
21+
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
22+
('username', models.CharField(db_index=True, max_length=255, unique=True)),
23+
('email', models.EmailField(db_index=True, max_length=254, unique=True)),
24+
('is_active', models.BooleanField(default=True)),
25+
('is_staff', models.BooleanField(default=False)),
26+
('date', models.DateTimeField(auto_now_add=True)),
27+
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
28+
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
7129
],
7230
options={
73-
"abstract": False,
31+
'abstract': False,
7432
},
7533
),
7634
]

api/user/migrations/0002_user_is_staff.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

api/user/migrations/0003_alter_user_username.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

core/settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
# Build paths inside the project like this: BASE_DIR / 'subdir'.
2222
BASE_DIR = Path(__file__).resolve().parent.parent
2323

24+
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
25+
2426
# Quick-start development settings - unsuitable for production
2527
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
2628

@@ -176,3 +178,7 @@
176178

177179
TESTING = False
178180
TEST_RUNNER = "core.test_runner.CoreTestRunner"
181+
182+
# GitHub social authentication
183+
GITHUB_CLIENT_ID = env('GITHUB_CLIENT_ID')
184+
GITHUB_SECRET_KEY = env('GITHUB_SECRET_KEY')

core/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from django.urls import path, include
2+
from django.contrib import admin
3+
from api.authentication.viewsets.social_login import GithubSocialLogin
24

35
urlpatterns = [
6+
path('admin/', admin.site.urls),
47
path("api/users/", include(("api.routers", "api"), namespace="api")),
58
]

requirements.txt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1+
asgiref==3.5.2
2+
certifi==2022.9.24
3+
cffi==1.15.1
4+
charset-normalizer==2.1.1
5+
cryptography==38.0.4
6+
defusedxml==0.7.1
17
Django==3.2.13
2-
djangorestframework==3.13.1
3-
PyJWT==2.4.0
48
django-cors-headers==3.13.0
5-
gunicorn==20.1.0
69
django-environ==0.8.1
10+
djangorestframework==3.13.1
11+
gunicorn==20.1.0
12+
idna==3.4
13+
oauthlib==3.2.2
14+
pycparser==2.21
15+
PyJWT==2.4.0
16+
python3-openid==3.2.0
17+
pytz==2022.6
18+
requests==2.28.1
19+
requests-oauthlib==1.3.1
20+
six==1.16.0
21+
social-auth-core==4.3.0
22+
sqlparse==0.4.3
23+
urllib3==1.26.13

0 commit comments

Comments
 (0)