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 import os
# Load variabel dari file .env # Load variabel dari file .env
load_dotenv() load_dotenv(override=True)
class Config: class Config:

View File

@ -1,39 +1,73 @@
import logging
import sys
from flask import jsonify, request 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 schemas import LoginSchema
from services import UserService from services import UserService, AuthService
from services import AuthService
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class AuthController: class AuthController:
def __init__( def __init__(self, userService: UserService, authService: AuthService):
self, userService: UserService, authService: AuthService, googleAuth=None
):
self.user_service = userService self.user_service = userService
self.auth_service = authService self.auth_service = authService
self.google_auth = googleAuth
def login(self): def login(self):
data = request.get_json() data = request.get_json()
dataSchema = LoginSchema(**data) dataSchema = LoginSchema(**data)
response = self.auth_service.login(dataSchema) response = self.auth_service.login(dataSchema)
if response.success: if response.success:
return jsonify(response.to_dict()), 200 return jsonify(response.to_dict()), 200
return jsonify(response.to_dict()), 400 return jsonify(response.to_dict()), 400
def google_login(self): def google_login(self):
"""Handles Google Login via ID Token verification"""
try:
data = request.get_json() data = request.get_json()
id_token = data["token"]
response = self.google_auth.parse_id_token(id_token) # Validasi data dengan Pydantic
if response: validated_data = GoogleLoginSchema(**data)
return jsonify({"message": response}) id_token = validated_data.id_token
return jsonify({"message": "google login failed"})
# 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): def register(self):
return jsonify({"message": "register"}), 200
return jsonify({"message": "register"})
def logout(self): def logout(self):
return jsonify({"message": "logout"}) return jsonify({"message": "logout"}), 200
def test(self): def test(self):
return "test" return "test"

View File

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

View File

@ -1,36 +1,22 @@
import sys
from dotenv import load_dotenv
from blueprints import default_blueprint from blueprints import default_blueprint
from di_container import Container from di_container import Container
from configs import Config from configs import Config
from flask import Flask from flask import Flask
from blueprints import auth_blueprint from blueprints import auth_blueprint
from database import init_db from database import init_db
from authlib.integrations.flask_client import OAuth
from flask_login import LoginManager
def createApp() -> Flask: def createApp() -> Flask:
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(Config) app.config.from_object(Config)
container = Container() container = Container()
app.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) mongo = init_db(app)
if mongo is not None: if mongo is not None:
container.mongo.override(mongo) container.mongo.override(mongo)

View File

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

View File

@ -1 +1,2 @@
from .login_schema import LoginSchema 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 schemas import LoginSchema
from repositories import UserRepository from repositories import UserRepository
from models import ApiResponse from models import ApiResponse
from google.oauth2 import id_token
from google.auth.transport import requests
from configs import Config
class AuthService: class AuthService:
def __init__(self, userRepository: UserRepository): def __init__(self, userRepository: UserRepository):
self.user_repository = 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): def login(self, data: LoginSchema):
try: 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",
)