Loading profile...
;
+ if (error) return
+
My Profile
+
Email: {user?.email}
+
Name: {user?.first_name} {user?.last_name}
+
Keycloak ID: {user?.keycloak_id}
+
+ );
+}
+```
+
+### Backend: User Profile Endpoint
+
+Already shown above in the "Protecting API Endpoints" section.
+
+---
+
+## User ID Mapping (CRITICAL!)
+
+### The Problem
+
+When integrating Keycloak with an existing application that has its own users table, you face a **user ID mismatch** problem:
+
+- **Keycloak** assigns its own user IDs (UUID format, e.g., `a1b2c3d4-...`)
+- **Your app** may have existing user IDs (integer, UUID, or other format)
+- **Foreign keys** in your database reference your app's user IDs, not Keycloak's
+
+### The Solution: keycloak_id Column
+
+Add a `keycloak_id` column to your users table to map between the two ID systems.
+
+#### Database Migration
+
+```python
+# alembic/versions/xxx_add_keycloak_id.py
+def upgrade():
+ op.add_column('users', sa.Column('keycloak_id', sa.String(), nullable=True))
+ op.create_unique_constraint('uq_users_keycloak_id', 'users', ['keycloak_id'])
+ op.create_index('ix_users_keycloak_id', 'users', ['keycloak_id'])
+
+def downgrade():
+ op.drop_index('ix_users_keycloak_id')
+ op.drop_constraint('uq_users_keycloak_id', 'users')
+ op.drop_column('users', 'keycloak_id')
+```
+
+#### Database Model
+
+```python
+# db/models.py
+from sqlalchemy.orm import Mapped, mapped_column
+from sqlalchemy import String
+
+class User(Base):
+ __tablename__ = 'users'
+
+ id: Mapped[int] = mapped_column(primary_key=True) # App's internal ID
+ email: Mapped[str] = mapped_column(String, unique=True, nullable=False)
+ keycloak_id: Mapped[str | None] = mapped_column(
+ String,
+ unique=True,
+ nullable=True,
+ index=True, # Index for fast lookups
+ )
+ first_name: Mapped[str]
+ last_name: Mapped[str]
+ # ... other fields
+```
+
+#### User Lookup Strategy
+
+The middleware (shown earlier) implements a **three-tier lookup strategy**:
+
+1. **Primary**: Lookup by `keycloak_id` (fast, indexed)
+2. **Fallback**: If not found, lookup by email
+3. **Auto-link**: If found by email, update `keycloak_id` for future fast lookups
+4. **Default**: If neither found, use Keycloak ID as user_id
+
+This strategy ensures:
+- ✅ Existing users are automatically linked to Keycloak
+- ✅ New users work immediately
+- ✅ Performance improves over time as mappings are created
+- ✅ No manual user migration required
+
+### Migration Workflow
+
+```mermaid
+graph TD
+ A[User logs in with Keycloak] --> B{keycloak_id exists?}
+ B -->|Yes| C[Fast lookup by keycloak_id]
+ C --> D[Use app user ID]
+ B -->|No| E[Lookup by email]
+ E --> F{User found?}
+ F -->|Yes| G[Update keycloak_id]
+ G --> H[Link accounts]
+ H --> D
+ F -->|No| I[Create new userAccess denied. Admin privileges required.
;
+ }
+
+ return