# 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. The Compose default uses `MONGO_IMAGE=mongo:4.4.29-focal` because MongoDB 5.0+ requires AVX CPU support. On newer hosts you can override it with a newer MongoDB image, for example `MONGO_IMAGE=mongo:7`. ## 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 ``` ## 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.