116 lines
3.8 KiB
Markdown
116 lines
3.8 KiB
Markdown
# Gapido Backend Code Challenge
|
|
|
|
Python gRPC OTP authentication service with MongoDB, Redis, RabbitMQ, and selectable SMS providers.
|
|
|
|
## Architecture
|
|
|
|
- `auth-service`: async `grpc.aio` API for OTP login, token refresh, token revocation, and RBAC demo methods.
|
|
- `sms-worker`: RabbitMQ consumer that sends OTP messages through the configured SMS provider.
|
|
- MongoDB stores users and refresh-token sessions.
|
|
- Redis stores OTP hashes, TTL, verification attempts, and OTP request rate limits.
|
|
- RabbitMQ decouples authentication from SMS delivery.
|
|
|
|
## Run
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
docker compose -f docker-compose.yml -f docker-compose.local.yml up --build
|
|
```
|
|
|
|
The gRPC service listens on `localhost:50051`. The demo UI is available at `http://localhost:8080`. RabbitMQ management is available at `http://localhost:15672` with `guest` / `guest`.
|
|
|
|
## Production Deployment
|
|
|
|
Production uses Caddy as the only public entrypoint for `https://gapido.amiirkhl.ir`.
|
|
|
|
```bash
|
|
cp .env.production.example .env.production
|
|
docker compose --env-file .env.production -f docker-compose.yml -f docker-compose.prod.yml up -d --build
|
|
```
|
|
|
|
Before running production, set real values in `.env.production`:
|
|
|
|
- `JWT_SECRET_KEY`
|
|
- `ADMIN_MOBILE`
|
|
- `SMS_PROVIDER`
|
|
- the selected SMS provider credentials
|
|
|
|
Only ports `80` and `443` are published in the production Compose overlay. MongoDB, Redis, RabbitMQ, gRPC, and the FastAPI demo service stay private on the Docker network.
|
|
|
|
## SMS Provider
|
|
|
|
The SMS integration uses the Strategy pattern behind the `SmsClient` port. Select the provider with:
|
|
|
|
```env
|
|
SMS_PROVIDER=kavenegar
|
|
```
|
|
|
|
Supported values:
|
|
|
|
- `kavenegar`: uses `KAVENEGAR_API_KEY` and `KAVENEGAR_LOGIN_TEMPLATE`.
|
|
- `sms_ir`: uses `SMS_IR_API_KEY` and `SMS_IR_VERIFY_TEMPLATE_ID`.
|
|
- `debug`: local-only provider that stores the latest OTP in Redis for the demo UI.
|
|
|
|
## Demo UI
|
|
|
|
`demo-app` is a small FastAPI backend-for-frontend, implemented as the sibling package `gapido_demo`, that calls `auth-service` over gRPC. It demonstrates how another microservice consumes the auth service.
|
|
|
|
Available browser actions:
|
|
|
|
- Request and verify OTP.
|
|
- Fetch the local debug OTP when `DEMO_ENABLE_DEBUG_OTP=true`.
|
|
- Call public, authenticated-user, and admin-only gRPC methods.
|
|
- Refresh and revoke tokens.
|
|
|
|
## Local Development
|
|
|
|
```bash
|
|
python -m venv .venv
|
|
pip install -e ".[dev]"
|
|
python -m gapido_auth.tools.generate_proto
|
|
pytest
|
|
```
|
|
|
|
The production Docker image installs runtime dependencies only. Development tools are installed locally through `.[dev]`.
|
|
|
|
## Documentation
|
|
|
|
Project documentation is available in [`docs/`](docs/README.md):
|
|
|
|
- [Architecture](docs/architecture.md)
|
|
- [Implementation decisions](docs/implementation-decisions.md)
|
|
- [Architecture decision records](docs/adr/README.md)
|
|
- [Presentation slides](docs/slides/index.html)
|
|
|
|
## gRPC Methods
|
|
|
|
- `RequestOtp`: public; creates a short-lived OTP and publishes an SMS job.
|
|
- `VerifyOtp`: public; verifies OTP and returns access and refresh tokens.
|
|
- `RefreshToken`: public; rotates refresh token and returns a new token pair.
|
|
- `RevokeRefreshToken`: authenticated; revokes a refresh token session.
|
|
- `PublicPing`: public.
|
|
- `UserOnly`: requires any authenticated active user.
|
|
- `AdminOnly`: requires an authenticated admin.
|
|
|
|
Protected calls use metadata:
|
|
|
|
```text
|
|
authorization: Bearer <access_token>
|
|
```
|
|
|
|
## Postman
|
|
|
|
Postman helper files are available in `postman/`:
|
|
|
|
- `Gapido Auth gRPC.postman_environment.json`
|
|
- `Gapido Auth gRPC.postman_collection.json`
|
|
- `README.md` with gRPC request setup steps
|
|
|
|
## Security Notes
|
|
|
|
- OTP codes are generated with `secrets`, stored only as HMAC hashes in Redis, and expire after 120 seconds by default.
|
|
- OTP requests are rate-limited per mobile number and client identity.
|
|
- Refresh tokens are opaque random values; only SHA-256 hashes are persisted.
|
|
- Refresh tokens rotate on use.
|
|
- SMS providers are hidden behind adapters and mocked in tests.
|