auth-kit-fastapi 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. auth_kit_fastapi-0.1.0/PKG-INFO +226 -0
  2. auth_kit_fastapi-0.1.0/README.md +171 -0
  3. auth_kit_fastapi-0.1.0/auth_kit_fastapi/__init__.py +27 -0
  4. auth_kit_fastapi-0.1.0/auth_kit_fastapi/schemas/__init__.py +84 -0
  5. auth_kit_fastapi-0.1.0/auth_kit_fastapi/schemas/auth.py +145 -0
  6. auth_kit_fastapi-0.1.0/auth_kit_fastapi/schemas/passkey.py +129 -0
  7. auth_kit_fastapi-0.1.0/auth_kit_fastapi/schemas/two_factor.py +73 -0
  8. auth_kit_fastapi-0.1.0/auth_kit_fastapi/services/__init__.py +17 -0
  9. auth_kit_fastapi-0.1.0/auth_kit_fastapi/services/email_service.py +468 -0
  10. auth_kit_fastapi-0.1.0/auth_kit_fastapi/services/passkey_service.py +461 -0
  11. auth_kit_fastapi-0.1.0/auth_kit_fastapi/services/two_factor_service.py +422 -0
  12. auth_kit_fastapi-0.1.0/auth_kit_fastapi/services/user_service.py +407 -0
  13. auth_kit_fastapi-0.1.0/auth_kit_fastapi.egg-info/PKG-INFO +226 -0
  14. auth_kit_fastapi-0.1.0/auth_kit_fastapi.egg-info/SOURCES.txt +23 -0
  15. auth_kit_fastapi-0.1.0/auth_kit_fastapi.egg-info/dependency_links.txt +1 -0
  16. auth_kit_fastapi-0.1.0/auth_kit_fastapi.egg-info/requires.txt +29 -0
  17. auth_kit_fastapi-0.1.0/auth_kit_fastapi.egg-info/top_level.txt +2 -0
  18. auth_kit_fastapi-0.1.0/setup.cfg +4 -0
  19. auth_kit_fastapi-0.1.0/setup.py +56 -0
  20. auth_kit_fastapi-0.1.0/tests/__init__.py +1 -0
  21. auth_kit_fastapi-0.1.0/tests/conftest.py +198 -0
  22. auth_kit_fastapi-0.1.0/tests/test_auth_endpoints.py +350 -0
  23. auth_kit_fastapi-0.1.0/tests/test_passkey_endpoints.py +282 -0
  24. auth_kit_fastapi-0.1.0/tests/test_services.py +449 -0
  25. auth_kit_fastapi-0.1.0/tests/test_two_factor_endpoints.py +377 -0
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: auth-kit-fastapi
3
+ Version: 0.1.0
4
+ Summary: FastAPI authentication backend for Auth Kit
5
+ Home-page: https://github.com/erickva/auth-kit
6
+ Author: Erick Ama
7
+ Author-email: me@erick.no
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Framework :: FastAPI
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: fastapi>=0.100.0
21
+ Requires-Dist: sqlalchemy>=2.0.0
22
+ Requires-Dist: pydantic>=2.0.0
23
+ Requires-Dist: pydantic-settings>=2.0.0
24
+ Requires-Dist: python-jose[cryptography]>=3.3.0
25
+ Requires-Dist: passlib[bcrypt]>=1.7.4
26
+ Requires-Dist: python-multipart>=0.0.6
27
+ Requires-Dist: email-validator>=2.0.0
28
+ Requires-Dist: pyotp>=2.9.0
29
+ Requires-Dist: qrcode>=7.4.0
30
+ Requires-Dist: webauthn>=1.9.0
31
+ Requires-Dist: alembic>=1.12.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
35
+ Requires-Dist: httpx>=0.24.0; extra == "dev"
36
+ Requires-Dist: black>=23.0.0; extra == "dev"
37
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
38
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
39
+ Provides-Extra: redis
40
+ Requires-Dist: redis>=4.5.0; extra == "redis"
41
+ Provides-Extra: postgres
42
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
43
+ Provides-Extra: mysql
44
+ Requires-Dist: pymysql>=1.1.0; extra == "mysql"
45
+ Dynamic: author
46
+ Dynamic: author-email
47
+ Dynamic: classifier
48
+ Dynamic: description
49
+ Dynamic: description-content-type
50
+ Dynamic: home-page
51
+ Dynamic: provides-extra
52
+ Dynamic: requires-dist
53
+ Dynamic: requires-python
54
+ Dynamic: summary
55
+
56
+ # Auth Kit FastAPI
57
+
58
+ FastAPI authentication backend for Auth Kit. Provides a complete authentication solution with JWT tokens, passkeys, 2FA, and more.
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install auth-kit-fastapi
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ ```python
69
+ from fastapi import FastAPI
70
+ from auth_kit_fastapi import create_auth_app, AuthConfig
71
+
72
+ app = FastAPI()
73
+
74
+ # Configure authentication
75
+ auth_config = AuthConfig(
76
+ database_url="postgresql://localhost/myapp",
77
+ jwt_secret="your-secret-key",
78
+ features={
79
+ "passkeys": True,
80
+ "two_factor": True,
81
+ "email_verification": True
82
+ }
83
+ )
84
+
85
+ # Create auth app
86
+ auth_app = create_auth_app(auth_config)
87
+
88
+ # Mount auth routes
89
+ app.mount("/api/auth", auth_app)
90
+ ```
91
+
92
+ ## Features
93
+
94
+ - 🔐 JWT-based authentication with refresh tokens
95
+ - 🔑 WebAuthn/Passkey support
96
+ - 🔒 Two-factor authentication (TOTP)
97
+ - 📧 Email verification
98
+ - 🔄 Password reset flow
99
+ - 👤 User management
100
+ - 🗄️ SQLAlchemy ORM support
101
+ - 🔍 Extensible user model
102
+ - 🛡️ Security best practices
103
+
104
+ ## Configuration
105
+
106
+ ```python
107
+ from auth_kit_fastapi import AuthConfig
108
+
109
+ config = AuthConfig(
110
+ # Database
111
+ database_url="postgresql://user:pass@localhost/db",
112
+
113
+ # JWT Settings
114
+ jwt_secret="your-secret-key",
115
+ jwt_algorithm="HS256",
116
+ access_token_expire_minutes=30,
117
+ refresh_token_expire_days=7,
118
+
119
+ # Passkey Settings
120
+ passkey_rp_id="localhost",
121
+ passkey_rp_name="My App",
122
+ passkey_origin="http://localhost:3000",
123
+
124
+ # Email Settings
125
+ email_from="noreply@example.com",
126
+ email_from_name="My App",
127
+
128
+ # Features
129
+ features={
130
+ "passkeys": True,
131
+ "two_factor": True,
132
+ "email_verification": True,
133
+ "social_login": ["google", "github"]
134
+ }
135
+ )
136
+ ```
137
+
138
+ ## Custom User Model
139
+
140
+ Extend the base User model with your own fields:
141
+
142
+ ```python
143
+ from auth_kit_fastapi import BaseUser
144
+ from sqlalchemy import Column, String
145
+
146
+ class User(BaseUser):
147
+ __tablename__ = "users"
148
+
149
+ # Add custom fields
150
+ company_name = Column(String, nullable=True)
151
+ department = Column(String, nullable=True)
152
+ ```
153
+
154
+ ## API Endpoints
155
+
156
+ All endpoints are mounted under your chosen prefix (e.g., `/api/auth`):
157
+
158
+ ### Authentication
159
+ - `POST /register` - Register new user
160
+ - `POST /login` - Login with email/password
161
+ - `POST /logout` - Logout user
162
+ - `POST /refresh` - Refresh access token
163
+ - `GET /me` - Get current user
164
+
165
+ ### Password Management
166
+ - `POST /password/change` - Change password
167
+ - `POST /password/reset` - Request password reset
168
+ - `POST /password/reset/confirm` - Confirm password reset
169
+
170
+ ### Email Verification
171
+ - `GET /verify-email/{token}` - Verify email
172
+ - `POST /resend-verification` - Resend verification email
173
+
174
+ ### Passkeys
175
+ - `GET /passkeys` - List user's passkeys
176
+ - `POST /passkeys/register/begin` - Begin passkey registration
177
+ - `POST /passkeys/register/complete` - Complete passkey registration
178
+ - `POST /passkeys/authenticate/begin` - Begin passkey authentication
179
+ - `POST /passkeys/authenticate/complete` - Complete passkey authentication
180
+ - `DELETE /passkeys/{id}` - Delete passkey
181
+
182
+ ### Two-Factor Authentication
183
+ - `POST /2fa/setup/begin` - Begin 2FA setup
184
+ - `POST /2fa/setup/verify` - Verify and enable 2FA
185
+ - `POST /2fa/disable` - Disable 2FA
186
+ - `POST /2fa/verify/login` - Verify 2FA during login
187
+ - `POST /2fa/recovery-codes` - Regenerate recovery codes
188
+
189
+ ## Middleware & Dependencies
190
+
191
+ Use the provided dependencies to protect your routes:
192
+
193
+ ```python
194
+ from fastapi import Depends
195
+ from auth_kit_fastapi import get_current_user, require_verified_user
196
+
197
+ @app.get("/protected")
198
+ async def protected_route(user = Depends(get_current_user)):
199
+ return {"message": f"Hello {user.email}"}
200
+
201
+ @app.get("/verified-only")
202
+ async def verified_only(user = Depends(require_verified_user)):
203
+ return {"message": "Only verified users can see this"}
204
+ ```
205
+
206
+ ## Events & Hooks
207
+
208
+ Subscribe to authentication events:
209
+
210
+ ```python
211
+ from auth_kit_fastapi import auth_events
212
+
213
+ @auth_events.on("user_registered")
214
+ async def on_user_registered(user):
215
+ # Send welcome email
216
+ print(f"New user registered: {user.email}")
217
+
218
+ @auth_events.on("user_logged_in")
219
+ async def on_user_logged_in(user):
220
+ # Log login event
221
+ print(f"User logged in: {user.email}")
222
+ ```
223
+
224
+ ## License
225
+
226
+ MIT License
@@ -0,0 +1,171 @@
1
+ # Auth Kit FastAPI
2
+
3
+ FastAPI authentication backend for Auth Kit. Provides a complete authentication solution with JWT tokens, passkeys, 2FA, and more.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install auth-kit-fastapi
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from fastapi import FastAPI
15
+ from auth_kit_fastapi import create_auth_app, AuthConfig
16
+
17
+ app = FastAPI()
18
+
19
+ # Configure authentication
20
+ auth_config = AuthConfig(
21
+ database_url="postgresql://localhost/myapp",
22
+ jwt_secret="your-secret-key",
23
+ features={
24
+ "passkeys": True,
25
+ "two_factor": True,
26
+ "email_verification": True
27
+ }
28
+ )
29
+
30
+ # Create auth app
31
+ auth_app = create_auth_app(auth_config)
32
+
33
+ # Mount auth routes
34
+ app.mount("/api/auth", auth_app)
35
+ ```
36
+
37
+ ## Features
38
+
39
+ - 🔐 JWT-based authentication with refresh tokens
40
+ - 🔑 WebAuthn/Passkey support
41
+ - 🔒 Two-factor authentication (TOTP)
42
+ - 📧 Email verification
43
+ - 🔄 Password reset flow
44
+ - 👤 User management
45
+ - 🗄️ SQLAlchemy ORM support
46
+ - 🔍 Extensible user model
47
+ - 🛡️ Security best practices
48
+
49
+ ## Configuration
50
+
51
+ ```python
52
+ from auth_kit_fastapi import AuthConfig
53
+
54
+ config = AuthConfig(
55
+ # Database
56
+ database_url="postgresql://user:pass@localhost/db",
57
+
58
+ # JWT Settings
59
+ jwt_secret="your-secret-key",
60
+ jwt_algorithm="HS256",
61
+ access_token_expire_minutes=30,
62
+ refresh_token_expire_days=7,
63
+
64
+ # Passkey Settings
65
+ passkey_rp_id="localhost",
66
+ passkey_rp_name="My App",
67
+ passkey_origin="http://localhost:3000",
68
+
69
+ # Email Settings
70
+ email_from="noreply@example.com",
71
+ email_from_name="My App",
72
+
73
+ # Features
74
+ features={
75
+ "passkeys": True,
76
+ "two_factor": True,
77
+ "email_verification": True,
78
+ "social_login": ["google", "github"]
79
+ }
80
+ )
81
+ ```
82
+
83
+ ## Custom User Model
84
+
85
+ Extend the base User model with your own fields:
86
+
87
+ ```python
88
+ from auth_kit_fastapi import BaseUser
89
+ from sqlalchemy import Column, String
90
+
91
+ class User(BaseUser):
92
+ __tablename__ = "users"
93
+
94
+ # Add custom fields
95
+ company_name = Column(String, nullable=True)
96
+ department = Column(String, nullable=True)
97
+ ```
98
+
99
+ ## API Endpoints
100
+
101
+ All endpoints are mounted under your chosen prefix (e.g., `/api/auth`):
102
+
103
+ ### Authentication
104
+ - `POST /register` - Register new user
105
+ - `POST /login` - Login with email/password
106
+ - `POST /logout` - Logout user
107
+ - `POST /refresh` - Refresh access token
108
+ - `GET /me` - Get current user
109
+
110
+ ### Password Management
111
+ - `POST /password/change` - Change password
112
+ - `POST /password/reset` - Request password reset
113
+ - `POST /password/reset/confirm` - Confirm password reset
114
+
115
+ ### Email Verification
116
+ - `GET /verify-email/{token}` - Verify email
117
+ - `POST /resend-verification` - Resend verification email
118
+
119
+ ### Passkeys
120
+ - `GET /passkeys` - List user's passkeys
121
+ - `POST /passkeys/register/begin` - Begin passkey registration
122
+ - `POST /passkeys/register/complete` - Complete passkey registration
123
+ - `POST /passkeys/authenticate/begin` - Begin passkey authentication
124
+ - `POST /passkeys/authenticate/complete` - Complete passkey authentication
125
+ - `DELETE /passkeys/{id}` - Delete passkey
126
+
127
+ ### Two-Factor Authentication
128
+ - `POST /2fa/setup/begin` - Begin 2FA setup
129
+ - `POST /2fa/setup/verify` - Verify and enable 2FA
130
+ - `POST /2fa/disable` - Disable 2FA
131
+ - `POST /2fa/verify/login` - Verify 2FA during login
132
+ - `POST /2fa/recovery-codes` - Regenerate recovery codes
133
+
134
+ ## Middleware & Dependencies
135
+
136
+ Use the provided dependencies to protect your routes:
137
+
138
+ ```python
139
+ from fastapi import Depends
140
+ from auth_kit_fastapi import get_current_user, require_verified_user
141
+
142
+ @app.get("/protected")
143
+ async def protected_route(user = Depends(get_current_user)):
144
+ return {"message": f"Hello {user.email}"}
145
+
146
+ @app.get("/verified-only")
147
+ async def verified_only(user = Depends(require_verified_user)):
148
+ return {"message": "Only verified users can see this"}
149
+ ```
150
+
151
+ ## Events & Hooks
152
+
153
+ Subscribe to authentication events:
154
+
155
+ ```python
156
+ from auth_kit_fastapi import auth_events
157
+
158
+ @auth_events.on("user_registered")
159
+ async def on_user_registered(user):
160
+ # Send welcome email
161
+ print(f"New user registered: {user.email}")
162
+
163
+ @auth_events.on("user_logged_in")
164
+ async def on_user_logged_in(user):
165
+ # Log login event
166
+ print(f"User logged in: {user.email}")
167
+ ```
168
+
169
+ ## License
170
+
171
+ MIT License
@@ -0,0 +1,27 @@
1
+ """
2
+ Auth Kit FastAPI - Complete authentication solution for FastAPI applications
3
+ """
4
+
5
+ from .core.config import AuthConfig
6
+ from .core.app import create_auth_app
7
+ from .core.dependencies import (
8
+ get_current_user,
9
+ get_current_active_user,
10
+ require_verified_user,
11
+ require_superuser
12
+ )
13
+ from .models.user import BaseUser
14
+ from .core.events import auth_events
15
+
16
+ __version__ = "1.0.0"
17
+
18
+ __all__ = [
19
+ "AuthConfig",
20
+ "create_auth_app",
21
+ "get_current_user",
22
+ "get_current_active_user",
23
+ "require_verified_user",
24
+ "require_superuser",
25
+ "BaseUser",
26
+ "auth_events"
27
+ ]
@@ -0,0 +1,84 @@
1
+ """
2
+ Export all schemas
3
+ """
4
+
5
+ from .auth import (
6
+ UserBase,
7
+ UserCreate,
8
+ UserUpdate,
9
+ UserResponse,
10
+ LoginRequest,
11
+ TokenResponse,
12
+ LoginResponse,
13
+ RefreshTokenRequest,
14
+ LogoutRequest,
15
+ PasswordChangeRequest,
16
+ PasswordResetRequest,
17
+ PasswordResetConfirmRequest,
18
+ EmailVerificationRequest,
19
+ MessageResponse,
20
+ ErrorResponse
21
+ )
22
+
23
+ from .passkey import (
24
+ PasskeyBase,
25
+ PasskeyCreate,
26
+ PasskeyResponse,
27
+ PasskeyListResponse,
28
+ RegistrationOptionsResponse,
29
+ RegistrationCompleteRequest,
30
+ AuthenticationOptionsResponse,
31
+ AuthenticationCompleteRequest,
32
+ AuthenticationBeginRequest
33
+ )
34
+
35
+ from .two_factor import (
36
+ TwoFactorSetupResponse,
37
+ TwoFactorVerifyRequest,
38
+ TwoFactorEnableResponse,
39
+ TwoFactorDisableRequest,
40
+ RecoveryCodesRegenerateRequest,
41
+ RecoveryCodesResponse,
42
+ TwoFactorLoginVerifyRequest,
43
+ TwoFactorStatusResponse
44
+ )
45
+
46
+ __all__ = [
47
+ # Auth schemas
48
+ "UserBase",
49
+ "UserCreate",
50
+ "UserUpdate",
51
+ "UserResponse",
52
+ "LoginRequest",
53
+ "TokenResponse",
54
+ "LoginResponse",
55
+ "RefreshTokenRequest",
56
+ "LogoutRequest",
57
+ "PasswordChangeRequest",
58
+ "PasswordResetRequest",
59
+ "PasswordResetConfirmRequest",
60
+ "EmailVerificationRequest",
61
+ "MessageResponse",
62
+ "ErrorResponse",
63
+
64
+ # Passkey schemas
65
+ "PasskeyBase",
66
+ "PasskeyCreate",
67
+ "PasskeyResponse",
68
+ "PasskeyListResponse",
69
+ "RegistrationOptionsResponse",
70
+ "RegistrationCompleteRequest",
71
+ "AuthenticationOptionsResponse",
72
+ "AuthenticationCompleteRequest",
73
+ "AuthenticationBeginRequest",
74
+
75
+ # 2FA schemas
76
+ "TwoFactorSetupResponse",
77
+ "TwoFactorVerifyRequest",
78
+ "TwoFactorEnableResponse",
79
+ "TwoFactorDisableRequest",
80
+ "RecoveryCodesRegenerateRequest",
81
+ "RecoveryCodesResponse",
82
+ "TwoFactorLoginVerifyRequest",
83
+ "TwoFactorStatusResponse"
84
+ ]
@@ -0,0 +1,145 @@
1
+ """
2
+ Authentication schemas for request/response validation
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import Optional, Dict, Any
7
+ from uuid import UUID
8
+ from pydantic import BaseModel, EmailStr, Field, validator
9
+
10
+
11
+ # Base schemas
12
+ class UserBase(BaseModel):
13
+ """Base user schema"""
14
+ email: EmailStr
15
+ first_name: Optional[str] = None
16
+ last_name: Optional[str] = None
17
+ phone_number: Optional[str] = None
18
+
19
+
20
+ class UserCreate(UserBase):
21
+ """User registration schema"""
22
+ password: str = Field(..., min_length=8, max_length=128)
23
+ confirm_password: Optional[str] = None
24
+ accept_terms: Optional[bool] = False
25
+
26
+ @validator('confirm_password')
27
+ def passwords_match(cls, v, values):
28
+ if 'password' in values and v != values['password']:
29
+ raise ValueError('Passwords do not match')
30
+ return v
31
+
32
+
33
+ class UserUpdate(BaseModel):
34
+ """User profile update schema"""
35
+ first_name: Optional[str] = None
36
+ last_name: Optional[str] = None
37
+ phone_number: Optional[str] = None
38
+ avatar_url: Optional[str] = None
39
+
40
+
41
+ class UserResponse(UserBase):
42
+ """User response schema"""
43
+ id: UUID
44
+ is_active: bool
45
+ is_verified: bool
46
+ is_superuser: bool
47
+ two_factor_enabled: bool
48
+ created_at: datetime
49
+ updated_at: datetime
50
+ last_login_at: Optional[datetime] = None
51
+
52
+ class Config:
53
+ from_attributes = True
54
+
55
+
56
+ # Authentication schemas
57
+ class LoginRequest(BaseModel):
58
+ """Login request schema (OAuth2 compatible)"""
59
+ username: EmailStr # OAuth2 spec requires 'username'
60
+ password: str
61
+ grant_type: Optional[str] = "password"
62
+ scope: Optional[str] = ""
63
+ client_id: Optional[str] = None
64
+ client_secret: Optional[str] = None
65
+
66
+
67
+ class TokenResponse(BaseModel):
68
+ """Token response schema (OAuth2 compatible)"""
69
+ access_token: str
70
+ refresh_token: Optional[str] = None
71
+ token_type: str = "bearer"
72
+ expires_in: Optional[int] = None
73
+ scope: Optional[str] = ""
74
+
75
+
76
+ class LoginResponse(BaseModel):
77
+ """Enhanced login response with user data"""
78
+ user: UserResponse
79
+ tokens: TokenResponse
80
+ requires_2fa: bool = False
81
+ message: Optional[str] = None
82
+
83
+
84
+ class RefreshTokenRequest(BaseModel):
85
+ """Refresh token request"""
86
+ refresh_token: str
87
+
88
+
89
+ class LogoutRequest(BaseModel):
90
+ """Logout request"""
91
+ refresh_token: str
92
+ everywhere: bool = False # Logout from all devices
93
+
94
+
95
+ # Password management schemas
96
+ class PasswordChangeRequest(BaseModel):
97
+ """Password change request"""
98
+ current_password: str
99
+ new_password: str = Field(..., min_length=8, max_length=128)
100
+ confirm_password: Optional[str] = None
101
+
102
+ @validator('confirm_password')
103
+ def passwords_match(cls, v, values):
104
+ if 'new_password' in values and v != values['new_password']:
105
+ raise ValueError('Passwords do not match')
106
+ return v
107
+
108
+
109
+ class PasswordResetRequest(BaseModel):
110
+ """Password reset request"""
111
+ email: EmailStr
112
+
113
+
114
+ class PasswordResetConfirmRequest(BaseModel):
115
+ """Password reset confirmation"""
116
+ token: str
117
+ new_password: str = Field(..., min_length=8, max_length=128)
118
+ confirm_password: Optional[str] = None
119
+
120
+ @validator('confirm_password')
121
+ def passwords_match(cls, v, values):
122
+ if 'new_password' in values and v != values['new_password']:
123
+ raise ValueError('Passwords do not match')
124
+ return v
125
+
126
+
127
+ # Email verification schemas
128
+ class EmailVerificationRequest(BaseModel):
129
+ """Request email verification resend"""
130
+ email: Optional[EmailStr] = None # Use current user's email if not provided
131
+
132
+
133
+ # Response messages
134
+ class MessageResponse(BaseModel):
135
+ """Generic message response"""
136
+ message: str
137
+ success: bool = True
138
+ data: Optional[Dict[str, Any]] = None
139
+
140
+
141
+ class ErrorResponse(BaseModel):
142
+ """Error response"""
143
+ detail: str
144
+ code: Optional[str] = None
145
+ field: Optional[str] = None