feat: done google login validation to get user data
This commit is contained in:
parent
ae2215e343
commit
a5f9953045
|
@ -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:
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
|
||||||
)
|
|
||||||
|
|
22
app/main.py
22
app/main.py
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
from .login_schema import LoginSchema
|
from .login_schema import LoginSchema
|
||||||
|
from .basic_response_schema import ResponseSchema, MetaSchema
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class GoogleLoginSchema(BaseModel):
|
||||||
|
tokenId: str
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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",
|
|
||||||
)
|
|
Loading…
Reference in New Issue