| 93 | |
| 94 | |
| 95 | class UserIdentity(Base): |
| 96 | __tablename__: str = "user_identity" |
| 97 | |
| 98 | id: Mapped[int] = mapped_column(primary_key=True) |
| 99 | user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), index=True) |
| 100 | provider: Mapped[str] = mapped_column( |
| 101 | SQLAEnum("github", "google", name="identity_provider"), |
| 102 | nullable=False, |
| 103 | index=True, |
| 104 | ) |
| 105 | provider_user_id: Mapped[str | None] = mapped_column( |
| 106 | String(100), nullable=True, index=True |
| 107 | ) |
| 108 | _access_token: Mapped[str | None] = mapped_column( |
| 109 | "access_token", String(2048), nullable=True |
| 110 | ) |
| 111 | _refresh_token: Mapped[str | None] = mapped_column( |
| 112 | "refresh_token", String(2048), nullable=True |
| 113 | ) |
| 114 | token_expires_at: Mapped[datetime | None] = mapped_column(nullable=True) |
| 115 | password_hash: Mapped[str | None] = mapped_column( |
| 116 | String(255), nullable=True |
| 117 | ) # Only for password provider |
| 118 | provider_metadata: Mapped[dict | None] = mapped_column( |
| 119 | JSON, nullable=True |
| 120 | ) # Store provider-specific data |
| 121 | created_at: Mapped[datetime] = mapped_column(default=utc_now) |
| 122 | updated_at: Mapped[datetime] = mapped_column(default=utc_now, onupdate=utc_now) |
| 123 | |
| 124 | # Relationships |
| 125 | user: Mapped[User] = relationship(back_populates="identities") |
| 126 | |
| 127 | __table_args__ = ( |
| 128 | UniqueConstraint( |
| 129 | "provider", "provider_user_id", name="uq_identity_provider_user" |
| 130 | ), |
| 131 | ) |
| 132 | |
| 133 | @property |
| 134 | def access_token(self) -> str | None: |
| 135 | if self._access_token: |
| 136 | fernet = get_fernet() |
| 137 | return fernet.decrypt(self._access_token.encode()).decode() |
| 138 | return None |
| 139 | |
| 140 | @access_token.setter |
| 141 | def access_token(self, value: str | None): |
| 142 | if value: |
| 143 | fernet = get_fernet() |
| 144 | self._access_token = fernet.encrypt(value.encode()).decode() |
| 145 | else: |
| 146 | self._access_token = None |
| 147 | |
| 148 | @property |
| 149 | def refresh_token(self) -> str | None: |
| 150 | if self._refresh_token: |
| 151 | fernet = get_fernet() |
| 152 | return fernet.decrypt(self._refresh_token.encode()).decode() |
no outgoing calls
no test coverage detected