Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions .env
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
MONGO_URL =
DB_NAME = development
DB_USERS_COLLECTION = "users"
DB_USERS_PICTURES_COLLECTION = "users.pictures"
DB_USERS_CONTACTS_COLLECTION = "users.contacts"
DB_USERS_MESSAGES_COLLECTION = "users.messages"
SECRET_KEY = ""
ALGORITHM = "HS256"
EXPIRE_MINUTES = 15
CORS_ALLOWED_HOSTS = "http://localhost:8081"
DB_URL=
DB_NAME=development
DB_USERS_COLLECTION=users
DB_USERS_PICTURES_COLLECTION=users.pictures
DB_USERS_CONTACTS_COLLECTION=users.contacts
DB_USERS_MESSAGES_COLLECTION=users.messages
LOG_DB_URL=
LOG_DATABASE_NAME=auth-logs
LOG_LEVEL=DEBUG
JWT_SECRET_KEY=
JWT_ALGORITHM=HS256
JWT_EXPIRE_MINUTES=15
CORS_ALLOWED_HOSTS="http://localhost:8081"
4 changes: 1 addition & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
FROM python:3.13-slim
FROM python:3.12-slim

WORKDIR /app

COPY ./requirements.txt ./
COPY ./*.pem ./
COPY .env ./
COPY *.ini ./

RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

Expand Down
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ It is a microservice for user administration and authenication, using JWT tokens

## Requirements
- Python 3.12+
- FastApi 0.117+
- FastApi 0.124+

## Installing
1. Create a virtual environment
Expand Down Expand Up @@ -35,9 +35,9 @@ Use the private key file to extract the public key in PEM format
```bash
openssl rsa -in private_key.pem -pubout -out public_key.pem
```
5. Set configurations files (.env, config.ini) for different purposes (mongodb, JWT, CORS, logs)
5. Set configurations files (.env) for different purposes (mongodb, JWT, CORS, logs)

The microservice uses mongoDB as its database, so the connection string and other configurations must be included in the configuration files
The microservice uses mongoDB as its database, so the connection string and other configurations must be included in the configuration file

6. Run local development server
```bash
Expand All @@ -48,5 +48,23 @@ uvicorn src.main:app --reload
http://127.0.0.1:8000/docs
```

## Using with Docker

1. Create the image
```bash
docker build -t auth-service:latest .
```
Or download the image hosted in this repository.
```bash
docker pull ghcr.io/ablogo/authfastapi:latest
```
2. Run a container from the image previously created
```bash
docker run -p 8000:80 --env-file .env auth-service:latest
```

> [!IMPORTANT]
> It is necessary to complete the configuration file(.env), and create the PEM files and place them in the root folder

> [!NOTE]
> Since the project is used for learning, it does not strictly follow the concept of microservices, where each microservice should have its own realm of responsability
12 changes: 0 additions & 12 deletions config.ini

This file was deleted.

10 changes: 5 additions & 5 deletions src/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from dotenv import dotenv_values
from pymongo import AsyncMongoClient
from pymongo.server_api import ServerApi
from dotenv import load_dotenv
import os

config = dotenv_values(".env")

client = AsyncMongoClient(config["MONGO_URL"], server_api= ServerApi(version='1', strict=True, deprecation_errors=True))
database = client.get_database(config["DB_NAME"])
load_dotenv()
client = AsyncMongoClient(os.environ["DB_URL"], server_api= ServerApi(version='1', strict=True, deprecation_errors=True))
database = client.get_database(os.environ["DB_NAME"])

async def get_db():
try:
Expand Down
15 changes: 9 additions & 6 deletions src/dependency_injection/containers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from dependency_injector import containers, providers
import os
from dotenv import load_dotenv

from src.services import mongodb_service
from src.services.crypto_service import CryptoService
from src.logging.mongo_logging import MongoLogger

load_dotenv()

class Container(containers.DeclarativeContainer):

Expand All @@ -18,20 +21,20 @@ class Container(containers.DeclarativeContainer):
"src.routers.products_router"
])

config = providers.Configuration(ini_files=["config.ini"])
#config = providers.Configuration(ini_files=["config.ini"])

logging = providers.Singleton(
MongoLogger,
config.log.db_url,
config.log.db_database,
os.environ["LOG_DB_URL"], #config.log.db_url,
os.environ["LOG_DATABASE_NAME"], #config.log.db_database,
"",
config.log.level
os.environ["LOG_LEVEL"], #config.log.level
)

database_client = providers.Singleton(
mongodb_service.MongoAsyncService,
config.database.url,
config.database.name
os.environ["DB_URL"], #config.database.url,
os.environ["DB_NAME"], #config.database.name
)

crypto_service = providers.Singleton(
Expand Down
8 changes: 4 additions & 4 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from fastapi import FastAPI
from contextlib import asynccontextmanager
from dotenv import dotenv_values
from fastapi.middleware.cors import CORSMiddleware
from dotenv import load_dotenv
import os

from src.routers import auth_router, products_router, users_router, chat_router
from src.routers.admin import users_router as admin_user_router
Expand All @@ -10,9 +11,8 @@
from src.dependency_injection.containers import Container
from src.dependencies import close_db

config = dotenv_values(".env")

origins = config["CORS_ALLOWED_HOSTS"].split(',') if config["CORS_ALLOWED_HOSTS"] else []
load_dotenv()
origins = os.environ["CORS_ALLOWED_HOSTS"].split(',') if os.environ["CORS_ALLOWED_HOSTS"] else []

@asynccontextmanager
async def lifespan(app: FastAPI):
Expand Down
13 changes: 7 additions & 6 deletions src/services/chat_service.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from dependency_injector.wiring import Provide, inject
from dotenv import dotenv_values
from dotenv import load_dotenv
import os

from src.services.user_service import get_user
from src.services.crypto_service import CryptoService
Expand All @@ -10,15 +11,15 @@
from src.logging.mongo_logging import MongoLogger
from src.dependency_injection.containers import Container

load_dotenv()
crypto_service: CryptoService = Provide[Container.crypto_service]
db_dependency: MongoAsyncService = Provide[Container.database_client]
logger: MongoLogger = Provide[Container.logging]
config = dotenv_values(".env")
users_collection = str(config["DB_USERS_COLLECTION"])
users_contacts_collection = str(config["DB_USERS_CONTACTS_COLLECTION"])
users_messages_collection = str(config["DB_USERS_MESSAGES_COLLECTION"])
users_collection = str(os.environ["DB_USERS_COLLECTION"])
users_contacts_collection = str(os.environ["DB_USERS_CONTACTS_COLLECTION"])
users_messages_collection = str(os.environ["DB_USERS_MESSAGES_COLLECTION"])



@inject
async def get_contacts(email: str, db = db_dependency, log = logger) -> list[Contact] | None:
try:
Expand Down
16 changes: 8 additions & 8 deletions src/services/jwt_service.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
from datetime import datetime, timedelta, timezone
from dependency_injector.wiring import Provide, inject
from dotenv import dotenv_values
from fastapi import HTTPException, Header, Request
import jwt
from fastapi import HTTPException, Request
from dotenv import load_dotenv
import os, jwt

from src.services.crypto_service import CryptoService
from src.logging.mongo_logging import MongoLogger
from src.dependency_injection.containers import Container

crypto_service: CryptoService = Provide[Container.crypto_service]
log_service: MongoLogger = Provide[Container.logging]
config = dotenv_values(".env")
load_dotenv()

@inject
async def create_token(data: dict, expire_time: timedelta = timedelta(minutes=int(str(config["EXPIRE_MINUTES"]))), crypto = crypto_service, log = log_service):
async def create_token(data: dict, expire_time: timedelta = timedelta(minutes=int(str(os.environ["JWT_EXPIRE_MINUTES"]))), crypto = crypto_service, log = log_service):
try:
for item in data:
data[item] = await crypto.encrypt_text(data[item])

expire = datetime.now(timezone.utc) + expire_time
data.update({ "exp": expire })
encode_jwt = jwt.encode(data, str(config["SECRET_KEY"]), algorithm= config["ALGORITHM"])
encode_jwt = jwt.encode(data, str(os.environ["JWT_SECRET_KEY"]), algorithm= os.environ["JWT_ALGORITHM"])
return encode_jwt
except Exception as e:
log.logger.error(e)
Expand Down Expand Up @@ -53,7 +53,7 @@ async def verify_token_from_requests(request: Request):
@inject
async def verify(request_token: str, log = log_service):
try:
payload = jwt.decode(request_token, str(config["SECRET_KEY"]), config["ALGORITHM"])
payload = jwt.decode(request_token, str(os.environ["JWT_SECRET_KEY"]), os.environ["JWT_ALGORITHM"])
return payload
except jwt.ExpiredSignatureError as e:
log.logger.error(e)
Expand All @@ -68,7 +68,7 @@ async def verify(request_token: str, log = log_service):
@inject
async def get_email(token, crypto = crypto_service, log = log_service):
try:
payload = jwt.decode(token, str(config["SECRET_KEY"]), config["ALGORITHM"])
payload = jwt.decode(token, str(os.environ["JWT_SECRET_KEY"]), os.environ["JWT_ALGORITHM"])
email = await crypto.decrypt_text(payload.get("sub"))
if email is None:
return None
Expand Down
2 changes: 1 addition & 1 deletion src/services/mongodb_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ def close_db(self):
try:
self.client.close()
except Exception as e:
raise e
raise e
9 changes: 5 additions & 4 deletions src/services/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from fastapi import UploadFile
from pymongo.asynchronous.database import AsyncDatabase
from bson import ObjectId, Binary
from dotenv import dotenv_values
from dotenv import load_dotenv
import os

from src.services.crypto_service import CryptoService
from src.services.jwt_service import get_email
Expand All @@ -14,9 +15,9 @@

crypto_service: CryptoService = Provide[Container.crypto_service]
log_service: MongoLogger = Provide[Container.logging]
config = dotenv_values(".env")
users_collection = str(config["DB_USERS_COLLECTION"])
users_pics_collection = str(config["DB_USERS_PICTURES_COLLECTION"])
load_dotenv()
users_collection = str(os.environ["DB_USERS_COLLECTION"])
users_pics_collection = str(os.environ["DB_USERS_PICTURES_COLLECTION"])

@inject
async def get_user(email: str, db: AsyncDatabase, log = log_service) -> User | None:
Expand Down