Files
auth-project/readme.md
T
2026-08-29 13:42:54 +03:00

155 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Auth & Authorization Service
Backend application built with **FastAPI + SQLAlchemy**, implementing a custom
authentication and authorization system without using the framework's built-in
auth mechanisms.
## Stack
- **FastAPI** — web framework, routing, dependency injection
- **SQLAlchemy (ORM)** — database access, `sessionmaker`, declarative models
- **python-jose (jwt)** — creating and validating JWT tokens
- **bcrypt / passlib** — password hashing
- **Pydantic v2** — request/response validation schemas
- **Alembic** — database migrations
## Authentication
Authentication is built on **JWT tokens** (Bearer), without server-side sessions.
1. The client sends `POST /protected/token` with `email` (as `username`) and
`password` in `OAuth2PasswordRequestForm` format (form-data).
2. The server looks up the user by email and verifies the password via bcrypt
(`hash_check(plain, hashed)`).
3. If the email/password pair is correct — a JWT is issued with payload
`{"sub": <user.id>}`, signed with a secret key (`ENV.SECRET_STRING`,
algorithm `ENV.ALGORITHM`).
4. The client passes the token in the `Authorization: Bearer <token>` header
with every subsequent request.
5. On protected endpoints, the `get_current_user` dependency:
- decodes the token (`jwt.decode`),
- extracts `sub` (user id),
- loads the user from the DB along with their permissions,
- on any error (invalid/expired token, user not found from the token) —
**401 Unauthorized**.
> Logout in a pure JWT scheme requires either a short token TTL + a refresh
> token, or a blacklist of revoked tokens (a `revoked_tokens` table).
> Alternatively, the token can simply be cleared on the frontend at logout, as
> the simplest option.
## Authorization Model
Instead of the classic `roles / business_elements / access_roles_rules` scheme
(with separate `read/create/update/delete` × `_all`), a simpler **flat
permission model** was chosen, which is easier to demonstrate but covers the
same principles: a user is granted a set of string permissions, and each
endpoint declares which permissions are required to call it.
### Database Tables
**users**
| field | type | description |
|-------------------|-------------|-----------------------------------------------------------------------------|
| id | int, PK | user identifier |
| name | str(64) | first name |
| last_name | str(64) | last name |
| middle_name | str(64) | middle name |
| email | str(255) | email, unique |
| status | bool | whether the account is active (used instead of `is_active` for soft delete) |
| hashed_password | str(255) | bcrypt password hash |
**permissions**
| field | type | description |
|-------------|-----------|--------------------------------------------------------------------------|
| id | int, PK | permission identifier |
| permission | str(64) | permission code: `admin`, `can_view`, `can_create`, `can_edit`, `can_delete` |
**user_permission** (association table, many-to-many)
| field | type | description |
|------------------|------|---------------------------|
| user_id | FK | → users.id |
| permission_id | FK | → permissions.id |
A single user can hold several permissions at once (e.g. `can_view` +
`can_edit`); the `admin` permission grants full access and allows
granting/revoking any permissions, including `admin`, to/from other users.
### Access Check Rules
Every business-logic method (`CrudActions`) explicitly declares the set of
permitted permissions and checks it before performing the action:
| Action | Required permission |
|------------------------------|----------------------------------|
| View user (by email/id) | `can_view` or `admin` |
| Create user | `can_create` or `admin` |
| Update user | `can_edit` or `admin` |
| Delete user | `can_delete` or `admin` |
Additionally, a **privilege escalation protection** rule applies: a user
without the `admin` permission cannot grant the `admin` permission to
themselves or any other user when creating/editing an account.
### Access Error Handling
- **401 Unauthorized** — if the request cannot be matched to a valid logged-in
user (no token, expired/invalid token, user from the token does not exist).
- **403 Forbidden** — the user is identified, but lacks the required
permission for the requested action/resource (including privilege
escalation attempts, attempts to claim someone else's email).
- **404 Not Found** — the requested resource (user, permission) does not
exist.
## API
| Method | Path | Required permission | Description |
|--------|--------------------------------------|--------------------------|----------------------------------------|
| POST | `/protected/token` | — | Login, JWT issuance |
| POST | `/protected/logout` | — | Login, JWT issuance |
| GET | `/protected/get_user_by_email` | `can_view` / `admin` | Get user by email |
| GET | `/protected/get_user_by_id` | `can_view` / `admin` | Get user by id |
| POST | `/protected/create_user` | `can_create` / `admin` | Create user |
| PATCH | `/protected/update_user` | `can_edit` / `admin` | Update own profile, or (for admin/can_edit) someone else's via `target_id` |
| DELETE | `/protected/delete_user` | `can_delete` / `admin` | Delete (deactivate) user |
## Test Data
For demonstrating the system, the DB must contain at least:
Passwords d@d.d, d1@d.d, d2@d.d 12345678
- 1 user with the `admin` permission (full access)
- 1 user with the `can_view` permission (view only)
- 1 user with no permissions (for testing 403)
## Project Structure
```
src/
db/ # ActionsDB — DB access (SQLAlchemy sessions, queries)
model/
database_model/ # SQLAlchemy models (User, Permissions)
user/ # Pydantic schemas (UserOut, UserCreate, UserUpdate, PermissionIn/Out)
env_read/ # Reading env via pydantic settings
service/
crud_actions/ # CrudActions — business logic + access permission checks
JWT/ # JWT (token encoding/decoding), Hash (bcrypt)
auth/ # JWT token creation and verification
errors/ # centralized HTTP errors (401/403/404)
web/ # FastAPI routers (endpoints)
migrations/ # DB migrations
tests/ # tests (empty, not yet created)
```
## Running
```bash
poetry
make run
```
Rename env