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.