8000 updated · chilcano/python_fastapi@17ce6e5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 17ce6e5

Browse files
committed
updated
1 parent 8fefd27 commit 17ce6e5

File tree

4 files changed

+71
-35
lines changed

4 files changed

+71
-35
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""added verification code
2+
3+
Revision ID: 39256113e8e5
4+
Revises: 1c7984990e1d
5+
Create Date: 2022-07-14 08:03:57.507140
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from sqlalchemy.dialects import postgresql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '39256113e8e5'
14+
down_revision = '1c7984990e1d'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.drop_table('posts')
22+
op.add_column('users', sa.Column('verification_code', sa.String(), nullable=True))
23+
op.create_unique_constraint(None, 'users', ['verification_code'])
24+
# ### end Alembic commands ###
25+
26+
27+
def downgrade() -> None:
28+
# ### commands auto generated by Alembic - please adjust! ###
29+
op.drop_constraint(None, 'users', type_='unique')
30+
op.drop_column('users', 'verification_code')
31+
op.create_table('posts',
32+
sa.Column('id', postgresql.UUID(), server_default=sa.text('uuid_generate_v4()'), autoincrement=False, nullable=False),
33+
sa.Column('user_id', postgresql.UUID(), autoincrement=False, nullable=False),
34+
sa.Column('title', sa.VARCHAR(), autoincrement=False, nullable=False),
35+
sa.Column('content', sa.VARCHAR(), autoincrement=False, nullable=False),
36+
sa.Column('category', sa.VARCHAR(), autoincrement=False, nullable=False),
37+
sa.Column('image', sa.VARCHAR(), autoincrement=False, nullable=False),
38+
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=False),
39+
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=False),
40+
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='posts_user_id_fkey', ondelete='CASCADE'),
41+
sa.PrimaryKeyConstraint('id', name='posts_pkey')
42+
)
43+
# ### end Alembic commands ###

app/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from enum import unique
12
import uuid
23
from .database import Base
34
from sqlalchemy import TIMESTAMP, Column, String, Boolean, text
@@ -13,6 +14,7 @@ class User(Base):
1314
password = Column(String, nullable=False)
1415
photo = Column(String, nullable=True)
1516
verified = Column(Boolean, nullable=False, server_default='False')
17+
verification_code = Column(String, nullable=True, unique=True)
1618
role = Column(String, server_default='user', nullable=False)
1719
created_at = Column(TIMESTAMP(timezone=True),
1820
nullable=False, server_default=text("now()"))

app/oauth2.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import base64
2-
from datetime import datetime, timedelta
32
from typing import List
43
from fastapi import Depends, HTTPException, status
54
from fastapi_jwt_auth import AuthJWT
65
from pydantic import BaseModel
7-
from jose import jwt, JWTError
86

97
from . import models
108
from .database import get_db
@@ -65,28 +63,3 @@ def require_user(db: Session = Depends(get_db), Authorize: AuthJWT = Depends()):
6563
raise HTTPException(
6664
status_code=status.HTTP_401_UNAUTHORIZED, detail='Token is invalid or has expired')
6765
return user_id
68-
69-
70-
def create_verification_token(user_id: str):
71-
expires = datetime.utcnow() + timedelta(minutes=30)
72-
payload = {'user_id': user_id, 'exp': expires}
73-
return jwt.encode(payload, settings.VERIFICATION_SECRET, algorithm='HS256')
74-
75-
76-
def verify_email_token(token: str):
77-
try:
78-
decoded = jwt.decode(
79-
token, settings.VERIFICATION_SECRET, algorithms=['HS256'])
80-
user_id = decoded.get('user_id')
81-
if not user_id:
82-
raise HTTPException(
83-
status_code=status.HTTP_400_BAD_REQUEST, detail='Could not verify user')
84-
except JWTError as e:
85-
error = e.__class__.__name__
86-
print(error)
87-
if error == 'ExpiredSignatureError':
88-
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
89-
detail='Token is invalid or has expired')
90-
raise HTTPException(
91-
status_code=status.HTTP_400_BAD_REQUEST, detail='Could not verify user')
92-
return user_id

app/routers/auth.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from datetime import timedelta
2+
import hashlib
3+
from random import randbytes
24
from fastapi import APIRouter, Request, Response, status, Depends, HTTPException
35
from pydantic import EmailStr
46

@@ -19,8 +21,9 @@
1921
@router.post('/register', status_code=status.HTTP_201_CREATED)
2022
async def create_user(payload: schemas.CreateUserSchema, request: Request, db: Session = Depends(get_db)):
2123
# Check if user already exist
22-
user = db.query(models.User).filter(
23-
models.User.email == EmailStr(payload.email.lower())).first()
24+
user_query = db.query(models.User).filter(
25+
models.User.email == EmailStr(payload.email.lower()))
26+
user = user_query.first()
2427
if user:
2528
raise HTTPException(status_code=status.HTTP_409_CONFLICT,
2629
detail='Account already exist')
@@ -40,11 +43,21 @@ async def create_user(payload: schemas.CreateUserSchema, request: Request, db: S
4043
db.refresh(new_user)
4144

4245
try:
43-
token = oauth2.create_verification_token(str(new_user.id))
44-
url = f"{request.url.scheme}://{request.client.host}:{request.url.port}/api/auth/verifyemail/{token}"
46+
# Send Verification Email
47+
token = randbytes(10)
48+
hashedCode = hashlib.sha256()
49+
hashedCode.update(token)
50+
verification_code = hashedCode.hexdigest()
51+
user_query.update(
52+
{'verification_code': verification_code}, synchronize_session=False)
53+
db.commit()
54+
url = f"{request.url.scheme}://{request.client.host}:{request.url.port}/api/auth/verifyemail/{token.hex()}"
4555
await Email(new_user, url, [payload.email]).sendVerificationCode()
4656
except Exception as error:
4757
print('Error', error)
58+
user_query.update(
59+
{'verification_code': None}, synchronize_session=False)
60+
db.commit()
4861
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_E CE44 RROR,
4962
detail='There was an error sending email')
5063
return {'status': 'success', 'message': 'Verification token successfully sent to your email'}
@@ -129,13 +142,18 @@ def logout(response: Response, Authorize: AuthJWT = Depends(), user_id: str = De
129142

130143
@router.get('/verifyemail/{token}')
131144
def verify_me(token: str, db: Session = Depends(get_db)):
132-
id = oauth2.verify_email_token(token)
133-
user_query = db.query(models.User).filter(models.User.id == id)
145+
hashedCode = hashlib.sha256()
146+
hashedCode.update(bytes.fromhex(token))
147+
verification_code = hashedCode.hexdigest()
148+
user_query = db.query(models.User).filter(
149+
models.User.verification_code == verification_code)
150+
db.commit()
134151
user = user_query.first()
135152
if not user:
136153
raise HTTPException(
137-
status_code=status.HTTP_404_NOT_FOUND, detail='User no longer exist')
138-
user_query.update({'verified': True}, synchronize_session=False)
154+
status_code=status.HTTP_403_FORBIDDEN, detail='Email can only be verified once')
155+
user_query.update(
156+
{'verified': True, 'verification_code': None}, synchronize_session=False)
139157
db.commit()
140158
return {
141159
"status": "success",

0 commit comments

Comments
 (0)
0