Skip to content

Commit 734a003

Browse files
authored
Merge pull request #1 from Valdemal/social-auth
Social auth
2 parents 5affad3 + 5665f74 commit 734a003

File tree

15 files changed

+189
-46
lines changed

15 files changed

+189
-46
lines changed

client/main/oauth_backend.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import requests
2+
from django.conf import settings
3+
from social_core.backends.oauth import BaseOAuth2
4+
5+
6+
class DjangoOAuthToolkitBackend(BaseOAuth2):
7+
name = 'django'
8+
AUTHORIZATION_URL = settings.OAUTH2_BACKEND['AUTHORIZATION_URL']
9+
ACCESS_TOKEN_URL = settings.OAUTH2_BACKEND['ACCESS_TOKEN_URL']
10+
USER_DETAILS_URL = settings.OAUTH2_BACKEND['USER_DETAILS_URL']
11+
ACCESS_TOKEN_METHOD = 'POST'
12+
13+
def get_user_details(self, response):
14+
resp = requests.get(self.USER_DETAILS_URL, headers={
15+
'Authorization': f'Bearer {response["access_token"]}',
16+
})
17+
18+
resp.raise_for_status()
19+
20+
user_details = resp.json()
21+
user_details['fullname'] = user_details['first_name'] + ' ' + user_details['last_name']
22+
23+
return user_details
24+
25+
def get_key_and_secret(self):
26+
return settings.OAUTH2_BACKEND['CLIENT_ID'], settings.OAUTH2_BACKEND['CLIENT_SECRET']
27+
28+
# todo: Нужно будет получать uid
29+
def get_user_id(self, details, response):
30+
return details['id']
31+
32+
33+
34+
35+

client/main/settings/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .general import *
2+
from .authentication import *
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
LOGIN_URL = 'social/login/django/'
2+
3+
SOCIAL_AUTH_URL_NAMESPACE = 'social'
4+
5+
AUTHENTICATION_BACKENDS = (
6+
"main.oauth_backend.DjangoOAuthToolkitBackend",
7+
"django.contrib.auth.backends.ModelBackend"
8+
)
9+
10+
OAUTH2_BACKEND = {
11+
'CLIENT_ID': 'r3CuSXmOtHOGn0XWTy1MmmwzT3RI7I0Hu4i6LIqP',
12+
'CLIENT_SECRET': 'client_secret&%$=',
13+
'AUTHORIZATION_URL': 'http://127.0.0.1:8000/oauth/authorize/',
14+
'ACCESS_TOKEN_URL': 'http://provider-nginx:8000/oauth/token/',
15+
'USER_DETAILS_URL': 'http://provider-nginx:8000/api/users/me/'
16+
}

client/main/settings.py renamed to client/main/settings/general.py

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
from pathlib import Path
33

4-
BASE_DIR = Path(__file__).resolve().parent.parent
4+
BASE_DIR = Path(__file__).resolve().parent.parent.parent
55

66
SECRET_KEY = 'django-insecure-gr5(f&_g8bi6$y355u(c6p#ys83!t4*^45+i(jfa($83b=ljng'
77

@@ -16,17 +16,15 @@
1616
'django.contrib.sessions',
1717
'django.contrib.messages',
1818
'django.contrib.staticfiles',
19-
'oauth2_provider',
19+
20+
# Сторонние пакеты
2021
'rest_framework',
2122
'drf_spectacular',
22-
]
2323

24-
OAUTH2_PROVIDER = {
25-
'RESOURCE_SERVER_INTROSPECTION_URL': 'http://provider-nginx:8000/oauth/introspect/',
26-
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (
27-
os.getenv('CLIENT_ID'), os.getenv('CLIENT_SECRET')
28-
)
29-
}
24+
# OAuth
25+
'oauth2_provider',
26+
'social_django',
27+
]
3028

3129
REST_FRAMEWORK = {
3230
'DEFAULT_AUTHENTICATION_CLASSES': (
@@ -35,6 +33,8 @@
3533
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
3634
}
3735

36+
SESSION_COOKIE_NAME = 'clientsessionid'
37+
3838
SPECTACULAR_SETTINGS = {
3939
'TITLE': 'OAuth 2.0 Client API',
4040
'VERSION': '1.0.0',
@@ -69,14 +69,16 @@
6969
TEMPLATES = [
7070
{
7171
'BACKEND': 'django.template.backends.django.DjangoTemplates',
72-
'DIRS': [],
72+
'DIRS': [os.path.join(BASE_DIR, 'templates')],
7373
'APP_DIRS': True,
7474
'OPTIONS': {
7575
'context_processors': [
7676
'django.template.context_processors.debug',
7777
'django.template.context_processors.request',
7878
'django.contrib.auth.context_processors.auth',
7979
'django.contrib.messages.context_processors.messages',
80+
'social_django.context_processors.backends',
81+
'social_django.context_processors.login_redirect',
8082
],
8183
},
8284
},
@@ -97,20 +99,6 @@
9799
# Password validation
98100
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
99101

100-
AUTH_PASSWORD_VALIDATORS = [
101-
{
102-
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
103-
},
104-
{
105-
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
106-
},
107-
{
108-
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
109-
},
110-
{
111-
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
112-
},
113-
]
114102

115103
# Internationalization
116104
# https://docs.djangoproject.com/en/4.1/topics/i18n/

client/main/urls.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
44
from rest_framework import routers
55

6-
from main.views import UserViewSet
6+
from .views import UserViewSet, index, logout
77

88
router = routers.DefaultRouter()
99
router.register(r'users', UserViewSet)
1010

1111
urlpatterns = [
12+
path('', index, name='index'),
1213
path('admin/', admin.site.urls),
13-
path('api/', include(router.urls)),
14+
path('api/', include(router.urls), name='api'),
15+
path('social/', include('social_django.urls'), name='social'),
16+
path('logout/', logout, name='logout')
1417
]
1518

1619
urlpatterns += [

client/main/views.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
from django.contrib.auth import logout as auth_logout
2+
from django.contrib.auth.decorators import login_required
13
from django.contrib.auth.models import User
4+
from django.shortcuts import render, redirect
25
from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope
36
from rest_framework import viewsets, permissions
47

5-
from main.serializers import UserSerializer
8+
from .serializers import UserSerializer
69

710

811
class UserViewSet(viewsets.ModelViewSet):
@@ -14,3 +17,13 @@ class UserViewSet(viewsets.ModelViewSet):
1417
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
1518
required_scopes = ['test']
1619

20+
21+
@login_required
22+
def index(request):
23+
return render(request, 'index.html', context={'user': request.user})
24+
25+
26+
def logout(request):
27+
"""Logs out user"""
28+
auth_logout(request)
29+
return redirect('index')

client/templates/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Клиент OAuth</title>
6+
</head>
7+
<body>
8+
<h1>Клиент OAuth</h1>
9+
<hr>
10+
<h3>Данные пользователя</h3>
11+
<p>Логин: {{ user.username }}</p>
12+
<p>Имя: {{ user.first_name }}</p>
13+
<p>Фамилия: {{ user.last_name }}</p>
14+
<hr>
15+
<a href="{% url 'docs' %}">API</a>
16+
<a href="{% url 'admin:index' %}">admin</a>
17+
<a href="{% url 'logout' %}">Выйти</a>
18+
</body>
19+
</html>

docker-compose.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ services:
88
command: gunicorn main.wsgi:application --bind 0.0.0.0:8000
99
volumes:
1010
- ./provider:/usr/src/provider
11+
- static_volume:/usr/src/static
1112
expose:
1213
- 8000
1314

1415
provider-nginx:
1516
container_name: oauth-example-provider-nginx
1617
build: ./provider/nginx
18+
volumes:
19+
- static_volume:/usr/src/static
1720
ports:
1821
- "8000:8000"
1922
depends_on:
@@ -27,7 +30,11 @@ services:
2730
command: python manage.py runserver 0.0.0.0:80
2831
volumes:
2932
- ./client:/usr/src/client
33+
- static_volume:/usr/src/static
3034
ports:
3135
- "80:80"
3236
depends_on:
3337
- provider
38+
39+
volumes:
40+
static_volume:

provider/main/serializers.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from django.contrib.auth.models import User
2+
from rest_framework import serializers
3+
4+
5+
class UserSerializer(serializers.HyperlinkedModelSerializer):
6+
class Meta:
7+
model = User
8+
fields = ['username', 'email', 'first_name', 'last_name', 'is_staff', 'id']

provider/main/settings.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from pathlib import Path
23

34
# Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -24,6 +25,8 @@
2425

2526
'rest_framework',
2627
'corsheaders',
28+
'djoser',
29+
'drf_spectacular',
2730

2831
'oauth2_provider',
2932
)
@@ -44,16 +47,14 @@
4447
TEMPLATES = [
4548
{
4649
'BACKEND': 'django.template.backends.django.DjangoTemplates',
47-
'DIRS': [],
50+
'DIRS': [os.path.join(BASE_DIR, 'templates')],
4851
'APP_DIRS': True,
4952
'OPTIONS': {
5053
'context_processors': [
5154
'django.template.context_processors.debug',
5255
'django.template.context_processors.request',
5356
'django.contrib.auth.context_processors.auth',
5457
'django.contrib.messages.context_processors.messages',
55-
'social_django.context_processors.backends',
56-
'social_django.context_processors.login_redirect',
5758
],
5859
},
5960
},
@@ -98,6 +99,7 @@
9899
'test': 'test api',
99100
'introspection': 'Introspect token Scope',
100101
},
102+
'PKCE_REQUIRED': False,
101103
}
102104

103105
REST_FRAMEWORK = {
@@ -106,6 +108,22 @@
106108
'rest_framework.authentication.SessionAuthentication',
107109
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
108110
],
111+
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
112+
}
113+
114+
DJOSER = {
115+
'SERIALIZERS': {
116+
'user': 'main.serializers.UserSerializer',
117+
'current_user': 'main.serializers.UserSerializer',
118+
}
119+
}
120+
121+
SPECTACULAR_SETTINGS = {
122+
'TITLE': 'OAuth 2.0 Provider API',
123+
'VERSION': '1.0.0',
124+
'SERVE_INCLUDE_SCHEMA': False,
125+
'COMPONENT_SPLIT_REQUEST': True,
126+
'SCHEMA_PATH_PREFIX': '/api/',
109127
}
110128

111129
LANGUAGE_CODE = 'ru'
@@ -117,9 +135,11 @@
117135
USE_TZ = True
118136

119137
STATIC_URL = 'static/'
138+
STATIC_ROOT = BASE_DIR.parent / 'static/'
139+
120140

121141
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
122142

123143
CORS_ALLOW_ALL_ORIGINS = True
124144

125-
CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1:8000']
145+
CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1:8000']

0 commit comments

Comments
 (0)