Skip to main content

Overview

The Water Quality Backend API uses JWT (JSON Web Tokens) for authentication, backed by Firebase Authentication for credential verification and user management. The API also supports GitHub OAuth for social login.

Authentication Methods

The API supports three authentication methods:
  1. Email/Password: Traditional registration and login
  2. GitHub OAuth: Social login via GitHub
  3. Password Reset: Secure password recovery flow

Registration

Create a new user account with email and password.

Endpoint

POST /auth/register/

Request Body

{
  "email": "[email protected]",
  "username": "john_doe",
  "password": "SecurePass123!",
  "phone": "+1234567890"  // optional
}

Response

{
  "message": "Registered successfully"
}

Example with cURL

curl -X POST https://api.example.com/auth/register/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "username": "john_doe",
    "password": "SecurePass123!"
  }'
The user is automatically created in Firebase Authentication and assigned the CLIENT role by default.

Login

Authenticate with email and password to receive a JWT access token.

Endpoint

POST /auth/login/

Request Body

{
  "email": "[email protected]",
  "password": "SecurePass123!"
}

Response

{
  "message": "Logged in successfully",
  "user": {
    "uid": "firebase_uid_123",
    "email": "[email protected]",
    "username": "john_doe",
    "phone": "+1234567890",
    "rol": "client"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Example with cURL

curl -X POST https://api.example.com/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "SecurePass123!"
  }'

Authentication Flow

1

Submit Credentials

Client sends email and password to /auth/login/
2

Verify with Firebase

Backend verifies credentials using Firebase Authentication API:
POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={API_KEY}
3

Generate JWT Token

If credentials are valid, create a JWT token with user payload:
payload = {
  "uid": user.uid,
  "email": user.email,
  "username": user.username,
  "phone": user.phone,
  "rol": user.rol,
  "exp": time.time() + 2592000  # 30 days
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
4

Return Token

Send JWT token to client along with user data

Using Access Tokens

Once authenticated, include the JWT token in the Authorization header for all protected endpoints.

Authorization Header

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Example Request

curl -X GET https://api.example.com/workspaces/ \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token Verification

Protected endpoints verify the token using the AccessToken service:
from app.share.jwt.infrastructure.access_token import AccessToken

access_token = AccessToken()
payload = access_token.validate(token)
# Returns: {"uid": "...", "email": "...", "username": "...", "rol": "...", "exp": ...}
Tokens expire after 30 days (2,592,000 seconds). Clients should handle token expiration and re-authenticate.

JWT Token Structure

Payload

The JWT token contains the following claims:
{
  "uid": "firebase_uid_123",
  "email": "[email protected]",
  "username": "john_doe",
  "phone": "+1234567890",
  "rol": "client",
  "exp": 1709654400
}

Claims Description

  • uid: Firebase user ID (unique identifier)
  • email: User’s email address
  • username: User’s display name
  • phone: User’s phone number (optional)
  • rol: User role (client, admin, etc.)
  • exp: Expiration timestamp (Unix epoch)

Algorithm

Tokens are signed using HS256 (HMAC with SHA-256) with the SECRET_KEY from environment variables.

GitHub OAuth

The API supports GitHub OAuth for seamless social login.

OAuth Flow

1

Initiate OAuth

Redirect user to GitHub authorization:
GET /auth/github/login/web
or for mobile apps:
GET /auth/github/login/mobile
2

GitHub Authorization

User authorizes the app on GitHub and is redirected back to:
/auth/github/callback?code=...&state=...
3

Exchange Code for Token

Backend exchanges the authorization code for a GitHub access token:
POST https://github.com/login/oauth/access_token
{
  "client_id": GITHUB_CLIENT_ID,
  "client_secret": GITHUB_CLIENT_SECRET,
  "code": code,
  "redirect_uri": GITHUB_CALLBACK_URL
}
4

Fetch User Profile

Retrieve user information from GitHub:
GET https://api.github.com/user
GET https://api.github.com/user/emails
5

Create or Login User

If user doesn’t exist, create account with GitHub email/username. If user exists, log them in.
6

Redirect with Token

Redirect to frontend with JWT token:
  • Web: {FRONTEND_ORIGIN}?token=...&email=...&username=...
  • Mobile: aquaminds://login-success?token=...&code=...

Web OAuth Example

# Step 1: Redirect user to GitHub login
curl -L https://api.example.com/auth/github/login/web

# Step 2: User authorizes on GitHub
# Step 3: User is redirected to frontend with token
https://app.example.com?token=eyJhbG...&email=[email protected]&username=john_doe&rol=client&uid=abc123

Mobile OAuth Example

# Step 1: Redirect user to GitHub login
curl -L https://api.example.com/auth/github/login/mobile

# Step 2: User authorizes on GitHub
# Step 3: User is redirected to mobile app via deep link
aquaminds://login-success?token=eyJhbG...&code=github_code

State Parameter Security

The OAuth flow uses a signed state parameter to prevent CSRF attacks:
state_payload = {
    "redirect_uri": FRONTEND_ORIGIN,
    "exp": int(time.time()) + 600  # 10 minutes
}
state = sign_state(state_payload)  # HMAC-signed with STATE_SECRET
The state is verified on callback to ensure authenticity.
Security: The STATE_SECRET environment variable must be set for OAuth to work. This secret is used to sign and verify the state parameter.

Password Reset Flow

Secure password recovery using email verification codes.

Step 1: Request Password Reset

POST /auth/request-password-reset/
Request:
{
  "email": "[email protected]"
}
Response:
{
  "message": "Código de verificación enviado"
}
This generates a 6-digit verification code stored in Firebase Realtime Database with a 10-minute expiration:
reset_code = random.randint(100000, 999999)
expire_date = datetime.now() + timedelta(minutes=10)

db.reference(f"/password_reset/{user.uid}").set({
    "code": reset_code,
    "expires": expire_date.timestamp()
})
The code is sent to the user’s email via Resend API.

Step 2: Verify Reset Code

POST /auth/verify-reset-code/
Request:
{
  "email": "[email protected]",
  "code": 123456
}
Response:
{
  "message": "Código de verificación válido",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
This validates the code and returns a short-lived token for password reset.

Step 3: Reset Password

POST /auth/reset-password/?token={reset_token}
Request:
{
  "new_password": "NewSecurePass123!"
}
Response:
{
  "message": "Contraseña actualizada con éxito",
  "user": {
    "uid": "firebase_uid_123",
    "email": "[email protected]",
    "username": "john_doe"
  }
}

Complete Password Reset Example

# Step 1: Request reset
curl -X POST https://api.example.com/auth/request-password-reset/ \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]"}'

# Step 2: Verify code from email
curl -X POST https://api.example.com/auth/verify-reset-code/ \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "code": 123456}'
# Returns: {"message": "...", "token": "xyz..."}

# Step 3: Reset password with token
curl -X POST 'https://api.example.com/auth/reset-password/?token=xyz...' \
  -H "Content-Type: application/json" \
  -d '{"new_password": "NewSecurePass123!"}'

User Roles

The API supports role-based access control (RBAC):
  • client: Standard user (default)
  • admin: Administrative privileges
Roles are stored in the JWT token and can be used for authorization:
from app.share.jwt.infrastructure.access_token import AccessToken

def get_current_user(token: str = Depends(oauth2_scheme)):
    payload = AccessToken().validate(token)
    if payload["rol"] != "admin":
        raise HTTPException(status_code=403, detail="Admin access required")
    return payload

Environment Configuration

Authentication requires the following environment variables:
SECRET_KEY
string
required
Secret key for signing JWT tokens (HS256 algorithm)
FIREBASE_API_KEY
string
required
Firebase API key for authentication requests
GITHUB_CLIENT_ID
string
required
GitHub OAuth app client ID
GITHUB_CLIENT_SECRET
string
required
GitHub OAuth app client secret
GITHUB_CALLBACK_URL
string
required
OAuth callback URL (e.g., https://api.example.com/auth/github/callback)
FRONTEND_ORIGIN
string
required
Frontend URL for web OAuth redirects (e.g., https://app.example.com/oauth/callback)
STATE_SECRET
string
required
Secret for HMAC-signing OAuth state parameter (prevent CSRF)
Mobile app deep link scheme (default: aquaminds://login-success)
See the Environment Variables guide for complete configuration.

Error Handling

The authentication endpoints return standard HTTP error codes:
Status CodeDescription
400Invalid request body or parameters
401Invalid credentials or expired token
404User not found
500Server error or Firebase error
502GitHub API communication error
504GitHub API timeout

Example Error Response

{
  "detail": "Credenciales inválidas"
}

Security Best Practices

Never expose SECRET_KEY: Keep your JWT secret key secure and never commit it to version control.
Use HTTPS: Always use HTTPS in production to prevent token interception.

Recommendations

  1. Rotate Secrets: Periodically rotate SECRET_KEY and STATE_SECRET
  2. Short Expiration: Consider shorter token expiration for sensitive applications
  3. Refresh Tokens: Implement refresh tokens for better security (currently not implemented)
  4. Rate Limiting: Add rate limiting to prevent brute force attacks
  5. Password Complexity: Enforce strong password requirements
  6. Email Verification: Consider adding email verification for new registrations

Next Steps

Environment Variables

Configure Firebase, GitHub OAuth, and other auth settings

API Reference

Explore all authentication endpoints in detail

Build docs developers (and LLMs) love