Skip to content

Commit

Permalink
v1
Browse files Browse the repository at this point in the history
  • Loading branch information
Kalim authored and Kalim committed Sep 17, 2023
0 parents commit 0ac4791
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 0 deletions.
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Authentication App

This is a flexible authentication app that allows you to choose between MongoDB and PostgreSQL as the backend database.

## Installation

1. Clone this repository:
```
git clone https://github.com/yourusername/my_authentication_app.git
cd authentication_app
```


2. Create a virtual environment (optional but recommended:
```
python -m venv venv
source venv/bin/activate
```


3. Install the required dependencies:

```
pip install -r requirements.txt
```


## Configuration

You can configure the choice of backend database in the `authentication/config.py` file by setting the `use_database` parameter to either "postgresql" or "mongodb."

## Usage

1. Run the application:

uvicorn my_authentication_app.main:app --reload


2. Access the Swagger documentation at http://localhost:8000/docs to interact with the API endpoints.



## Integrate with any applications

```
from authentication_app.authentication.auth import Auth
from fastapi import Depends, FastAPI
app = FastAPI()
auth = Auth() # Create an instance of the Auth class
@app.get("/protected", response_model=str)
def protected_endpoint(current_user: str = Depends(auth.get_current_user)):
# Your protected endpoint logic here
return "This is a protected endpoint."
```


## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

Empty file added authentication_app/__init__.py
Empty file.
Empty file.
102 changes: 102 additions & 0 deletions authentication_app/authentication/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# my_authentication_app/authentication/auth.py
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import HTTPException, Depends, Security
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from pymongo.collection import Collection
from pymongo.database import Database
from pydantic import BaseModel

from .config import config
from .db_models import UserDB as PostgresUserDB
from .mongodb_models import UserDB as MongoDBUserDB
from .database import Database

# OAuth2PasswordBearer for token authentication
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Pydantic models
class UserCreate(BaseModel):
username: str
password: str

class User(BaseModel):
username: str
is_superuser: bool

class Token(BaseModel):
access_token: str
token_type: str

# Password hashing
passlib_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# Authentication class
class Auth:
def __init__(self, db: Database):
self.db = db

def create_user(self, user: UserCreate, is_superuser: bool = False):
if self.db.config.use_database == "postgresql":
# Create user in PostgreSQL
hashed_password = self.get_password_hash(user.password)
db_user = PostgresUserDB(username=user.username, hashed_password=hashed_password, is_superuser=is_superuser)
db = self.db.get_session()
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
elif self.db.config.use_database == "mongodb":
user_data = MongoDBUserCreate(username=user.username, hashed_password=user.password, is_superuser=is_superuser)
user_id = self.db.user_collection.insert_one(user_data.dict()).inserted_id
return user_id
else:
raise ValueError("Invalid database type. Please choose 'postgresql' or 'mongodb'.")

def create_access_token(self, data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=config.access_token_expire_minutes)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, config.secret_key, algorithm=config.algorithm)
return encoded_jwt

def verify_password(self, plain_password, hashed_password):
return passlib_context.verify(plain_password, hashed_password)

def get_password_hash(self, password):
return passlib_context.hash(password)

def get_current_user(self, token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, config.secret_key, algorithms=[config.algorithm])
username: str = payload.get("sub")
if username is None:
raise HTTPException(status_code=401, detail="Could not validate credentials")
token_data = TokenData(username=username)
except JWTError:
raise HTTPException(status_code=401, detail="Could not validate credentials")

if self.db.config.use_database == "postgresql":
db_user = self.db.get_session().query(PostgresUserDB).filter(PostgresUserDB.username == token_data.username).first()
if db_user is None:
raise HTTPException(status_code=401, detail="User not found")
return User(username=db_user.username, is_superuser=db_user.is_superuser)
elif self.db.config.use_database == "mongodb":
user_data = self.db.user_collection.find_one({"username": token_data.username})
if user_data is None:
raise HTTPException(status_code=401, detail="User not found")
user_db = MongoDBUserDB(**user_data)
return User(username=user_db.username, is_superuser=user_db.is_superuser)
else:
raise ValueError("Invalid database type. Please choose 'postgresql' or 'mongodb'.")

def get_current_active_user(current_user: User = Security(Auth.get_current_user, scopes=["superuser"])):
# Check user permissions
if not current_user.is_superuser:
raise HTTPException(status_code=403, detail="Insufficient permissions")
return current_user
17 changes: 17 additions & 0 deletions authentication_app/authentication/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# my_authentication_app/authentication/config.py
from pydantic import BaseSettings

class AuthConfig(BaseSettings):
secret_key: str = "your-secret-key" # Replace with a secure secret key
algorithm: str = "HS256"
access_token_expire_minutes: int = 60
use_database: str = "postgresql" # Default to PostgreSQL

# PostgreSQL configuration
postgresql_uri: str = "postgresql://username:password@localhost/database_name"

# MongoDB configuration
mongodb_uri: str = "mongodb://localhost:27017/"
mongodb_db_name: str = "my_auth_db"

config = AuthConfig()
26 changes: 26 additions & 0 deletions authentication_app/authentication/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# my_authentication_app/authentication/database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from pymongo import MongoClient
from pymongo.collection import Collection
from pymongo.database import Database
from pydantic import BaseSettings
import config
class Database:
def __init__(self, config: AuthConfig):
self.config = config

def get_session(self):
if self.config.use_database == "postgresql":
engine = create_engine(self.config.postgresql_uri)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
return SessionLocal()
elif self.config.use_database == "mongodb":
client = MongoClient(self.config.mongodb_uri)
db = client[self.config.mongodb_db_name]
return db
else:
raise ValueError("Invalid database type. Please choose 'postgresql' or 'mongodb'.")

# config = AuthConfig()
db = Database(config)
Empty file.
20 changes: 20 additions & 0 deletions authentication_app/authentication/mongodb_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# my_authentication_app/authentication/mongodb_models.py
from pydantic import BaseModel
from bson import ObjectId
from typing import Optional

class MongoDBUserDB(BaseModel):
id: Optional[ObjectId] # Optional for new user creation
username: str
hashed_password: str
is_superuser: bool

class MongoDBTokenDB(BaseModel):
id: ObjectId
access_token: str
token_type: str

class MongoDBUserCreate(BaseModel):
username: str
hashed_password: str
is_superuser: bool
13 changes: 13 additions & 0 deletions authentication_app/authentication/postgresql_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# my_authentication_app/authentication/postgresql_models.py
from sqlalchemy import Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class UserDB(Base):
__tablename__ = "users"

id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_superuser = Column(Boolean, default=False)
8 changes: 8 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fastapi==0.68.1
uvicorn==0.15.0
passlib[bcrypt]==1.7.4
pydantic==1.8.3
jose[cryptography]==1.3.2
asyncpg==0.24.0 # PostgreSQL driver (optional)
pymongo==3.12.1 # MongoDB driver (optional)
python-multipart==0.0.5
17 changes: 17 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from setuptools import setup, find_packages

setup(
name="authentication_app",
version="0.1",
packages=find_packages(),
install_requires=[
"fastapi==0.68.1",
"uvicorn==0.15.0",
"passlib[bcrypt]==1.7.4",
"pydantic==1.8.3",
"jose[cryptography]==1.3.2",
"asyncpg==0.24.0", # PostgreSQL driver (optional)
"pymongo==3.12.1", # MongoDB driver (optional)
"python-multipart==0.0.5",
],
)

0 comments on commit 0ac4791

Please sign in to comment.