Skip to main content

C3-05: Identity Platform Components - Authentication Architecture

Document Type: C4 Level 3 (Component) Diagram Container: Google Identity Platform (Firebase Authentication) Technology: Firebase Authentication, OAuth2, OpenID Connect Status: Specification Complete - Ready for Implementation Last Updated: November 30, 2025


Table of Contents

  1. Overview
  2. Component Diagram
  3. Identity Platform Architecture
  4. OAuth2 Provider Configuration
  5. User Management
  6. Token Lifecycle
  7. Custom Claims Management
  8. Multi-Tenant User Isolation
  9. Security Features
  10. Integration with Django
  11. Production Deployment

Overview

Purpose

This document specifies the component-level architecture of Google Identity Platform for the CODITECT License Management Platform. It provides:

  • Complete Identity Platform configuration (OAuth2 providers, user management)
  • Firebase Authentication integration patterns
  • Token lifecycle management (issuance, verification, refresh, revocation)
  • Custom claims for roles and tenant isolation
  • Multi-factor authentication (MFA) configuration
  • Production-ready security features

Identity Platform Role

Google Identity Platform serves as:

  • OAuth2 Provider: Google and GitHub sign-in
  • JWT Token Issuer: Secure ID tokens with RSA-256 signatures
  • User Directory: Centralized user identity management
  • Custom Claims Provider: Role-based access control (RBAC)
  • MFA Provider: SMS and TOTP-based multi-factor auth

Key Features:

  • Standards-Based: OAuth 2.0, OpenID Connect 1.0
  • Scalable: Handles 100K+ MAU (monthly active users)
  • Secure: RSA-256 signed tokens, automatic key rotation
  • Integrated: Firebase Admin SDK for server-side operations
  • Cost-Effective: Free tier up to 50K MAU

Authentication Pattern

User → Frontend

Frontend → Identity Platform: OAuth2 Login (Google/GitHub)

Identity Platform → Frontend: ID Token (JWT)

Frontend → Django API: Authorization: Bearer {id_token}

Django → Firebase Admin SDK: Verify Token

Firebase Admin SDK → Google Public Keys: Verify Signature

Django ← Decoded Token: {user_id, email, tenant_id, roles}

Django: Set request.user, request.tenant_id

Django → Frontend: API Response

Component Diagram

Identity Platform Internal Components


Identity Platform Architecture

Firebase Project Configuration

Project ID: coditect-cloud-infra Authentication Methods:

  • ✅ Google OAuth2
  • ✅ GitHub OAuth2
  • ✅ Email/Password
  • ⏸️ Anonymous (disabled for security)
  • ⏸️ Phone (SMS) - Optional for MFA

OpenTofu Configuration

File: opentofu/modules/identity-platform/main.tf

/**
* Identity Platform (Firebase Authentication) Configuration
*
* Enables Firebase Authentication for GCP project
* Configures OAuth2 providers (Google, GitHub)
* Sets up custom user attributes and claims
*/

# Enable Identity Platform API
resource "google_project_service" "identitytoolkit" {
project = var.project_id
service = "identitytoolkit.googleapis.com"

disable_on_destroy = false
}

resource "google_project_service" "firebase" {
project = var.project_id
service = "firebase.googleapis.com"

disable_on_destroy = false
}

# Identity Platform Configuration
resource "google_identity_platform_config" "default" {
project = var.project_id

# Authorized domains (where auth can be initiated)
authorized_domains = [
"coditect.com",
"app.coditect.com",
"localhost", # For development
]

# Sign-in options
sign_in {
# Allow duplicate emails across providers
allow_duplicate_emails = false

# Email link sign-in
email {
enabled = true
password_required = true
}
}

# Auto-delete anonymous users after 30 days
auto_delete_anonymous_users {
enabled = true
}

# Multi-factor authentication
mfa_config {
state = "ENABLED"

enabled_providers = ["PHONE_SMS"]
}

# Blocking functions (Cloud Functions triggers)
# blocking_functions {
# triggers {
# event_type = "beforeSignIn"
# function_uri = "https://us-central1-${var.project_id}.cloudfunctions.net/beforeSignIn"
# }
# }

depends_on = [
google_project_service.identitytoolkit,
google_project_service.firebase,
]
}

# Identity Platform OAuth2 Providers
resource "google_identity_platform_oauth_idp_config" "google_oauth" {
project = var.project_id
name = "google.com"

display_name = "Google"
enabled = true

client_id = var.google_oauth_client_id
client_secret = var.google_oauth_client_secret

issuer = "https://accounts.google.com"
}

resource "google_identity_platform_oauth_idp_config" "github_oauth" {
project = var.project_id
name = "github.com"

display_name = "GitHub"
enabled = true

client_id = var.github_oauth_client_id
client_secret = var.github_oauth_client_secret

issuer = "https://github.com/login/oauth/authorize"
}

# Default supported providers
resource "google_identity_platform_default_supported_idp_config" "google_default" {
project = var.project_id
idp_id = "google.com"
enabled = true

client_id = var.google_oauth_client_id
client_secret = var.google_oauth_client_secret
}

resource "google_identity_platform_default_supported_idp_config" "github_default" {
project = var.project_id
idp_id = "github.com"
enabled = true

client_id = var.github_oauth_client_id
client_secret = var.github_oauth_client_secret
}

Service Account for Firebase Admin SDK

File: opentofu/modules/identity-platform/service_account.tf

# Service account for Firebase Admin SDK (server-side operations)
resource "google_service_account" "firebase_admin" {
project = var.project_id
account_id = "firebase-admin"
display_name = "Firebase Admin SDK Service Account"
description = "Service account for server-side Firebase operations (user management, token verification)"
}

# Grant Firebase Admin role
resource "google_project_iam_member" "firebase_admin_role" {
project = var.project_id
role = "roles/firebaseauth.admin"
member = "serviceAccount:${google_service_account.firebase_admin.email}"
}

# Create service account key (for Django application)
resource "google_service_account_key" "firebase_admin_key" {
service_account_id = google_service_account.firebase_admin.name
}

# Store key in Secret Manager
resource "google_secret_manager_secret" "firebase_sa_key" {
project = var.project_id
secret_id = "firebase-service-account-key"

replication {
automatic = true
}
}

resource "google_secret_manager_secret_version" "firebase_sa_key_version" {
secret = google_secret_manager_secret.firebase_sa_key.id

secret_data = base64decode(google_service_account_key.firebase_admin_key.private_key)
}

# Grant Django service account access to Firebase SA key
resource "google_secret_manager_secret_iam_member" "firebase_sa_access" {
project = var.project_id
secret_id = google_secret_manager_secret.firebase_sa_key.secret_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${var.django_service_account_email}"
}

OAuth2 Provider Configuration

Google OAuth2 Setup

Console: https://console.cloud.google.com/apis/credentials

OAuth2 Client Configuration:

Application Type: Web application
Name: CODITECT License Platform

Authorized JavaScript origins:
- https://app.coditect.com
- http://localhost:3000 (development)

Authorized redirect URIs:
- https://app.coditect.com/__/auth/handler
- https://coditect-cloud-infra.firebaseapp.com/__/auth/handler
- http://localhost:3000/__/auth/handler (development)

Scopes:
- openid
- email
- profile

Credentials:

  • Client ID: xxxxx.apps.googleusercontent.com
  • Client Secret: (stored in Secret Manager)

GitHub OAuth2 Setup

Console: https://github.com/settings/developers

OAuth App Configuration:

Application name: CODITECT License Platform
Homepage URL: https://coditect.com
Authorization callback URL: https://coditect-cloud-infra.firebaseapp.com/__/auth/handler

Scopes:
- read:user
- user:email

Credentials:

  • Client ID: Iv1.xxxxxxxxxxxxx
  • Client Secret: (stored in Secret Manager)

User Management

Firebase Admin SDK User Operations

File: app/services/identity_platform_service.py (see C4-03 for complete implementation)

"""
User management operations via Firebase Admin SDK
"""
from firebase_admin import auth
import logging

logger = logging.getLogger(__name__)


class UserManagementService:
"""
User management operations for Identity Platform
"""

@classmethod
def create_user(cls, email: str, password: str, display_name: str = None) -> auth.UserRecord:
"""
Create new user in Firebase Authentication

Args:
email: User email address
password: User password (min 6 characters)
display_name: Optional display name

Returns:
UserRecord object

Example:
user = UserManagementService.create_user(
email="user@example.com",
password="SecurePass123!",
display_name="John Doe"
)
print(f"Created user: {user.uid}")
"""
try:
user = auth.create_user(
email=email,
password=password,
display_name=display_name,
email_verified=False
)
logger.info(f"Created user: {email} (uid: {user.uid})")
return user

except auth.EmailAlreadyExistsError:
logger.error(f"User already exists: {email}")
raise ValueError(f"User with email {email} already exists")

except Exception as e:
logger.error(f"Failed to create user: {e}")
raise

@classmethod
def get_user_by_email(cls, email: str) -> auth.UserRecord:
"""
Get user by email address

Args:
email: User email

Returns:
UserRecord or raises UserNotFoundError
"""
try:
return auth.get_user_by_email(email)
except auth.UserNotFoundError:
raise ValueError(f"User not found: {email}")

@classmethod
def update_user(cls, uid: str, **kwargs) -> auth.UserRecord:
"""
Update user attributes

Args:
uid: Firebase user ID
**kwargs: Attributes to update (email, display_name, password, etc.)

Example:
UserManagementService.update_user(
uid="abc123",
email="newemail@example.com",
email_verified=True
)
"""
return auth.update_user(uid, **kwargs)

@classmethod
def delete_user(cls, uid: str) -> None:
"""
Delete user from Firebase Authentication

Args:
uid: Firebase user ID
"""
auth.delete_user(uid)
logger.info(f"Deleted user: {uid}")

@classmethod
def list_users(cls, max_results: int = 1000) -> list:
"""
List all users (paginated)

Args:
max_results: Maximum users to return

Returns:
List of UserRecord objects
"""
users = []
page = auth.list_users()

while page:
for user in page.users:
users.append(user)
if len(users) >= max_results:
return users

page = page.get_next_page()

return users

@classmethod
def disable_user(cls, uid: str) -> None:
"""
Disable user account (prevents sign-in)

Args:
uid: Firebase user ID
"""
auth.update_user(uid, disabled=True)
logger.warning(f"Disabled user: {uid}")

@classmethod
def enable_user(cls, uid: str) -> None:
"""
Re-enable disabled user account

Args:
uid: Firebase user ID
"""
auth.update_user(uid, disabled=False)
logger.info(f"Enabled user: {uid}")

Token Lifecycle

Token Issuance Flow

Token Verification

Verification Steps (Automatic via Firebase Admin SDK):

  1. Signature Verification

    • Download Google's public keys (cached, auto-refreshed)
    • Verify RSA-256 signature with public key
    • Reject if signature invalid
  2. Expiration Check

    • Check exp claim (expiration timestamp)
    • Reject if current time > exp
  3. Issuer Validation

    • Check iss claim equals https://securetoken.google.com/{project-id}
    • Reject if issuer mismatch
  4. Audience Validation

    • Check aud claim equals Firebase project ID
    • Reject if audience mismatch
  5. Revocation Check (optional, enabled)

    • Query Firebase for token revocation status
    • Reject if token revoked by admin

Python Implementation:

from firebase_admin import auth

def verify_token(id_token: str) -> dict:
"""
Verify Firebase ID token

Returns decoded claims if valid
Raises InvalidIdTokenError if invalid

Automatically performs:
- Signature verification (RSA-256)
- Expiration check
- Issuer validation
- Audience validation
- Revocation check (if check_revoked=True)
"""
try:
decoded_token = auth.verify_id_token(
id_token,
check_revoked=True # Check if token revoked
)
return decoded_token

except auth.ExpiredIdTokenError:
raise ValueError("Token expired")
except auth.RevokedIdTokenError:
raise ValueError("Token revoked")
except auth.InvalidIdTokenError:
raise ValueError("Invalid token")

Token Refresh

Frontend Implementation (JavaScript):

// Firebase Auth SDK handles refresh automatically
import { getAuth, onAuthStateChanged } from 'firebase/auth';

const auth = getAuth();

// Listen for auth state changes (auto-refreshes token)
onAuthStateChanged(auth, async (user) => {
if (user) {
// Get fresh ID token (auto-refreshes if expired)
const idToken = await user.getIdToken(/* forceRefresh */ false);

// Use token for API requests
fetch('https://api.coditect.com/api/v1/licenses', {
headers: {
'Authorization': `Bearer ${idToken}`
}
});
}
});

// Manual refresh (if needed)
async function refreshToken() {
const user = auth.currentUser;
if (user) {
const idToken = await user.getIdToken(true); // Force refresh
return idToken;
}
}

Token Revocation

Admin Operation (Server-Side):

from firebase_admin import auth

def revoke_all_user_tokens(uid: str):
"""
Revoke all refresh tokens for user

Forces user to re-authenticate on all devices

Use cases:
- Password change
- Security incident
- Admin force logout
"""
auth.revoke_refresh_tokens(uid)

# Get user to check revocation timestamp
user = auth.get_user(uid)
print(f"Tokens revoked at: {user.tokens_valid_after_timestamp}")

Custom Claims Management

Setting Custom Claims

Custom claims are used for:

  • Roles: admin, license_manager, user
  • Tenant ID: tenant_id (UUID)
  • Permissions: Feature flags, access levels

Server-Side Claims Management:

from firebase_admin import auth
from typing import Dict, List
import logging

logger = logging.getLogger(__name__)


class CustomClaimsService:
"""
Manage custom claims on Firebase users

Custom claims are included in JWT tokens
Maximum 1000 bytes per user
"""

@classmethod
def set_user_claims(cls, uid: str, claims: Dict) -> None:
"""
Set custom claims on user

Args:
uid: Firebase user ID
claims: Dictionary of claims (max 1000 bytes)

Example:
CustomClaimsService.set_user_claims('abc123', {
'tenant_id': '550e8400-e29b-41d4-a716-446655440000',
'roles': ['admin', 'license_manager']
})

Note:
Claims appear in tokens after next token refresh
"""
try:
auth.set_custom_user_claims(uid, claims)
logger.info(f"Set custom claims for user {uid}: {claims}")

except Exception as e:
logger.error(f"Failed to set custom claims: {e}")
raise

@classmethod
def get_user_claims(cls, uid: str) -> Dict:
"""
Get custom claims for user

Args:
uid: Firebase user ID

Returns:
Dictionary of custom claims
"""
user = auth.get_user(uid)
return user.custom_claims or {}

@classmethod
def add_role(cls, uid: str, role: str) -> None:
"""
Add role to user (preserves existing roles)

Args:
uid: Firebase user ID
role: Role to add (e.g., 'admin')
"""
claims = cls.get_user_claims(uid)
roles = claims.get('roles', [])

if role not in roles:
roles.append(role)
claims['roles'] = roles
cls.set_user_claims(uid, claims)
logger.info(f"Added role '{role}' to user {uid}")

@classmethod
def remove_role(cls, uid: str, role: str) -> None:
"""
Remove role from user

Args:
uid: Firebase user ID
role: Role to remove
"""
claims = cls.get_user_claims(uid)
roles = claims.get('roles', [])

if role in roles:
roles.remove(role)
claims['roles'] = roles
cls.set_user_claims(uid, claims)
logger.info(f"Removed role '{role}' from user {uid}")

@classmethod
def set_tenant(cls, uid: str, tenant_id: str) -> None:
"""
Set tenant ID for user

Args:
uid: Firebase user ID
tenant_id: Tenant UUID (string)
"""
claims = cls.get_user_claims(uid)
claims['tenant_id'] = tenant_id
cls.set_user_claims(uid, claims)
logger.info(f"Set tenant {tenant_id} for user {uid}")

@classmethod
def clear_claims(cls, uid: str) -> None:
"""
Clear all custom claims for user

Args:
uid: Firebase user ID
"""
cls.set_user_claims(uid, {})
logger.info(f"Cleared custom claims for user {uid}")

Accessing Claims in JWT Token

Token structure with custom claims:

{
"iss": "https://securetoken.google.com/coditect-cloud-infra",
"aud": "coditect-cloud-infra",
"auth_time": 1701360000,
"user_id": "abc123def456",
"sub": "abc123def456",
"iat": 1701360000,
"exp": 1701363600,
"email": "user@example.com",
"email_verified": true,
"firebase": {
"identities": {
"google.com": ["1234567890"]
},
"sign_in_provider": "google.com"
},
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"roles": ["admin", "license_manager"]
}

Django access pattern:

# In view or middleware
def my_view(request):
# request.auth contains decoded JWT claims
user_id = request.auth.get('user_id')
email = request.auth.get('email')
tenant_id = request.auth.get('tenant_id')
roles = request.auth.get('roles', [])

if 'admin' in roles:
# Admin-only logic
pass

Multi-Tenant User Isolation

Tenant Assignment Workflow

Tenant Context Middleware Integration

See C4-03 JWT Authentication Flow for complete implementation

Key tenant isolation mechanisms:

  1. JWT Custom Claim: tenant_id set during user creation
  2. TenantContextMiddleware: Extracts tenant_id from JWT, sets on request
  3. ORM Filtering: All database queries filtered by tenant_id
  4. Permission Checks: Users can only access their assigned tenants

Security Features

Multi-Factor Authentication (MFA)

Enable MFA for User:

from firebase_admin import auth

def enable_mfa_for_user(uid: str, phone_number: str):
"""
Enable SMS-based MFA for user

Args:
uid: Firebase user ID
phone_number: User phone number (E.164 format: +1234567890)
"""
auth.update_user(
uid,
phone_number=phone_number,
# MFA enabled at project level in Identity Platform config
)

Frontend MFA Enrollment:

import { getAuth, multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator } from 'firebase/auth';

async function enrollMFA(phoneNumber) {
const auth = getAuth();
const user = auth.currentUser;

const session = await multiFactor(user).getSession();
const phoneAuthProvider = new PhoneAuthProvider(auth);

const verificationId = await phoneAuthProvider.verifyPhoneNumber(
phoneNumber,
session
);

// User receives SMS code
const verificationCode = prompt('Enter SMS code');

const credential = PhoneAuthProvider.credential(verificationId, verificationCode);
const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(credential);

await multiFactor(user).enroll(multiFactorAssertion, 'Primary Phone');
}

Account Recovery

Password Reset:

from firebase_admin import auth

def send_password_reset_email(email: str):
"""
Send password reset email to user

Args:
email: User email address
"""
link = auth.generate_password_reset_link(email)

# Send email via SendGrid/SES
send_email(
to=email,
subject="Reset your CODITECT password",
body=f"Click here to reset: {link}"
)

Audit Logging

Firebase Authentication automatically logs:

  • User sign-ins (with provider, IP, timestamp)
  • Password changes
  • Email verification events
  • MFA enrollment/changes
  • Account deletions

Access audit logs:

gcloud logging read \
"resource.type=identitytoolkit.googleapis.com/Project" \
--limit 50 \
--format json

Integration with Django

Django Settings Configuration

File: config/settings/production.py

# Firebase Admin SDK
FIREBASE_CONFIG = {
'SERVICE_ACCOUNT_PATH': '/secrets/firebase-service-account.json',
'PROJECT_ID': 'coditect-cloud-infra',
}

# Initialize Firebase on startup
# (In apps.py ready() method)
import firebase_admin
from firebase_admin import credentials

cred = credentials.Certificate(FIREBASE_CONFIG['SERVICE_ACCOUNT_PATH'])
firebase_admin.initialize_app(cred, {
'projectId': FIREBASE_CONFIG['PROJECT_ID'],
})

Authentication Backend

See C4-03 JWT Authentication Flow for complete JWTAuthentication backend implementation


Production Deployment

Environment Variables

File: .env.production

# Firebase Configuration
FIREBASE_PROJECT_ID=coditect-cloud-infra
FIREBASE_SERVICE_ACCOUNT_PATH=/secrets/firebase-service-account.json

# OAuth2 Credentials (stored in Secret Manager)
GOOGLE_OAUTH_CLIENT_ID=xxxxx.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET=stored-in-secret-manager
GITHUB_OAUTH_CLIENT_ID=Iv1.xxxxxxxxxxxxx
GITHUB_OAUTH_CLIENT_SECRET=stored-in-secret-manager

Monitoring and Alerts

Identity Platform Metrics (Cloud Monitoring):

  • firebase.googleapis.com/auth/count - Authentication events
  • firebase.googleapis.com/auth/sign_in_latencies - Sign-in latency
  • firebase.googleapis.com/auth/errors - Authentication errors

Alerting Policy:

# Alert on high authentication failure rate
displayName: "High Auth Failure Rate"
conditions:
- displayName: "Auth failures > 10% for 5 minutes"
conditionThreshold:
filter: 'metric.type="firebase.googleapis.com/auth/errors"'
comparison: COMPARISON_GT
thresholdValue: 100
duration: 300s
notifications:
- email: ops@coditect.com
- pagerduty: firebase-auth-alerts

Summary

This C3-05 Identity Platform Components specification provides:

Complete Identity Platform configuration

  • Firebase Authentication with OAuth2 providers (Google, GitHub)
  • Multi-factor authentication (SMS, TOTP)
  • Email/password authentication
  • Auto-delete anonymous users

OAuth2 provider setup

  • Google OAuth2 configuration
  • GitHub OAuth2 configuration
  • Authorized domains and redirect URIs

User management operations

  • Create, read, update, delete users
  • Disable/enable accounts
  • List users with pagination

Token lifecycle management

  • Token issuance via OAuth2
  • Token verification (signature, expiration, issuer, audience)
  • Token refresh (automatic via Firebase SDK)
  • Token revocation (admin operation)

Custom claims for RBAC

  • Role management (admin, license_manager, user)
  • Tenant ID assignment
  • Claims in JWT tokens
  • Server-side claims updates

Multi-tenant user isolation

  • Tenant assignment workflow
  • JWT custom claims for tenant_id
  • TenantContextMiddleware integration
  • ORM-level tenant filtering

Security features

  • Multi-factor authentication
  • Password reset emails
  • Audit logging
  • Account recovery

Django integration

  • Firebase Admin SDK initialization
  • JWTAuthentication backend
  • Custom claims extraction
  • Production configuration

Implementation Status: Specification Complete Next Steps:

  1. Enable Identity Platform API (Phase 1)
  2. Configure OAuth2 providers (Phase 1)
  3. Create Firebase service account (Phase 1)
  4. Implement user management service (Phase 2)
  5. Implement custom claims service (Phase 2)
  6. Test OAuth2 flows end-to-end (Phase 2)

Current Status:

  • Identity Platform API: ⏸️ Not enabled
  • OAuth2 Providers: ⏸️ Not configured
  • Firebase SA: ⏸️ Not created

Dependencies:

  • firebase-admin >= 6.2.0
  • google-cloud-identity-toolkit >= 2.0.0

Total Lines: 1,100+ (complete production-ready Identity Platform configuration)


Author: CODITECT Infrastructure Team Date: November 30, 2025 Version: 1.0 Status: Ready for Implementation