Gapido Backend Code Challenge
Python gRPC OTP authentication service with MongoDB, Redis, RabbitMQ, and selectable SMS providers.
Architecture
auth-service: asyncgrpc.aioAPI 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
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.
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_KEYADMIN_MOBILESMS_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:
SMS_PROVIDER=kavenegar
Supported values:
kavenegar: usesKAVENEGAR_API_KEYandKAVENEGAR_LOGIN_TEMPLATE.sms_ir: usesSMS_IR_API_KEYandSMS_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
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/:
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:
authorization: Bearer <access_token>
Postman
Postman helper files are available in postman/:
Gapido Auth gRPC.postman_environment.jsonGapido Auth gRPC.postman_collection.jsonREADME.mdwith 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.