auth endpoints added

This commit is contained in:
2025-04-03 14:19:34 +03:00
parent 3583d178e9
commit ee405133be
37 changed files with 976 additions and 570 deletions

View File

@@ -25,4 +25,4 @@ COPY /Schemas/identity /Schemas/identity
ENV PYTHONPATH=/ PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
# Run the application using the configured uvicorn server
CMD ["poetry", "run", "python", "ApiServices/TemplateService/app.py"]
CMD ["poetry", "run", "python", "ApiServices/AuthService/app.py"]

View File

@@ -2,11 +2,12 @@ import uvicorn
from config import api_config
from ApiServices.TemplateService.create_app import create_app
from ApiServices.AuthService.create_app import create_app
# from prometheus_fastapi_instrumentator import Instrumentator
app = create_app() # Create FastAPI application
app = create_app() # Create FastAPI application
# Instrumentator().instrument(app=app).expose(app=app) # Setup Prometheus metrics

View File

@@ -8,9 +8,9 @@ class Configs(BaseSettings):
"""
PATH: str = ""
HOST: str = "",
PORT: int = 0,
LOG_LEVEL: str = "info",
HOST: str = ("",)
PORT: int = (0,)
LOG_LEVEL: str = ("info",)
RELOAD: int = 0
ACCESS_TOKEN_TAG: str = ""
@@ -36,7 +36,7 @@ class Configs(BaseSettings):
"host": self.HOST,
"port": int(self.PORT),
"log_level": self.LOG_LEVEL,
"reload": bool(self.RELOAD)
"reload": bool(self.RELOAD),
}
@property

View File

@@ -3,16 +3,16 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from ApiServices.TemplateService.create_route import RouteRegisterController
from ApiServices.TemplateService.endpoints.routes import get_routes
from ApiServices.TemplateService.open_api_creator import create_openapi_schema
from ApiServices.TemplateService.middlewares.token_middleware import token_middleware
from ApiServices.TemplateService.config import template_api_config
from ApiServices.AuthService.create_route import RouteRegisterController
from ApiServices.AuthService.endpoints.routes import get_routes
from ApiServices.AuthService.open_api_creator import create_openapi_schema
from ApiServices.AuthService.middlewares.token_middleware import token_middleware
from ApiServices.AuthService.config import api_config
def create_app():
application = FastAPI(**template_api_config.api_info)
application = FastAPI(**api_config.api_info)
# application.mount(
# "/application/static",
# StaticFiles(directory="application/static"),
@@ -20,7 +20,7 @@ def create_app():
# )
application.add_middleware(
CORSMiddleware,
allow_origins=template_api_config.ALLOW_ORIGINS,
allow_origins=api_config.ALLOW_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],

View File

@@ -0,0 +1,322 @@
import uuid
from typing import Union
from fastapi import APIRouter, Request, status, Header
from fastapi.responses import JSONResponse
from ApiServices.AuthService.config import api_config
from ApiServices.AuthService.validations.request.authentication.login_post import (
RequestLogin,
RequestSelectLiving,
RequestSelectOccupant, RequestCreatePassword, RequestChangePassword, RequestForgotPasswordPhone,
RequestForgotPasswordEmail,
)
auth_route = APIRouter(
prefix="/authentication",
tags=["Authentication Cluster"],
)
@auth_route.post(
path="/login",
summary="Login via domain and access key : [email] | [phone]",
description="Login Route",
)
def authentication_login_post(
request: Request,
data: RequestLogin,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Authentication Login Route with Post Method
"""
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0001"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={**data.model_dump()},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.post(
path="/select",
summary="Select company or occupant type",
description="Selection of users company or occupant type",
)
def authentication_select_post(
request: Request,
data: Union[RequestSelectOccupant, RequestSelectLiving],
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Authentication Select Route with Post Method
"""
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"token": token,
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0001"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content=data.model_dump(),
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.get(
path="/logout",
summary="Logout user",
description="Logout only single session of user which domain is provided",
)
def authentication_logout_post(
request: Request,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Logout user from the system
"""
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"token": token,
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0003"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.get(
path="/disconnect",
summary="Disconnect all sessions",
description="Disconnect all sessions of user in access token",
)
def authentication_disconnect_post(
request: Request,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Disconnect all sessions of user in access token
"""
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"token": token,
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0003"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.get(
path="/token/check",
summary="Check if token is valid",
description="Check if access token is valid for user",
)
def authentication_token_check_post(
request: Request,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Check if access token is valid for user
"""
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"token": token,
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0003"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.get(
path="/token/refresh",
summary="Refresh if token is valid",
description="Refresh if access token is valid for user",
)
def authentication_token_refresh_post(
request: Request,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Refresh if access token is valid for user
"""
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0003"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.post(
path="/password/create",
summary="Create password with access token",
description="Create password",
)
def authentication_password_create_post(
request: Request,
data: RequestCreatePassword,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Authentication create password Route with Post Method
"""
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"token": token,
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0001"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={**data.model_dump()},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.post(
path="/password/change",
summary="Change password with access token",
description="Change password",
)
def authentication_password_change_post(
request: Request,
data: RequestChangePassword,
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Authentication change password Route with Post Method
"""
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
"token": token,
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0001"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={**data.model_dump()},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)
@auth_route.post(
path="/password/reset",
summary="Reset password with access token",
description="Reset password",
)
def authentication_password_reset_post(
request: Request,
data: Union[RequestForgotPasswordEmail, RequestForgotPasswordPhone],
language: str = Header(None, alias="language"),
domain: str = Header(None, alias="domain"),
):
"""
Authentication reset password Route with Post Method
"""
headers = {
"language": language or "",
"domain": domain or "",
"eys-ext": f"{str(uuid.uuid4())}",
}
if not domain or not language:
return JSONResponse(
content={"error": "EYS_0001"},
status_code=status.HTTP_406_NOT_ACCEPTABLE,
headers=headers,
)
return JSONResponse(
content={**data.model_dump()},
status_code=status.HTTP_202_ACCEPTED,
headers=headers,
)

View File

@@ -1,9 +1,9 @@
from fastapi import APIRouter
from .test_template.route import test_template_route
from ApiServices.AuthService.endpoints.auth.route import auth_route
def get_routes() -> list[APIRouter]:
return [test_template_route]
return [auth_route]
def get_safe_endpoint_urls() -> list[tuple[str, str]]:
@@ -15,6 +15,5 @@ def get_safe_endpoint_urls() -> list[tuple[str, str]]:
("/auth/register", "POST"),
("/auth/login", "POST"),
("/metrics", "GET"),
("/test/template", "GET"),
("/test/template", "POST"),
]
("/authentication/login", "POST"),
]

View File

@@ -1,40 +0,0 @@
from fastapi import APIRouter, Request, Response
test_template_route = APIRouter(prefix="/test", tags=["Test"])
@test_template_route.get(path="/template", description="Test Template Route")
def test_template(request: Request, response: Response):
"""
Test Template Route
"""
headers = dict(request.headers)
response.headers["X-Header"] = "Test Header GET"
return {
"completed": True,
"message": "Test Template Route",
"info": {
"host": headers.get("host", "Not Found"),
"user_agent": headers.get("user-agent", "Not Found"),
},
}
@test_template_route.post(
path="/template",
description="Test Template Route with Post Method",
)
def test_template_post(request: Request, response: Response):
"""
Test Template Route with Post Method
"""
headers = dict(request.headers)
response.headers["X-Header"] = "Test Header POST"
return {
"completed": True,
"message": "Test Template Route with Post Method",
"info": {
"host": headers.get("host", "Not Found"),
"user_agent": headers.get("user-agent", "Not Found"),
},
}

View File

@@ -1,5 +1,7 @@
from fastapi import Request, Response
from ApiServices.TemplateService.endpoints.routes import get_safe_endpoint_urls
from fastapi import Request, status
from fastapi.responses import JSONResponse
from ..endpoints.routes import get_safe_endpoint_urls
from ..config import api_config
async def token_middleware(request: Request, call_next):
@@ -9,9 +11,14 @@ async def token_middleware(request: Request, call_next):
if base_url in safe_endpoints:
return await call_next(request)
token = request.headers.get("Authorization")
token = request.headers.get(api_config.ACCESS_TOKEN_TAG, None)
if not token:
return Response(content="Missing token", status_code=400)
return JSONResponse(
content={
"error": "EYS_0002",
},
status_code=status.HTTP_401_UNAUTHORIZED,
)
response = await call_next(request)
return response

View File

@@ -3,8 +3,8 @@ from fastapi import FastAPI
from fastapi.routing import APIRoute
from fastapi.openapi.utils import get_openapi
from ApiServices.TemplateService.config import template_api_config
from ApiServices.TemplateService.endpoints.routes import get_safe_endpoint_urls
from ApiServices.AuthService.config import api_config
from ApiServices.AuthService.endpoints.routes import get_safe_endpoint_urls
class OpenAPISchemaCreator:
@@ -36,7 +36,7 @@ class OpenAPISchemaCreator:
"BearerAuth": {
"type": "apiKey",
"in": "header",
"name": template_api_config.ACCESS_TOKEN_TAG,
"name": api_config.ACCESS_TOKEN_TAG,
"description": "Enter: **'Bearer <JWT>'**, where JWT is the access token",
}
}
@@ -73,9 +73,9 @@ class OpenAPISchemaCreator:
Dict[str, Any]: Complete OpenAPI schema
"""
openapi_schema = get_openapi(
title=template_api_config.TITLE,
description=template_api_config.DESCRIPTION,
version=template_api_config.VERSION,
title=api_config.TITLE,
description=api_config.DESCRIPTION,
version=api_config.VERSION,
routes=self.app.routes,
)
@@ -83,9 +83,7 @@ class OpenAPISchemaCreator:
if "components" not in openapi_schema:
openapi_schema["components"] = {}
openapi_schema["components"][
"securitySchemes"
] = self.create_security_schemes()
openapi_schema["components"]["securitySchemes"] = self.create_security_schemes()
# Configure route security and responses
for route in self.app.routes:
@@ -115,4 +113,4 @@ def create_openapi_schema(app: FastAPI) -> Dict[str, Any]:
Dict[str, Any]: Complete OpenAPI schema
"""
creator = OpenAPISchemaCreator(app)
return creator.create_schema()
return creator.create_schema()

View File

@@ -0,0 +1,38 @@
from typing import Optional
from pydantic import BaseModel
class RequestLogin(BaseModel):
access_key: str
password: str
remember_me: Optional[bool]
class RequestSelectOccupant(BaseModel):
company_uu_id: str
class RequestSelectLiving(BaseModel):
build_living_space_uu_id: str
class RequestCreatePassword(BaseModel):
password_token: str
password: str
re_password: str
class RequestChangePassword(BaseModel):
old_password: str
password: str
re_password: str
class RequestForgotPasswordEmail(BaseModel):
email: str
class RequestForgotPasswordPhone(BaseModel):
phone_number: str