auth endpoints added
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=["*"],
|
||||
|
||||
322
ApiServices/AuthService/endpoints/auth/route.py
Normal file
322
ApiServices/AuthService/endpoints/auth/route.py
Normal 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,
|
||||
)
|
||||
@@ -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"),
|
||||
]
|
||||
|
||||
@@ -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"),
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user