An OAuth2 authorization server with Time-based One-Time Password (TOTP) authentication built with Flask and Authlib.
Production-ready OAuth2 & 2FA (TOTP) server built with Flask — ideal for academic and ed-tech systems.
🌐 Live Demonstration: https://web-production-7a862.up.railway.app/docs/
Try the interactive API documentation and test all endpoints directly in your browser.
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Register │─────▶│ QR Code │─────▶│ Verify │ │ User │ │ (TOTP) │ │ OTP │ └─────────────┘ └──────────────┘ └─────────────┘ │ ▼ ┌──────────────┐ │ Scan in │ │ Authenticator│ └──────────────┘ │ ▼ ┌──────────────┐ │ Get OAuth2 │ │ Token │ └──────────────┘
Interactive API Documentation with Swagger UI
QR Code for TOTP Authentication
- 🔐 OAuth2 Authorization Server (RFC 6749)
- 🔑 TOTP/OTP Authentication (Google Authenticator compatible)
- 🎫 Multiple Grant Types: Password, Client Credentials, Refresh Token
- 🛡️ Secure token management
- 📱 QR Code generation for OTP setup
- 🚀 Railway deployment ready
- 💾 SQLite/PostgreSQL support
- Python 3.9+
- Poetry Package Manager
# Clone the repository git clone https://github.com/moduguvikram/security-services.git cd security-services # Install dependencies poetry install # Configure environment (optional) cp .env.example .env # Edit .env with your settings# Set development mode export FLASK_ENV=development # Run the server (with HTTPS) poetry run python3 src/auth_server/app.pyServer runs at: https://127.0.0.1:5001
See Deployment Guide below.
Interactive Documentation: https://web-production-7a862.up.railway.app/docs/
All examples below use local development URLs. Replace https://127.0.0.1:5001 with https://web-production-7a862.up.railway.app for production.
Register a new user and get OTP setup information.
curl -X POST https://127.0.0.1:5000/register_user \ -H "Content-Type: application/json" \ -d '{"username":"testUser","password":"testPassword123"}' -kResponse:
{ "message": "User registered", "otp_uri": "otpauth://totp/ThiaOAuthServer:testUser?secret=ABCD1234...", "qr_code_url": "https://127.0.0.1:5000/qr_code/testUser" }Retrieve the QR code as a PNG image.
curl https://127.0.0.1:5000/qr_code/testUser -k > qr_code.pngOr open in browser: https://127.0.0.1:5000/qr_code/testUser
Scan the QR code in Google Authenticator or any TOTP authenticator app.
Verify the OTP code from authenticator app.
curl -X POST https://127.0.0.1:5000/verify_otp \ -H "Content-Type: application/json" \ -d '{"username":"testUser","code":"123456"}' -kResponse:
{ "valid": true }Create a new OAuth2 client application.
curl -X POST https://127.0.0.1:5000/create_client \ -H "Content-Type: application/json" \ -d '{"client_name":"MyApp","redirect_uri":"https://127.0.0.1:5000/callback"}' -kOptional parameters:
grant_types: Array of grant types (default:["client_credentials", "authorization_code", "password"])scope: Space-separated scopes (default:"profile email")
Response:
{ "client_id": "4R0o9atlUvUvAWhNNihPSsLN", "client_secret": "J3oq2uHDGdJQbz7zlwjOSnECYqplEXj34eH3SCOzK0liV4kO" }Save the client_id and client_secret.
Get an access token using user credentials (requires verified OTP).
curl -X POST https://127.0.0.1:5000/token \ -u "CLIENT_ID:CLIENT_SECRET" \ -d "grant_type=password&username=testUser&password=testPassword123" -kResponse:
{ "access_token": "MTV3IBXhMhrs6RnKAx6jJ0wgXZN1ooq5pbXHI7F2YE", "expires_in": 864000, "refresh_token": "g67yHraHSKeShhelY8VVjfcjEDwx9iAbAcBbdno1nDvsV5KU", "token_type": "Bearer" }Get an access token for machine-to-machine authentication.
curl -X POST https://127.0.0.1:5000/token \ -u "CLIENT_ID:CLIENT_SECRET" \ -d "grant_type=client_credentials&scope=profile" -kResponse:
{ "access_token": "GPSlmILLMF3W1GMNyVqJN4GSnwmuiKr02zbT4iaZ1f", "expires_in": 864000, "scope": "profile", "token_type": "Bearer" }Refresh an expired access token.
curl -X POST https://127.0.0.1:5000/token \ -u "CLIENT_ID:CLIENT_SECRET" \ -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" -kAccess a protected endpoint using the access token.
curl https://127.0.0.1:5000/profile \ -H "Authorization: Bearer ACCESS_TOKEN" -kResponse (User token):
{ "type": "user", "username": "testUser", "otp_verified": true }Response (Client token):
{ "type": "client", "client_id": "4R0o9atlUvUvAWhNNihPSsLN", "message": "Machine token access" }- Push code to GitHub
- Connect repository to Railway
- Add environment variables:
SECRET_KEY: Random secret string- Optional: Add PostgreSQL database
- Deploy automatically
DATABASE_URL: Database connection string (default: SQLite)SECRET_KEY: Flask secret key (required for production)PORT: Server port (default: 5000)FLASK_ENV: Set todevelopmentfor local dev
- Always use HTTPS in production
- Set a strong
SECRET_KEY - Use PostgreSQL for production (not SQLite)
- Implement rate limiting for production
- Review token expiration settings
See CONTRIBUTING.md for guidelines.
MIT License - see LICENSE file.
Create an issue on GitHub for bugs or feature requests.