feat: done google login validation to get user data

This commit is contained in:
akhdanre 2025-03-13 13:12:19 +07:00
parent ae2215e343
commit a5f9953045
10 changed files with 126 additions and 76 deletions

View File

@ -2,7 +2,7 @@ from dotenv import load_dotenv
import os
# Load variabel dari file .env
load_dotenv()
load_dotenv(override=True)
class Config:

View File

@ -1,39 +1,73 @@
import logging
import sys
from flask import jsonify, request
from pydantic import ValidationError
from app.schemas.basic_response_schema import ResponseSchema
from app.schemas.google_login_schema import GoogleLoginSchema
from schemas import LoginSchema
from services import UserService
from services import AuthService
from services import UserService, AuthService
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class AuthController:
def __init__(
self, userService: UserService, authService: AuthService, googleAuth=None
):
def __init__(self, userService: UserService, authService: AuthService):
self.user_service = userService
self.auth_service = authService
self.google_auth = googleAuth
def login(self):
data = request.get_json()
dataSchema = LoginSchema(**data)
response = self.auth_service.login(dataSchema)
if response.success:
return jsonify(response.to_dict()), 200
return jsonify(response.to_dict()), 400
def google_login(self):
"""Handles Google Login via ID Token verification"""
try:
data = request.get_json()
id_token = data["token"]
response = self.google_auth.parse_id_token(id_token)
if response:
return jsonify({"message": response})
return jsonify({"message": "google login failed"})
# Validasi data dengan Pydantic
validated_data = GoogleLoginSchema(**data)
id_token = validated_data.id_token
# Verifikasi ID Token ke layanan AuthService
user_info = self.auth_service.verify_google_id_token(id_token)
if not user_info:
logger.error("Invalid Google ID Token")
response = ResponseSchema(
message="Invalid Google ID Token", data=None, meta=None
)
return jsonify(response.model_dump()), 401
# Jika berhasil, kembalikan data user tanpa meta
response = ResponseSchema(
message="Login successful",
data=user_info,
meta=None, # Karena ini single data, tidak ada meta
)
return jsonify(response.model_dump()), 200
except ValidationError as e:
logger.error(f"Validation error: {e}")
response = ResponseSchema(message="Invalid input", data=None, meta=None)
return jsonify(response.model_dump()), 400
except Exception as e:
logger.error(f"Error during Google login: {str(e)}", exc_info=True)
response = ResponseSchema(
message="Internal server error", data=None, meta=None
)
return jsonify(response.model_dump()), 500
def register(self):
return jsonify({"message": "register"})
return jsonify({"message": "register"}), 200
def logout(self):
return jsonify({"message": "logout"})
return jsonify({"message": "logout"}), 200
def test(self):
return "test"

View File

@ -9,7 +9,7 @@ class Container(containers.DeclarativeContainer):
"""Dependency Injection Container"""
mongo = providers.Dependency()
google_auth = providers.Dependency()
# repository
user_repository = providers.Factory(UserRepository, mongo.provided.db)
@ -18,6 +18,4 @@ class Container(containers.DeclarativeContainer):
user_service = providers.Factory(UserService, user_repository)
# controllers
auth_controller = providers.Factory(
AuthController, user_service, auth_service, google_auth
)
auth_controller = providers.Factory(AuthController, user_service, auth_service)

View File

@ -1,36 +1,22 @@
import sys
from dotenv import load_dotenv
from blueprints import default_blueprint
from di_container import Container
from configs import Config
from flask import Flask
from blueprints import auth_blueprint
from database import init_db
from authlib.integrations.flask_client import OAuth
from flask_login import LoginManager
def createApp() -> Flask:
app = Flask(__name__)
app.config.from_object(Config)
container = Container()
app.container = container
login_manager = LoginManager(app)
oauth = OAuth(app)
google = oauth.register(
name="google",
client_id=app.config["GOOGLE_CLIENT_ID"],
client_secret=app.config["GOOGLE_CLIENT_SECRET"],
access_token_url=app.config["GOOGLE_TOKEN_URI"],
authorize_url=app.config["GOOGLE_AUTH_URI"],
api_base_url=app.config["GOOGLE_BASE_URL"],
client_kwargs={"scope": "openid email profile"},
)
if google is not None:
container.google_auth.override(google)
mongo = init_db(app)
if mongo is not None:
container.mongo.override(mongo)

View File

@ -1,22 +1,22 @@
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional
# from pydantic import BaseModel
# from typing import Generic, TypeVar, Optional
T = TypeVar("T")
# T = TypeVar("T")
class ApiResponse(BaseModel, Generic[T]):
success: bool
message: str
data: Optional[T] = None
# class ApiResponse(BaseModel, Generic[T]):
# success: bool
# message: str
# data: Optional[T] = None
def to_json(self) -> str:
"""
Convert the model to a properly formatted JSON string.
"""
return self.model_dump_json(indent=4)
# def to_json(self) -> str:
# """
# Convert the model to a properly formatted JSON string.
# """
# return self.model_dump_json(indent=4)
def to_dict(self) -> dict:
"""
Convert the model to a dictionary with proper key-value pairs.
"""
return self.model_dump()
# def to_dict(self) -> dict:
# """
# Convert the model to a dictionary with proper key-value pairs.
# """
# return self.model_dump()

View File

@ -1 +1,2 @@
from .login_schema import LoginSchema
from .basic_response_schema import ResponseSchema, MetaSchema

View File

@ -0,0 +1,17 @@
from typing import Generic, TypeVar, Optional
from pydantic import BaseModel
T = TypeVar("T")
class MetaSchema:
total_page: int
current_page: int
total_data: int
total_all_data: int
class ResponseSchema(BaseModel, Generic[T]):
message: str
data: Optional[T] = None
meta: Optional[MetaSchema] = None

View File

@ -0,0 +1,5 @@
from pydantic import BaseModel
class GoogleLoginSchema(BaseModel):
tokenId: str

View File

@ -1,12 +1,42 @@
import sys
from schemas import LoginSchema
from repositories import UserRepository
from models import ApiResponse
from google.oauth2 import id_token
from google.auth.transport import requests
from configs import Config
class AuthService:
def __init__(self, userRepository: UserRepository):
self.user_repository = userRepository
def verify_google_id_token(self, id_token_str):
try:
payload = id_token.verify_oauth2_token(
id_token_str, requests.Request(), Config.GOOGLE_CLIENT_ID
)
print(f"output verify {payload}", file=sys.stderr)
user_data = {
"_id": payload.get("sub"),
"email": payload.get("email"),
"name": payload.get("name"),
"picture": payload.get("picture"),
"given_name": payload.get("given_name"),
"family_name": payload.get("family_name"),
"locale": payload.get("locale"),
"email_verified": payload.get("email_verified", False),
"iat": payload.get("iat"),
"exp": payload.get("exp"),
}
return payload
except Exception as e:
print(f"issue on the verify {e}", file=sys.stderr)
return None
def login(self, data: LoginSchema):
try:

View File

@ -1,21 +0,0 @@
from flask_login import (
LoginManager,
UserMixin,
login_user,
logout_user,
login_required,
current_user,
)
google = oauth.remote_app(
"google",
consumer_key="YOUR_GOOGLE_CLIENT_ID",
consumer_secret="YOUR_GOOGLE_CLIENT_SECRET",
request_token_params={"scope": "email"},
base_url="https://www.googleapis.com/oauth2/v1/",
request_token_url=None,
access_token_method="POST",
access_token_url="https://accounts.google.com/o/oauth2/token",
authorize_url="https://accounts.google.com/o/oauth2/auth",
)