vikrant892/password-strength-api
0
1from fastapi import FastAPI
2from fastapi.middleware.cors import CORSMiddleware
3from fastapi.staticfiles import StaticFiles
4from fastapi.responses import FileResponse
5import os
6
7from app.models import (
8 AnalyzeRequest, AnalyzeResponse,
9 GenerateRequest, GenerateResponse,
10 HealthResponse,
11)
12from app.analyzer import analyze_password
13from app.breach_check import check_breach
14from app.generator import generate_password
15
16app = FastAPI(
17 title="Password Strength Analyzer",
18 description="Analyze password strength, check breaches, generate secure passwords",
19 version="0.1.0",
20)
21
22# CORS - configurable via environment variable, defaults to localhost for safety
23_allowed_origins = os.environ.get("CORS_ORIGINS", "http://localhost:3000").split(",")
24app.add_middleware(
25 CORSMiddleware,
26 allow_origins=_allowed_origins,
27 allow_credentials=False,
28 allow_methods=["GET", "POST"],
29 allow_headers=["Content-Type"],
30)
31
32
33@app.get("/health", response_model=HealthResponse)
34async def health():
35 return HealthResponse()
36
37
38@app.post("/analyze", response_model=AnalyzeResponse)
39async def analyze(req: AnalyzeRequest):
40 result = analyze_password(req.password)
41
42 # optionally check HIBP - off by default since it makes a network call
43 if req.check_breach:
44 breach_result = await check_breach(req.password)
45 result.update(breach_result)
46
47 return AnalyzeResponse(**result)
48
49
50@app.post("/generate", response_model=GenerateResponse)
51async def generate(req: GenerateRequest):
52 password = generate_password(
53 length=req.length,
54 uppercase=req.uppercase,
55 lowercase=req.lowercase,
56 digits=req.digits,
57 symbols=req.symbols,
58 exclude_ambiguous=req.exclude_ambiguous,
59 )
60
61 # run the analyzer on the generated password so we can show the score
62 analysis = analyze_password(password)
63
64 return GenerateResponse(
65 password=password,
66 score=analysis["score"],
67 entropy_bits=analysis["entropy_bits"],
68 )
69
70
71# serve the demo frontend
72static_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static")
73if os.path.isdir(static_dir):
74 app.mount("/static", StaticFiles(directory=static_dir), name="static")
75
76
77@app.get("/")
78async def root():
79 index_path = os.path.join(static_dir, "index.html")
80 if os.path.exists(index_path):
81 return FileResponse(index_path)
82 return {"message": "Password Strength API - see /docs for API documentation"}
83 