Implementation:Arize ai Phoenix Polymorphic User Vignette
Overview
The users.py file is an internal vignette demonstrating SQLAlchemy's Single Table Inheritance (STI) pattern for polymorphic user authentication. It models a User base class with two concrete subclasses -- LocalUser (password-based authentication) and ExternalUser (OAuth/SSO) -- all stored in a single users database table. The discriminator column auth_method determines which Python class is instantiated for each row.
This pattern directly informs the authentication architecture used in the Phoenix server, which supports both local and external (OAuth2/OIDC/LDAP) authentication methods.
Code Reference
| Attribute | Value |
|---|---|
| Source File | internal_docs/vignettes/sqlalchemy/polymorphic_user/users.py |
| Lines | 354 |
| Domain | Internal_Documentation, Database |
| Language | Python |
| Dependencies | SQLAlchemy 2.0, bcrypt |
Class Hierarchy
Base (DeclarativeBase)
+-- User (abstract, polymorphic_on="auth_method")
+-- LocalUser (polymorphic_identity="local")
+-- ExternalUser (polymorphic_identity="external")
Key Classes
User (Abstract Base)
| Column | Type | Description |
|---|---|---|
id |
int (primary key) |
Auto-incrementing primary key |
email |
str (unique) |
User email address |
auth_method |
AuthMethod ("local" or "external") |
Discriminator column with check constraint |
password_hash |
Optional[bytes] |
bcrypt password hash (NULL for external users) |
password_salt |
Optional[bytes] |
bcrypt salt (NULL for external users) |
Table constraints:
auth_method IN ('local', 'external')-- ensures valid discriminator values(password_hash IS NULL) = (password_salt IS NULL)-- ensures password fields are consistent (both NULL or both populated)- Direct instantiation of the
Userbase class raisesTypeError
LocalUser
Represents users authenticating with email and password. The __init__ method:
- Generates a unique bcrypt salt via
bcrypt.gensalt() - Hashes the password with
bcrypt.hashpw(password.encode(), salt) - Stores both the hash and salt as separate
LargeBinarycolumns
Important: SQLAlchemy bypasses __init__ when loading from the database, so the password hashing only occurs during initial creation.
ExternalUser
Represents users authenticating through external identity providers (OAuth, SSO). No password is required or stored; the password_hash and password_salt columns are explicitly NULL.
Security Considerations
- Passwords are hashed using bcrypt with a unique salt per user
- A module-level
SECRET_KEYis generated viasecrets.token_urlsafe(32)for potential JWT/session use - Password fields are constrained at the database level to prevent inconsistent states
Usage Example
The main() function demonstrates:
- Creating a
LocalUserwith hashed password - Creating an
ExternalUserwithout password - Attempting to create a base
User(raisesTypeError) - Querying all users polymorphically (returns mixed
LocalUser/ExternalUserinstances) - Querying specific subclasses
uv run python internal_docs/vignettes/sqlalchemy/polymorphic_user/users.py
Related Pages
- Implementation:Arize_ai_Phoenix_Helm_Values - Helm chart with authentication configuration (basic auth, LDAP, OAuth2) that reflects this pattern
- Implementation:Arize_ai_Phoenix_Contextvars_Async_Demo - Another internal vignette (async context management)
- Implementation:Arize_ai_Phoenix_Pyproject_Config - Main package with SQLAlchemy and authlib dependencies