Source code for regstack.models.oauth_identity
"""External OAuth identity linked to a regstack user.
One row per ``(provider, subject_id)``. Both
``(provider, subject_id)`` and ``(user_id, provider)`` are unique:
- The first stops two regstack users from sharing one external
identity (otherwise a single Google account could log in as
whichever regstack user the lookup happened to find first).
- The second stops one regstack user from linking two identities of
the same provider (no use case in the UI; would only confuse the
/account/me listing).
"""
from __future__ import annotations
from datetime import UTC, datetime
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
from regstack.models._objectid import IdStr
def _utcnow() -> datetime:
return datetime.now(UTC)
[docs]
class OAuthIdentity(BaseModel):
"""A user's link to one external OAuth provider."""
model_config = ConfigDict(populate_by_name=True, extra="allow")
id: IdStr | None = Field(default=None, alias="_id")
user_id: IdStr
"""The regstack user this identity belongs to."""
provider: str
"""Provider name — matches ``OAuthProvider.name`` (e.g. ``"google"``)."""
subject_id: str
"""The provider's stable, opaque user identifier. Never an email."""
email: str | None = None
"""Snapshot of the provider's email at link time. Non-authoritative —
the provider may change it. We never key on this field."""
linked_at: datetime = Field(default_factory=_utcnow)
last_used_at: datetime | None = None
[docs]
def to_mongo(self) -> dict[str, Any]:
data = self.model_dump(by_alias=True, exclude_none=True)
if data.get("_id") is None:
data.pop("_id", None)
return data