Skip to content

Commit f5280f6

Browse files
committed
1) Рефакторинг.
2) Авторизация через телеграм.
1 parent a7c9164 commit f5280f6

File tree

16 files changed

+138
-109
lines changed

16 files changed

+138
-109
lines changed

.env.dist

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ POSTGRES_USER=
1010
POSTGRES_PASSWORD=
1111
POSTGRES_DATABASE=
1212

13-
TELEGRAM_BOT_TOKEN=
13+
TELEGRAM_BOT_TOKEN=
14+
TELEGRAM_BOT_USERNAME=

app/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
title=settings.APP_TITLE,
1212
version=settings.APP_VERSION,
1313
description=settings.APP_DESCRIPTION,
14+
docs_url=f'{settings.APP_API_PREFIX}/docs',
1415
openapi_url=f'{settings.APP_API_PREFIX}/openapi.json',
16+
swagger_ui_oauth2_redirect_url=f'{settings.APP_API_PREFIX}/docs/oauth2-redirect',
1517
)
1618

1719
app.include_router(api.api_router, prefix=settings.APP_API_PREFIX)

app/api/deps.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,30 @@
88
from app import models
99
from app.core import exps, settings
1010
from app.core.db import Database, SessionLocal
11-
from app.core.security import JWTTokenManager
11+
from app.core.security import JWTManager, TelegramAuth
1212

1313

1414
async def get_db() -> Database:
1515
async with SessionLocal() as session:
1616
yield Database(session)
1717

1818

19-
async def get_tkn_manager() -> JWTTokenManager:
20-
return JWTTokenManager(settings.APP_SECRET_KEY)
19+
async def get_jwt_manager() -> JWTManager:
20+
return JWTManager(settings.APP_SECRET_KEY)
21+
22+
23+
async def get_telegram_auth() -> TelegramAuth:
24+
return TelegramAuth(
25+
settings.TELEGRAM_BOT_TOKEN, settings.TELEGRAM_BOT_USERNAME
26+
)
2127

2228

2329
async def get_current_user(
24-
access_token: Annotated[str, Header()],
25-
db: Annotated[Database, Depends(get_db)],
26-
tkn_manager: Annotated[JWTTokenManager, Depends(get_tkn_manager)],
30+
access_token: Annotated[str, Header()],
31+
db: Annotated[Database, Depends(get_db)],
32+
jwt_manager: Annotated[JWTManager, Depends(get_jwt_manager)],
2733
) -> models.User:
28-
payload = tkn_manager.decode_token(access_token)
34+
payload = jwt_manager.decode_token(access_token)
2935
if payload.get('type') != 'access':
3036
raise exps.TOKEN_INVALID
3137
if not (user := await db.user.read(payload.get('id'))):

app/api/endpoints/tokens/auth.py

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,41 @@
11
"""
22
User Endpoints Module
33
"""
4-
import hmac
54

65
from fastapi import APIRouter, Depends
76
from typing_extensions import Annotated
87

98
from app import models
109
from app.api import deps
11-
from app.core import exps, settings
10+
from app.core import exps
1211
from app.core.db import Database
13-
from app.core.security import JWTTokenManager
12+
from app.core.security import JWTManager, TelegramAuth
1413

15-
router = APIRouter()
14+
router = APIRouter(prefix='/auth')
1615

1716

18-
@router.post('/auth/', response_model=models.AuthToken)
19-
async def auth(
17+
@router.post('/telegram/', response_model=models.AuthToken)
18+
async def telegram(
2019
user: models.UserCreate,
2120
db: Annotated[Database, Depends(deps.get_db)],
22-
tkn_manager: Annotated[JWTTokenManager, Depends(deps.get_tkn_manager)],
21+
auth: Annotated[TelegramAuth, Depends(deps.get_telegram_auth)],
22+
jwt_manager: Annotated[JWTManager, Depends(deps.get_jwt_manager)],
2323
):
2424
"""
2525
Get auth token
2626
"""
27-
data_check_string = '\n'.join(
28-
sorted(
29-
f'{x}={y}'
30-
for x, y in user.model_dump().items()
31-
if x not in 'hash' and y is not None
32-
)
33-
)
34-
computed_hash = hmac.new(
35-
settings.telegram_bot_token_hash.digest(),
36-
data_check_string.encode(),
37-
'sha256',
38-
).hexdigest()
39-
is_correct = hmac.compare_digest(computed_hash, user.hash)
40-
if not is_correct:
27+
user_dict = user.model_dump()
28+
computed_hash = auth.generate_hash(user_dict)
29+
if not auth.is_correct(computed_hash, user.hash):
4130
raise exps.USER_IS_CORRECT
4231

4332
model_user = models.User(**user.model_dump())
44-
if cur_user := await db.user.retrieve(user.id):
33+
if cur_user := await db.user.retrieve_one(ident=user.id):
4534
await db.user.update(cur_user.id, **model_user.model_dump())
4635
else:
4736
await db.user.create(model_user)
4837
await db.session.commit()
49-
auth_token = tkn_manager.encode_token({'id': user.id, 'type': 'auth'}, 15)
50-
return models.AuthToken(auth_token=auth_token)
38+
token = models.AuthToken(token=jwt_manager.encode_token(
39+
{'id': user.id, 'type': 'auth'},15
40+
))
41+
return token

app/api/endpoints/tokens/pair.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,26 @@
44
from app import models
55
from app.api import deps
66
from app.core import exps
7-
from app.core.security import JWTTokenManager
7+
from app.core.security import JWTManager
88

99
router = APIRouter()
1010

1111

1212
@router.post('/pair/', response_model=models.PairTokens)
1313
async def new_pair_tokens(
1414
data: models.AuthToken,
15-
tkn_manager: Annotated[JWTTokenManager, Depends(deps.get_tkn_manager)],
15+
jwt_manager: Annotated[JWTManager, Depends(deps.get_jwt_manager)],
1616
):
1717
"""
1818
Get pair tokens
1919
"""
2020

21-
payload = tkn_manager.decode_token(data.auth_token)
21+
payload = jwt_manager.decode_token(data.auth_token)
2222
if payload.get('type') != 'auth':
2323
raise exps.TOKEN_INVALID
2424
payload['type'] = 'access'
25-
access_token = tkn_manager.encode_token(payload, 120)
25+
access_token = models.AccessToken(token=jwt_manager.encode_token(payload, 120))
2626
payload['type'] = 'refresh'
27-
refresh_token = tkn_manager.encode_token(payload, 1440)
28-
return models.PairTokens(
29-
access_token=access_token, refresh_token=refresh_token
30-
)
27+
refresh_token = models.RefreshToken(token=jwt_manager.encode_token(payload, 1440))
28+
tokens = models.PairTokens(access=access_token, refresh=refresh_token)
29+
return tokens

app/api/endpoints/tokens/refresh.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from app.api import deps
66
from app.core import exps
77
from app.core.db import Database
8-
from app.core.security import JWTTokenManager
8+
from app.core.security import JWTManager
99

1010
router = APIRouter()
1111

@@ -14,15 +14,14 @@
1414
async def refresh_access_token(
1515
data: models.RefreshToken,
1616
db: Annotated[Database, Depends(deps.get_db)],
17-
tkn_manager: Annotated[JWTTokenManager, Depends(deps.get_tkn_manager)],
17+
jwt_manager: Annotated[JWTManager, Depends(deps.get_jwt_manager)],
1818
):
1919
"""
2020
Get new access token
2121
"""
22-
payload = tkn_manager.decode_token(data.refresh_token)
22+
payload = jwt_manager.decode_token(data.refresh_token)
2323
if payload.get('type') != 'refresh':
2424
raise exps.TOKEN_INVALID
2525
if not await db.user.read(payload.get('id')):
2626
raise exps.USER_NOT_FOUND
27-
access_token = tkn_manager.encode_token(payload, 120)
28-
return models.AccessToken(access_token=access_token)
27+
return models.AccessToken(token=jwt_manager.encode_token(payload, 120))

app/core/security/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
from .token_manager import JWTTokenManager
1+
from .auth import TelegramAuth
2+
from .jwt_manager import JWTManager
23

3-
__all__ = ['JWTTokenManager']
4+
__all__ = ['JWTManager', 'TelegramAuth']

app/core/security/auth/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .telegram import TelegramAuth
2+
3+
__all__ = ['TelegramAuth']

app/core/security/auth/telegram.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import hashlib
2+
import hmac
3+
4+
from app.core import settings
5+
6+
7+
class TelegramAuth:
8+
def __init__(
9+
self,
10+
token: str = settings.TELEGRAM_BOT_TOKEN,
11+
username: str = settings.TELEGRAM_BOT_USERNAME,
12+
):
13+
self.token = token
14+
self.username = username
15+
16+
@property
17+
def hash(self) -> bytes:
18+
return hashlib.sha256(self.token.encode()).digest()
19+
20+
def generate_hash(self, user: dict) -> str:
21+
user.pop('hash')
22+
string = '\n'.join(f'{x}={y}' for x, y in sorted(user.items()))
23+
computed_hash = hmac.new(
24+
self.hash,
25+
string.encode(),
26+
hashlib.sha256,
27+
).hexdigest()
28+
return computed_hash
29+
30+
@classmethod
31+
def is_correct(cls, computed_hash: str, user_hash: str) -> bool:
32+
return hmac.compare_digest(computed_hash, user_hash)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .jwt_manager import JWTManager
2+
3+
__all__ = ['JWTManager']

0 commit comments

Comments
 (0)