Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/Implement 42oauth login #10

Merged
merged 6 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
transcendence
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ build:

up:
@docker-compose -f docker-compose.yml up -d
@echo "🛜 $(FG_GREEN)Connect to $(FG_WHITE)$(UNDERLINE)https://localhost$(RESET) 🛜"
@echo "🛜 $(FG_GREEN)Connect to $(FG_WHITE)$(UNDERLINE)https://localhost:80$(RESET) 🛜"

down:
@docker-compose -f docker-compose.yml down
Expand All @@ -51,7 +51,7 @@ stop:
start:
@echo "$(FG_GREEN)Started$(RESET)"
@docker-compose -f docker-compose.yml start
@echo "$(FG_GREEN)Connect to $(FG_WHITE)$(UNDERLINE)http://localhost$(RESET)"
@echo "$(FG_GREEN)Connect to $(FG_WHITE)$(UNDERLINE)http://localhost:80$(RESET)"

re:
@echo "$(FG_GREEN)Restarted$(RESET)"
Expand All @@ -70,6 +70,7 @@ clean:
fclean:
@$(MAKE) down
@docker system prune -af --volumes
@docker volume rm transcendence_db_data
@echo "🧹 $(FG_BLUE)Fully cleaned up$(RESET) 🧹"

.PHONY: all build up down stop start re log clean fclean
Expand Down
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y \

WORKDIR /app

COPY ./django/ .
COPY . .
RUN chmod -R 755 /app

CMD ["sh", "docker-run-server.sh"]
6 changes: 0 additions & 6 deletions backend/django/ansanking/urls.py

This file was deleted.

7 changes: 0 additions & 7 deletions backend/django/ansanking/views.py

This file was deleted.

25 changes: 0 additions & 25 deletions backend/django/docker-run-server.sh

This file was deleted.

41 changes: 41 additions & 0 deletions backend/docker-run-server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

pip install --upgrade pip

if [ -f "requirements.txt" ]; then
cnt=$(pip freeze | grep -f requirements.txt | wc -l)
if [ $cnt -lt $(cat requirements.txt | wc -l) ]; then
pip install -r requirements.txt
echo "Installed packages from requirements.txt."
else
echo "Packages from requirements.txt are already installed."
fi
else
echo "requirements.txt not found."
exit 1
fi

echo "Applying migrations..."
python manage.py makemigrations
python manage.py migrate

# Create a superuser
DJANGO_SUPERUSER_USERNAME=$DJANGO_SUPERUSER_USERNAME
DJANGO_SUPERUSER_PASSWORD=$DJANGO_SUPERUSER_PASSWORD
DJANGO_SUPERUSER_EMAIL=$DJANGO_SUPERUSER_EMAIL

USER_EXISTS=$(python manage.py shell -c "
from django.contrib.auth import get_user_model;
User = get_user_model();
print(User.objects.filter(username='admin').exists())
")

echo "USER_EXISTS: $USER_EXISTS"

if [ "$USER_EXISTS" = "False" ]; then
echo "Creating superuser..."
python manage.py createsuperuser --username $DJANGO_SUPERUSER_USERNAME --email $DJANGO_SUPERUSER_EMAIL --noinput || true
fi

echo "Starting development server..."
python manage.py runserver 0.0.0.0:8000
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions backend/django/ansanking/apps.py → backend/login/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.apps import AppConfig


class AnsankingConfig(AppConfig):
class LoginConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'ansanking'
name = 'login'
File renamed without changes.
File renamed without changes.
12 changes: 12 additions & 0 deletions backend/login/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.urls import path
from .views import (
login,
callback,
validate_token
)

urlpatterns = [
path('login/', login),
path('callback/', callback),
path('validate/', validate_token),
]
107 changes: 107 additions & 0 deletions backend/login/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from django.shortcuts import redirect
from django.views.decorators.csrf import csrf_exempt
from django.middleware.csrf import get_token
from django.http import JsonResponse
from django.conf import settings
from users.models import User
from rest_framework.decorators import api_view
from datetime import datetime, timedelta
import requests
import jwt
import os


@api_view(["GET"])
def login(request):
oauth_url = "https://api.intra.42.fr/oauth/authorize"
redirect_uri = "http://localhost:8000/api/callback/"
client_id = os.getenv("OAUTH_CLIENT_ID")
state = os.getenv("OAUTH_STATE") # CSRF 방지용 랜덤 문자열
return redirect(f"{oauth_url}?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code&state={state}")


@api_view(["GET"])
def callback(request):
code = request.GET.get("code")
if not code:
return redirect("http://localhost:5173")

token_url = "https://api.intra.42.fr/oauth/token"
redirect_uri = "http://localhost:8000/api/callback/"
client_id = os.getenv("OAUTH_CLIENT_ID")
client_secret = os.getenv("OAUTH_CLIENT_SECRET")

data = {
"grant_type": "authorization_code",
"code": code,
"client_id": client_id,
"client_secret": client_secret,
"redirect_uri": redirect_uri,
}


response = requests.post(token_url, data=data)
if response.status_code != 200:
return redirect("http://localhost:5173")

token_data = response.json()
access_token = token_data.get("access_token")

# Access Token을 사용하여 사용자 정보 가져오기
user_info = requests.get("https://api.intra.42.fr/v2/me", headers={"Authorization": f"Bearer {access_token}"})
if user_info.status_code != 200:
return redirect("http://localhost:5173")

user_data = user_info.json()

# 사용자 정보 처리 (예: DB 저장)
nickname = user_data.get("login") # 42 API에서 사용자 로그인 이름
email = user_data.get("email") # 42 API에서 사용자 이메일
img_url = user_data.get("image", {}).get("link") # 42 API에서 사용자 이미지 URL
is_2FA = user_data.get("is_2fa", False) # 2FA 여부
is_online = False # 기본값으로 설정 (온라인 상태는 API에서 제공하지 않음)

# 사용자 정보 저장 또는 업데이트
user, created = User.objects.update_or_create(
nickname=nickname, defaults={
"email": email,
"img_url": img_url,
"is_2FA": is_2FA,
"is_online": is_online}
)

# 생성된 경우, 추가 로직 (예: 환영 메시지 등)
if created:
print(f"새 사용자 생성: {nickname}")
else:
print(f"기존 사용자 업데이트: {nickname}")

# JWT 토큰 생성
payload = {
"id": user.user_id,
"nickname": user.nickname,
"exp": datetime.utcnow() + timedelta(hours=24), # 토큰 유효 시간 설정
}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")

response = redirect("http://localhost:5173")
response.set_cookie("jwt", token, httponly=True, secure=True, samesite='Lax')

return response

@csrf_exempt
def validate_token(request):
# CSRF 토큰 발급
get_token(request)

token = request.COOKIES.get('jwt')
if not token:
return JsonResponse({"isValid": False})

try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
return JsonResponse({"isValid": True, "user": payload})
except jwt.ExpiredSignatureError:
return JsonResponse({"isValid": False, "error": "Token has expired"})
except jwt.InvalidTokenError:
return JsonResponse({"isValid": False, "error": "Invalid token"})
File renamed without changes.
7 changes: 7 additions & 0 deletions backend/django/requirements.txt → backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
asgiref==3.8.1
certifi==2024.7.4
charset-normalizer==3.3.2
Django==4.2.14
django-cors-headers==4.4.0
djangorestframework==3.15.2
djangorestframework-simplejwt==5.3.1
drf-yasg==1.21.7
idna==3.7
inflection==0.5.1
packaging==24.1
psycopg2-binary==2.9.9
PyJWT==2.9.0
python-dotenv==1.0.1
pytz==2024.1
PyYAML==6.0.1
requests==2.31.0
setuptools==71.1.0
sqlparse==0.5.1
typing_extensions==4.12.2
uritemplate==4.1.1
urllib3==2.2.2
33 changes: 22 additions & 11 deletions backend/django/run-server.sh → backend/run-server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,19 @@ DOT_ENV_FILE=".env"
# 스크립트 파일의 디렉토리로 이동
cd "$(dirname "$0")"

if [ -f "$DOT_ENV_FILE" ]; then
echo "$DOT_ENV_FILE already exists."
else
cp ../../$DOT_ENV_FILE .
cp ../$DOT_ENV_FILE .
echo "Copied $DOT_ENV_FILE."
sed -i '' 's/^DB_HOST=.*$/DB_HOST=localhost/' .env
fi

# TEST: .env 파일의 변수들을 환경 변수로 설정 (슈퍼 유저를 생성하기 위해 필요)
if [ -f "$DOT_ENV_FILE" ]; then
export $(grep -v '^#' $DOT_ENV_FILE | xargs)
echo "Exported .env variables to environment."
else
echo ".env file not found."
exit 1
fi

rm $DOT_ENV_FILE

if [ ! -d "$ENV_DIR" ]; then
python3 -m venv $ENV_DIR
echo "Virtual environment created at $ENV_DIR."
Expand All @@ -37,23 +32,39 @@ source $ENV_DIR/bin/activate
echo "Virtual environment activated."

if [ -f "requirements.txt" ]; then
pip install -r requirements.txt
echo "Installed packages from requirements.txt."
cnt=$(pip freeze | grep -f requirements.txt | wc -l)
if [ $cnt -lt $(cat requirements.txt | wc -l) ]; then
pip install -r requirements.txt
echo "Installed packages from requirements.txt."
else
echo "Packages from requirements.txt are already installed."
fi
else
echo "requirements.txt not found."
exit 1
fi

echo "Applying migrations..."
python manage.py makemigrations
python manage.py migrate

# Create a superuser
echo "Creating superuser..."
DJANGO_SUPERUSER_USERNAME=$DJANGO_SUPERUSER_USERNAME
DJANGO_SUPERUSER_PASSWORD=$DJANGO_SUPERUSER_PASSWORD
DJANGO_SUPERUSER_EMAIL=$DJANGO_SUPERUSER_EMAIL

python manage.py createsuperuser --username $DJANGO_SUPERUSER_USERNAME --email $DJANGO_SUPERUSER_EMAIL --noinput || true
USER_EXISTS=$(python manage.py shell -c "
from django.contrib.auth import get_user_model;
User = get_user_model();
print(User.objects.filter(username='admin').exists())
")

if [ "$USER_EXISTS" = "False" ]; then
echo "Creating superuser..."
python manage.py createsuperuser --username $DJANGO_SUPERUSER_USERNAME --email $DJANGO_SUPERUSER_EMAIL --noinput || true
fi

echo "Starting development server..."
clear

python manage.py runserver localhost:8000
File renamed without changes.
Loading